diff --git a/.gitignore b/.gitignore index e9af7c68..d710987c 100644 --- a/.gitignore +++ b/.gitignore @@ -31,16 +31,19 @@ Makefile.in configure aclocal.m4 -DSLogic-gui/ui_*.h -DSLogic-gui/DSLogic -DSLogic-gui/install_manifest.txt +DSView/ui_*.h +DSView/DSView +DSView/install_manifest.txt moc_*.cxx moc_*.cxx_parameters +qrc_*.cxx -libsigrok4DSLogic/version.h +libsigrok4DSL/version.h libusbx-1.0.18/doc/doxygen.cfg libusbx-1.0.18/m4/ - +DSView-prj +libsigrokdecode +build* diff --git a/DSView/CMakeLists.txt b/DSView/CMakeLists.txt index 2b48a7b6..2f5eb787 100644 --- a/DSView/CMakeLists.txt +++ b/DSView/CMakeLists.txt @@ -103,7 +103,7 @@ set(DS_DESCRIPTION "A GUI for instruments of DreamSourceLab") set(DS_VERSION_MAJOR 0) set(DS_VERSION_MINOR 9) -set(DS_VERSION_MICRO 2) +set(DS_VERSION_MICRO 3) set(DS_VERSION_STRING ${DS_VERSION_MAJOR}.${DS_VERSION_MINOR}.${DS_VERSION_MICRO} ) @@ -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/icons/wait.gif b/DSView/icons/wait.gif index 4d615018..58279b9f 100644 Binary files a/DSView/icons/wait.gif and b/DSView/icons/wait.gif differ diff --git a/DSView/main.cpp b/DSView/main.cpp index 3964880a..0d3a763f 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -30,7 +30,7 @@ #include -#include +#include #include #include #include @@ -40,8 +40,6 @@ #include "config.h" -char decoders_path[256]; - void usage() { fprintf(stdout, @@ -122,11 +120,6 @@ int main(int argc, char *argv[]) do { #ifdef ENABLE_DECODE - QDir dir(QCoreApplication::applicationDirPath()); - assert(dir.cd("decoders")); - std::string str = dir.absolutePath().toStdString() + "/"; - strcpy(decoders_path, str.c_str()); - // Initialise libsigrokdecode if (srd_init(NULL) != SRD_OK) { qDebug() << "ERROR: libsigrokdecode init failed."; diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 5e5ca875..b78b9996 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -433,6 +433,11 @@ void DecoderStack::decode_proc() _decode_state = Stopped; } +uint64_t DecoderStack::sample_count() const +{ + return _sample_count; +} + void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) { assert(pdata); diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 955230a8..19c518a4 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -120,6 +120,8 @@ public: void options_changed(bool changed); + uint64_t sample_count() const; + private: boost::optional wait_for_data() const; diff --git a/DSView/pv/data/logicsnapshot.cpp b/DSView/pv/data/logicsnapshot.cpp index 4b18551e..31614471 100644 --- a/DSView/pv/data/logicsnapshot.cpp +++ b/DSView/pv/data/logicsnapshot.cpp @@ -208,8 +208,6 @@ void LogicSnapshot::get_subsampled_edges( boost::lock_guard lock(_mutex); const uint64_t block_length = (uint64_t)max(min_length, 1.0f); - const unsigned int min_level = max((int)floorf(logf(min_length) / - LogMipMapScaleFactor) - 1, 0); const uint64_t sig_mask = 1ULL << sig_index; if (!edges.empty()) @@ -220,132 +218,8 @@ void LogicSnapshot::get_subsampled_edges( while (index + block_length <= end) { - //----- Continue to search -----// - level = min_level; - - // We cannot fast-forward if there is no mip-map data at - // at the minimum level. - fast_forward = (_mip_map[level].data != NULL); - - if (min_length < MipMapScaleFactor) - { - // Search individual samples up to the beginning of - // the next first level mip map block - const uint64_t final_index = min(end, - pow2_ceil(index, MipMapScalePower)); - - for (; index < final_index && - (index & ~(~0 << MipMapScalePower)) != 0; - index++) - { - const bool sample = - (get_sample(index) & sig_mask) != 0; - - // If there was a change we cannot fast forward - if (sample != last_sample) { - fast_forward = false; - break; - } - } - } - else - { - // If resolution is less than a mip map block, - // round up to the beginning of the mip-map block - // for this level of detail - const int min_level_scale_power = - (level + 1) * MipMapScalePower; - index = pow2_ceil(index, min_level_scale_power); - if (index >= end) - break; - - // We can fast forward only if there was no change - const bool sample = - (get_sample(index) & sig_mask) != 0; - if (last_sample != sample) - fast_forward = false; - } - - if (fast_forward) { - - // Fast forward: This involves zooming out to higher - // levels of the mip map searching for changes, then - // zooming in on them to find the point where the edge - // begins. - - // Slide right and zoom out at the beginnings of mip-map - // blocks until we encounter a change - while (1) { - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; - - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) - break; - - if ((offset & ~(~0 << MipMapScalePower)) == 0) { - // If we are now at the beginning of a - // higher level mip-map block ascend one - // level - if (level + 1 >= ScaleStepCount || - !_mip_map[level + 1].data) - break; - - level++; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } - } - - // Zoom in, and slide right until we encounter a change, - // and repeat until we reach min_level - while (1) { - assert(_mip_map[level].data); - - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; - - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) { - // Zoom in unless we reached the minimum - // zoom - if (level == min_level) - break; - - level--; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } - } - - // If individual samples within the limit of resolution, - // do a linear search for the next transition within the - // block - if (min_length < MipMapScaleFactor) { - for (; index < end; index++) { - const bool sample = (get_sample(index) & - sig_mask) != 0; - if (sample != last_sample) - break; - } - } - } + // search next edge + get_nxt_edge(index, last_sample, end, min_length, sig_index); //----- Store the edge -----// @@ -373,44 +247,34 @@ void LogicSnapshot::get_subsampled_edges( edges.push_back(pair(end + 1, ~last_sample)); } -int LogicSnapshot::get_first_edge( - uint64_t &edge_index, bool &edge, - uint64_t start, uint64_t end, - int sig_index, int edge_type, - int flag_index, int flag) +bool LogicSnapshot::get_nxt_edge( + uint64_t &index, bool last_sample, uint64_t end, + float min_length, int sig_index) { - uint64_t index = start; unsigned int level; - bool last_sample; bool fast_forward; - assert(end <= get_sample_count()); - assert(start <= end); - assert(sig_index >= 0); - assert(sig_index < 64); + assert(index > 0); - boost::lock_guard lock(_mutex); - - const uint64_t block_length = 1; - const unsigned int min_level = 0; + const unsigned int min_level = max((int)floorf(logf(min_length) / + LogMipMapScaleFactor) - 1, 0); const uint64_t sig_mask = 1ULL << sig_index; - const uint64_t flag_mask = 1ULL << flag_index; - // Store the initial state - last_sample = (get_sample(start) & sig_mask) != 0; + if (index >= end) + return false; - while (index + block_length <= end) + //----- Continue to search -----// + level = min_level; + + // We cannot fast-forward if there is no mip-map data at + // at the minimum level. + fast_forward = (_mip_map[level].data != NULL); + + if (min_length < MipMapScaleFactor) { - //----- Continue to search -----// - level = min_level; - - // We cannot fast-forward if there is no mip-map data at - // at the minimum level. - fast_forward = (_mip_map[level].data != NULL); - // Search individual samples up to the beginning of // the next first level mip map block - uint64_t final_index = min(end, + const uint64_t final_index = min(end, pow2_ceil(index, MipMapScalePower)); for (; index < final_index && @@ -426,78 +290,97 @@ int LogicSnapshot::get_first_edge( break; } } + } + else + { + // If resolution is less than a mip map block, + // round up to the beginning of the mip-map block + // for this level of detail + const int min_level_scale_power = + (level + 1) * MipMapScalePower; + index = pow2_ceil(index, min_level_scale_power); + if (index >= end) + return false; - if (fast_forward) { + // We can fast forward only if there was no change + const bool sample = + (get_sample(index) & sig_mask) != 0; + if (last_sample != sample) + fast_forward = false; + } - // Fast forward: This involves zooming out to higher - // levels of the mip map searching for changes, then - // zooming in on them to find the point where the edge - // begins. + if (fast_forward) { - // Slide right and zoom out at the beginnings of mip-map - // blocks until we encounter a change - while (1) { - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; + // Fast forward: This involves zooming out to higher + // levels of the mip map searching for changes, then + // zooming in on them to find the point where the edge + // begins. - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) + // Slide right and zoom out at the beginnings of mip-map + // blocks until we encounter a change + while (1) { + const int level_scale_power = + (level + 1) * MipMapScalePower; + const uint64_t offset = + index >> level_scale_power; + + // Check if we reached the last block at this + // level, or if there was a change in this block + if (offset >= _mip_map[level].length || + (get_subsample(level, offset) & + sig_mask)) + break; + + if ((offset & ~(~0 << MipMapScalePower)) == 0) { + // If we are now at the beginning of a + // higher level mip-map block ascend one + // level + if (level + 1 >= ScaleStepCount || + !_mip_map[level + 1].data) break; - if ((offset & ~(~0 << MipMapScalePower)) == 0) { - // If we are now at the beginning of a - // higher level mip-map block ascend one - // level - if (level + 1 >= ScaleStepCount || - !_mip_map[level + 1].data) - break; - - level++; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } + level++; + } else { + // Slide right to the beginning of the + // next mip map block + index = pow2_ceil(index + 1, + level_scale_power); } + } - // Zoom in, and slide right until we encounter a change, - // and repeat until we reach min_level - while (1) { - assert(_mip_map[level].data); + // Zoom in, and slide right until we encounter a change, + // and repeat until we reach min_level + while (1) { + assert(_mip_map[level].data); - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; + const int level_scale_power = + (level + 1) * MipMapScalePower; + const uint64_t offset = + index >> level_scale_power; - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) { - // Zoom in unless we reached the minimum - // zoom - if (level == min_level) - break; + // Check if we reached the last block at this + // level, or if there was a change in this block + if (offset >= _mip_map[level].length || + (get_subsample(level, offset) & + sig_mask)) { + // Zoom in unless we reached the minimum + // zoom + if (level == min_level) + break; - level--; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } + level--; + } else { + // Slide right to the beginning of the + // next mip map block + index = pow2_ceil(index + 1, + level_scale_power); } + } - // If individual samples within the limit of resolution, - // do a linear search for the next transition within the - // block + // If individual samples within the limit of resolution, + // do a linear search for the next transition within the + // block + if (min_length < MipMapScaleFactor) { for (; index < end; index++) { const bool sample = (get_sample(index) & sig_mask) != 0; @@ -505,90 +388,45 @@ int LogicSnapshot::get_first_edge( break; } } - - //----- Store the edge -----// - - // Take the last sample of the quanization block - final_index = index + block_length; - if (index + block_length > end) - break; - - // Store the final state - const bool final_sample = - (get_sample(final_index - 1) & sig_mask) != 0; - if (final_index > 1) { - const bool final_flag_sample = ((get_sample(final_index - 1) & flag_mask) != 0); - const bool final_pre_flag_sample = ((get_sample(final_index - 2) & flag_mask) != 0); - if (final_sample != last_sample && - ((edge_type == -1) || final_sample == (edge_type != 0)) && - ((flag == -1) || (final_flag_sample == (flag != 0) && final_flag_sample == final_pre_flag_sample))) { - edge_index = index; - edge = final_sample; - return SR_OK; - } - } - - index = final_index; - last_sample = final_sample; } - // Add the final state - const bool end_sample = ((get_sample(end) & sig_mask) != 0); - const bool end_flag_sample = ((get_sample(end) & flag_mask) != 0); - const bool end_pre_flag_sample = ((get_sample(end - 1) & flag_mask) != 0); - if (end_sample != last_sample && - ((edge_type == -1) || end_sample == (edge_type != 0)) && - ((flag == -1) || (end_flag_sample == (flag != 0) && end_flag_sample == end_pre_flag_sample))) { - edge_index = end; - edge = end_sample; - return SR_OK; - } else { - return SR_ERR; - } + if (index >= end) + return false; + else + return true; } -void LogicSnapshot::get_edges( - std::vector &edges, - uint64_t start, uint64_t end, int sig_index, int edge_type) +bool LogicSnapshot::get_pre_edge( + uint64_t &index, bool last_sample, + float min_length, int sig_index) { - uint64_t index = start; unsigned int level; - bool last_sample; bool fast_forward; - assert(end <= get_sample_count()); - assert(start <= end); - assert(sig_index >= 0); - assert(sig_index < 64); + assert(index < get_sample_count()); - boost::lock_guard lock(_mutex); - - const uint64_t block_length = 1; - const unsigned int min_level = 0; + const unsigned int min_level = max((int)floorf(logf(min_length) / + LogMipMapScaleFactor) - 1, 0); const uint64_t sig_mask = 1ULL << sig_index; - if (!edges.empty()) - edges.clear(); - // Store the initial state - last_sample = (get_sample(start) & sig_mask) != 0; + //----- Continue to search -----// + level = min_level; - while (index + block_length <= end) + // We cannot fast-forward if there is no mip-map data at + // at the minimum level. + fast_forward = (_mip_map[level].data != NULL); + + if (min_length < MipMapScaleFactor) { - //----- Continue to search -----// - level = min_level; + // Search individual samples down to the ending of + // the previous first level mip map block + uint64_t final_index; + if (index < (1 << MipMapScalePower)) + final_index = 0; + else + final_index = pow2_ceil(index + 1, MipMapScalePower) - (1 << MipMapScalePower) - 1; - // We cannot fast-forward if there is no mip-map data at - // at the minimum level. - fast_forward = (_mip_map[level].data != NULL); - - // Search individual samples up to the beginning of - // the next first level mip map block - uint64_t final_index = min(end, - pow2_ceil(index, MipMapScalePower)); - - for (; index < final_index && - (index & ~(~0 << MipMapScalePower)) != 0; - index++) + for (; index >= final_index; index--) { const bool sample = (get_sample(index) & sig_mask) != 0; @@ -596,267 +434,125 @@ void LogicSnapshot::get_edges( // If there was a change we cannot fast forward if (sample != last_sample) { fast_forward = false; - break; + index++; + return true; } + + if (index == 0) + return false; } - - if (fast_forward) { - - // Fast forward: This involves zooming out to higher - // levels of the mip map searching for changes, then - // zooming in on them to find the point where the edge - // begins. - - // Slide right and zoom out at the beginnings of mip-map - // blocks until we encounter a change - while (1) { - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; - - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) - break; - - if ((offset & ~(~0 << MipMapScalePower)) == 0) { - // If we are now at the beginning of a - // higher level mip-map block ascend one - // level - if (level + 1 >= ScaleStepCount || - !_mip_map[level + 1].data) - break; - - level++; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } - } - - // Zoom in, and slide right until we encounter a change, - // and repeat until we reach min_level - while (1) { - assert(_mip_map[level].data); - - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; - - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) { - // Zoom in unless we reached the minimum - // zoom - if (level == min_level) - break; - - level--; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } - } - - // If individual samples within the limit of resolution, - // do a linear search for the next transition within the - // block - for (; index < end; index++) { - const bool sample = (get_sample(index) & - sig_mask) != 0; - if (sample != last_sample) - break; - } - } - - //----- Store the edge -----// - - // Take the last sample of the quanization block - final_index = index + block_length; - if (index + block_length > end) - break; - - // Store the final state - const bool final_sample = - (get_sample(final_index - 1) & sig_mask) != 0; - if ((edge_type == -1) || (final_sample == (edge_type != 0))) - edges.push_back(pair(final_index - 1, final_sample)); - - index = final_index; - last_sample = final_sample; } - - // Add the final state - const bool end_sample = ((get_sample(end) & sig_mask) != 0); - if ((end_sample != last_sample) && - ((edge_type == -1) || (end_sample == (edge_type != 0)))) - edges.push_back(pair(end, end_sample)); -} - -uint64_t LogicSnapshot::get_min_pulse(uint64_t start, uint64_t end, int sig_index) -{ - uint64_t index = start; - unsigned int level; - bool last_sample; - bool fast_forward; - uint64_t last_index; - uint64_t min_pulse = end - start; - - assert(end <= get_sample_count()); - assert(start <= end); - assert(sig_index >= 0); - assert(sig_index < 64); - - boost::lock_guard lock(_mutex); - - const uint64_t block_length = 1; - const unsigned int min_level = 0; - const uint64_t sig_mask = 1ULL << sig_index; - - // Store the initial state - last_index = start; - last_sample = (get_sample(start) & sig_mask) != 0; - - while (index + block_length <= end) + else { - //----- Continue to search -----// - level = min_level; + // If resolution is less than a mip map block, + // round up to the beginning of the mip-map block + // for this level of detail + const int min_level_scale_power = + (level + 1) * MipMapScalePower; + if (index < (1 << min_level_scale_power)) + index = 0; + else + index = pow2_ceil(index, min_level_scale_power) - (1 << min_level_scale_power) - 1; - // We cannot fast-forward if there is no mip-map data at - // at the minimum level. - fast_forward = (_mip_map[level].data != NULL); - - // Search individual samples up to the beginning of - // the next first level mip map block - uint64_t final_index = min(end, - pow2_ceil(index, MipMapScalePower)); - - for (; index < final_index && - (index & ~(~0 << MipMapScalePower)) != 0; - index++) - { - const bool sample = - (get_sample(index) & sig_mask) != 0; - - // If there was a change we cannot fast forward - if (sample != last_sample) { - fast_forward = false; - break; - } + // We can fast forward only if there was no change + const bool sample = + (get_sample(index) & sig_mask) != 0; + if (last_sample != sample) { + fast_forward = false; + index++; + return true; } - if (fast_forward) { - - // Fast forward: This involves zooming out to higher - // levels of the mip map searching for changes, then - // zooming in on them to find the point where the edge - // begins. - - // Slide right and zoom out at the beginnings of mip-map - // blocks until we encounter a change - while (1) { - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; - - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) - break; - - if ((offset & ~(~0 << MipMapScalePower)) == 0) { - // If we are now at the beginning of a - // higher level mip-map block ascend one - // level - if (level + 1 >= ScaleStepCount || - !_mip_map[level + 1].data) - break; - - level++; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } - } - - // Zoom in, and slide right until we encounter a change, - // and repeat until we reach min_level - while (1) { - assert(_mip_map[level].data); - - const int level_scale_power = - (level + 1) * MipMapScalePower; - const uint64_t offset = - index >> level_scale_power; - - // Check if we reached the last block at this - // level, or if there was a change in this block - if (offset >= _mip_map[level].length || - (get_subsample(level, offset) & - sig_mask)) { - // Zoom in unless we reached the minimum - // zoom - if (level == min_level) - break; - - level--; - } else { - // Slide right to the beginning of the - // next mip map block - index = pow2_ceil(index + 1, - level_scale_power); - } - } - - // If individual samples within the limit of resolution, - // do a linear search for the next transition within the - // block - for (; index < end; index++) { - const bool sample = (get_sample(index) & - sig_mask) != 0; - if (sample != last_sample) - break; - } - } - - //----- Store the edge -----// - - // Take the last sample of the quanization block - final_index = index + block_length; - if (index + block_length > end) - break; - - // get pulse width - const bool final_sample = - (get_sample(final_index - 1) & sig_mask) != 0; - min_pulse = min(index - last_index, min_pulse); - last_index = index; - if (min_pulse == 1) - break; - - index = final_index; - last_sample = final_sample; + if (index == 0) + return false; } - // Add the final state - min_pulse = min(end - last_index, min_pulse); + if (fast_forward) { - return min_pulse; + // Fast forward: This involves zooming out to higher + // levels of the mip map searching for changes, then + // zooming in on them to find the point where the edge + // begins. + + // Slide left and zoom out at the endings of mip-map + // blocks until we encounter a change + while (1) { + const int level_scale_power = + (level + 1) * MipMapScalePower; + const uint64_t offset = + index >> level_scale_power; + + // Check if we reached the first block at this + // level, or if there was a change in this block + if (offset == 0 || + (get_subsample(level, offset) & + sig_mask)) + break; + + if (((offset+1) & ~(~0 << MipMapScalePower)) == 0) { + // If we are now at the ending of a + // higher level mip-map block ascend one + // level + if (level + 1 >= ScaleStepCount || + !_mip_map[level + 1].data) + break; + + level++; + } else { + // Slide left to the beginning of the + // previous mip map block + index = pow2_ceil(index + 1, + level_scale_power) - (1 << level_scale_power) - 1; + } + } + + // Zoom in, and slide left until we encounter a change, + // and repeat until we reach min_level + while (1) { + assert(_mip_map[level].data); + + const int level_scale_power = + (level + 1) * MipMapScalePower; + const uint64_t offset = + index >> level_scale_power; + + // Check if we reached the first block at this + // level, or if there was a change in this block + if (offset == 0 || + (get_subsample(level, offset) & + sig_mask)) { + // Zoom in unless we reached the minimum + // zoom + if (level == min_level) + break; + + level--; + } else { + // Slide left to the ending of the + // previous mip map block + index = pow2_ceil(index + 1, + level_scale_power) - (1 << level_scale_power) - 1; + } + } + + // If individual samples within the limit of resolution, + // do a linear search for the next transition within the + // block + if (min_length < MipMapScaleFactor) { + for (; index >= 0; index--) { + const bool sample = (get_sample(index) & + sig_mask) != 0; + if (sample != last_sample) { + index++; + return true; + } + + if (index == 0) + return false; + } + } + } + return false; } uint64_t LogicSnapshot::get_subsample(int level, uint64_t offset) const diff --git a/DSView/pv/data/logicsnapshot.h b/DSView/pv/data/logicsnapshot.h index 3914a2ed..6e9aae12 100644 --- a/DSView/pv/data/logicsnapshot.h +++ b/DSView/pv/data/logicsnapshot.h @@ -90,15 +90,11 @@ public: uint64_t start, uint64_t end, float min_length, int sig_index); - int get_first_edge(uint64_t &edge_index, bool &edge, - uint64_t start, uint64_t end, - int sig_index, int edge_type, - int flag_index, int flag); + bool get_nxt_edge(uint64_t &index, bool last_sample, uint64_t end, + float min_length, int sig_index); - void get_edges(std::vector &edges, - uint64_t start, uint64_t end, int sig_index, int edge_type); - - uint64_t get_min_pulse(uint64_t start, uint64_t end, int sig_index); + bool get_pre_edge(uint64_t &index, bool last_sample, + float min_length, int sig_index); private: uint64_t get_subsample(int level, uint64_t offset) const; diff --git a/DSView/pv/device/devinst.cpp b/DSView/pv/device/devinst.cpp index b02c3551..1a023c05 100644 --- a/DSView/pv/device/devinst.cpp +++ b/DSView/pv/device/devinst.cpp @@ -170,7 +170,7 @@ double DevInst::get_sample_time() if (sample_rate == 0) sample_time = 0; else - sample_time = sample_limit * 1.0f / sample_rate; + sample_time = sample_limit * 1.0 / sample_rate; return sample_time; } 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..eb3960c4 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, @@ -48,8 +48,7 @@ WaitingDialog::WaitingDialog(QWidget *parent, shared_ptr de this->setFixedSize((GIF_SIZE+TIP_WIDTH)*2, (GIF_SIZE+TIP_HEIGHT)*2); int midx = this->width() / 2; int midy = this->height() / 2; - this->setWindowOpacity(0.6); - this->setStyleSheet("background-color: rgb(255, 255, 255);"); + this->setWindowOpacity(0.7); this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); label = new QLabel(this); @@ -60,7 +59,6 @@ WaitingDialog::WaitingDialog(QWidget *parent, shared_ptr de tips = new QLabel(this); tips->setText(TIPS_INFO); - tips->setStyleSheet("color: rgb(17, 133, 209); background-color: transparent;"); QFont font; font.setPointSize(10); font.setBold(true); @@ -71,8 +69,6 @@ WaitingDialog::WaitingDialog(QWidget *parent, shared_ptr de timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(changeText())); - QString styleSheet = "* {color: rgb(255, 255, 255); background-color: rgb(17, 133, 209);} *:hover {background-color: rgb(238, 178, 17);}"; - _button_box.setStyleSheet(styleSheet); _button_box.setGeometry(width()-_button_box.width()-30, height()-_button_box.height()-15, _button_box.width(), _button_box.height()); connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index f782ee3a..211acdff 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -26,6 +26,7 @@ #include "../sigsession.h" #include "../view/cursor.h" #include "../view/view.h" +#include "../view/viewport.h" #include "../view/timemarker.h" #include "../view/ruler.h" #include "../view/logicsignal.h" @@ -54,9 +55,10 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : _mouse_groupBox = new QGroupBox("Mouse measurement", this); _fen_checkBox = new QCheckBox("Enable floating measurement", this); _fen_checkBox->setChecked(true); - _width_label = new QLabel(view.get_mm_width(), this); - _period_label = new QLabel(view.get_mm_period(), this); - _freq_label = new QLabel(view.get_mm_freq(), this); + _width_label = new QLabel("#####", this); + _period_label = new QLabel("#####", this); + _freq_label = new QLabel("#####", this); + _duty_label = new QLabel("#####", this); _mouse_layout = new QGridLayout(); _mouse_layout->addWidget(_fen_checkBox, 0, 0, 1, 2); @@ -66,10 +68,13 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : _mouse_layout->addWidget(_period_label, 2, 1); _mouse_layout->addWidget(new QLabel("Frequency: ", this), 3, 0); _mouse_layout->addWidget(_freq_label, 3, 1); + _mouse_layout->addWidget(new QLabel("Duty Cycle: ", this), 4, 0); + _mouse_layout->addWidget(_duty_label, 4, 1); _mouse_layout->addWidget(new QLabel(this), 0, 2); _mouse_layout->addWidget(new QLabel(this), 1, 2); _mouse_layout->addWidget(new QLabel(this), 2, 2); _mouse_layout->addWidget(new QLabel(this), 3, 2); + _mouse_layout->addWidget(new QLabel(this), 4, 2); _mouse_layout->setColumnStretch(2, 1); _mouse_groupBox->setLayout(_mouse_layout); @@ -134,6 +139,7 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : connect(_t3_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(delta_update())); connect(_fen_checkBox, SIGNAL(stateChanged(int)), &_view, SLOT(set_measure_en(int))); + connect(_view.get_viewport(), SIGNAL(mouse_measure()), this, SLOT(mouse_measure())); } MeasureDock::~MeasureDock() @@ -216,11 +222,12 @@ void MeasureDock::cursor_update() update(); } -void MeasureDock::mouse_moved() +void MeasureDock::mouse_measure() { - _width_label->setText(_view.get_mm_width()); - _period_label->setText(_view.get_mm_period()); - _freq_label->setText(_view.get_mm_freq()); + _width_label->setText(_view.get_viewport()->get_measure("width")); + _period_label->setText(_view.get_viewport()->get_measure("period")); + _freq_label->setText(_view.get_viewport()->get_measure("frequency")); + _duty_label->setText(_view.get_viewport()->get_measure("duty")); } void MeasureDock::cursor_moved() diff --git a/DSView/pv/dock/measuredock.h b/DSView/pv/dock/measuredock.h index 500ec66e..fa29db64 100644 --- a/DSView/pv/dock/measuredock.h +++ b/DSView/pv/dock/measuredock.h @@ -74,7 +74,7 @@ private slots: public slots: void cursor_update(); void cursor_moved(); - void mouse_moved(); + void mouse_measure(); private: SigSession &_session; @@ -86,6 +86,7 @@ private: QLabel *_width_label; QLabel *_period_label; QLabel *_freq_label; + QLabel *_duty_label; QGridLayout *_cursor_layout; QGroupBox *_cursor_groupBox; diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index 1e382b91..ee4ae6f2 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -369,11 +369,12 @@ void TriggerDock::device_change() if (stream || strcmp(_session.get_device()->dev_inst()->driver->name, "DSLogic") != 0) { - position_spinBox->setDisabled(true); - position_slider->setDisabled(true); + const int maxRange = SR_MB(11)*100/_session.get_device()->get_sample_limit(); + position_spinBox->setRange(0, maxRange); + position_slider->setRange(0, maxRange); } else { - position_spinBox->setDisabled(false); - position_slider->setDisabled(false); + position_spinBox->setRange(0, 99); + position_slider->setRange(0, 99); } } diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 7a0c9407..b37dc504 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -180,7 +180,7 @@ void MainWindow::setup_ui() connect(_sampling_bar, SIGNAL(device_selected()), this, SLOT(update_device_list())); - connect(_sampling_bar, SIGNAL(device_updated()), &_session, + connect(_sampling_bar, SIGNAL(device_updated()), this, SLOT(reload())); connect(_sampling_bar, SIGNAL(run_stop()), this, SLOT(run_stop())); @@ -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, @@ -244,8 +243,6 @@ void MainWindow::setup_ui() SLOT(cursor_update())); connect(_view, SIGNAL(cursor_moved()), _measure_widget, SLOT(cursor_moved())); - connect(_view, SIGNAL(mouse_moved()), _measure_widget, - SLOT(mouse_moved())); connect(_view, SIGNAL(mode_changed()), this, SLOT(update_device_list())); @@ -320,6 +317,12 @@ void MainWindow::update_device_list() _logo_bar->dsl_connected(false); } +void MainWindow::reload() +{ + _trigger_widget->device_change(); + _session.reload(); +} + void MainWindow::load_file(QString file_name) { try { @@ -522,7 +525,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/mainwindow.h b/DSView/pv/mainwindow.h index a52e901c..34566805 100644 --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -90,6 +90,8 @@ private slots: */ void update_device_list(); + void reload(); + void show_session_error( const QString text, const QString info_text); diff --git a/DSView/pv/prop/binding/binding_deviceoptions.cpp b/DSView/pv/prop/binding/binding_deviceoptions.cpp index 8c8d9e48..ba5045d7 100644 --- a/DSView/pv/prop/binding/binding_deviceoptions.cpp +++ b/DSView/pv/prop/binding/binding_deviceoptions.cpp @@ -90,6 +90,7 @@ DeviceOptions::DeviceOptions(struct sr_dev_inst *sdi) : case SR_CONF_STREAM: case SR_CONF_TEST: case SR_CONF_STATUS: + case SR_CONF_FACTOR: bind_enum(name, key, gvar_list); break; diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 9e51799a..2a4f5006 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -54,21 +54,27 @@ #include #include +#include +#include +#include #include -using boost::dynamic_pointer_cast; -using boost::function; -using boost::lock_guard; -using boost::mutex; -using boost::shared_ptr; -using std::list; -using std::map; -using std::set; -using std::string; -using std::vector; -using std::deque; -using std::min; +//using boost::dynamic_pointer_cast; +//using boost::function; +//using boost::lock_guard; +//using boost::mutex; +//using boost::shared_ptr; +//using std::list; +//using std::map; +//using std::set; +//using std::string; +//using std::vector; +//using std::deque; +//using std::min; + +using namespace boost; +using namespace std; namespace pv { @@ -97,10 +103,6 @@ SigSession::SigSession(DeviceManager &device_manager) : SigSession::~SigSession() { stop_capture(); - if (_hotplug_handle) { - stop_hotplug_proc(); - deregister_hotplug_callback(); - } ds_trigger_destroy(); @@ -108,6 +110,11 @@ SigSession::~SigSession() // TODO: This should not be necessary _session = NULL; + + if (_hotplug_handle) { + stop_hotplug_proc(); + deregister_hotplug_callback(); + } } boost::shared_ptr SigSession::get_device() const @@ -115,7 +122,7 @@ boost::shared_ptr SigSession::get_device() const return _dev_inst; } -void SigSession::set_device(shared_ptr dev_inst) throw(QString) +void SigSession::set_device(boost::shared_ptr dev_inst) throw(QString) { using pv::device::Device; @@ -151,13 +158,13 @@ void SigSession::set_file(const string &name) throw(QString) // Deslect the old device, because file type detection in File::create // destorys the old session inside libsigrok. try { - set_device(shared_ptr()); + set_device(boost::shared_ptr()); } catch(const QString e) { throw(e); return; } try { - set_device(shared_ptr(device::File::create(name))); + set_device(boost::shared_ptr(device::File::create(name))); } catch(const QString e) { throw(e); return; @@ -179,10 +186,168 @@ 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; + if (_dev_inst->dev_inst()->mode == DSO && strcmp((*supportedModules)->id, "csv")) + 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){ + boost::shared_ptr snapshot; + int channel_type; + + if (_dev_inst->dev_inst()->mode == LOGIC) { + const deque< boost::shared_ptr > &snapshots = + _logic_data->get_snapshots(); + if(snapshots.empty()) + return; + snapshot = snapshots.front(); + channel_type = SR_CHANNEL_LOGIC; + } else if (_dev_inst->dev_inst()->mode == DSO) { + const deque< boost::shared_ptr > &snapshots = + _dso_data->get_snapshots(); + if(snapshots.empty()) + return; + snapshot = snapshots.front(); + channel_type = SR_CHANNEL_DSO; + } else { + return; + } + + 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; + + + 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); + GVariant* typeGVariant = g_variant_new_int16(channel_type); + g_hash_table_insert(params, (char*)"type", typeGVariant); + BOOST_FOREACH(const boost::shared_ptr s, _signals) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + GVariant* timebaseGVariant = g_variant_new_uint64(dsoSig->get_hDialValue()); + g_hash_table_insert(params, (char*)"timebase", timebaseGVariant); + break; + } + } + + struct sr_output output; + 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; + if (_dev_inst->dev_inst()->mode == LOGIC) { + 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; + } + }); + } else if (_dev_inst->dev_inst()->mode == DSO) { + future = QtConcurrent::run([&]{ + saveFileThreadRunning = true; + unsigned char* datat = (unsigned char*)snapshot->get_data(); + unsigned int numsamples = snapshot->get_sample_count(); + GString *data_out; + int usize = 8192; + int size = usize; + struct sr_datafeed_dso dp; + struct sr_datafeed_packet p; + for(uint64_t i = 0; i < numsamples; i+=usize){ + if(numsamples - i < usize) + size = numsamples - i; + dp.data = &datat[i*snapshot->get_channel_num()]; + dp.num_samples = size; + p.type = SR_DF_DSO; + p.payload = &dp; + 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; - const list< shared_ptr > &devices = + boost::shared_ptr default_device; + const list > &devices = _device_manager.devices(); if (!devices.empty()) { @@ -190,7 +355,7 @@ void SigSession::set_default_device(boost::function error_ default_device = devices.front(); // Try and find the DreamSourceLab device and select that by default - BOOST_FOREACH (shared_ptr dev, devices) + BOOST_FOREACH (boost::shared_ptr dev, devices) if (dev->dev_inst() && strcmp(dev->dev_inst()->driver->name, "demo") != 0) { @@ -212,7 +377,7 @@ void SigSession::release_device(device::DevInst *dev_inst) assert(_dev_inst.get() == dev_inst); assert(_capture_state != Running); - _dev_inst = shared_ptr(); + _dev_inst = boost::shared_ptr(); //_dev_inst.reset(); } @@ -286,11 +451,11 @@ vector< boost::shared_ptr > SigSession::get_group_signals() return _group_traces; } -set< shared_ptr > SigSession::get_data() const +set< boost::shared_ptr > SigSession::get_data() const { lock_guard lock(_signals_mutex); - set< shared_ptr > data; - BOOST_FOREACH(const shared_ptr sig, _signals) { + set< boost::shared_ptr > data; + BOOST_FOREACH(const boost::shared_ptr sig, _signals) { assert(sig); data.insert(sig->data()); } @@ -352,7 +517,7 @@ void SigSession::set_capture_state(capture_state state) capture_state_changed(state); } -void SigSession::sample_thread_proc(shared_ptr dev_inst, +void SigSession::sample_thread_proc(boost::shared_ptr dev_inst, boost::function error_handler) { assert(dev_inst); @@ -416,8 +581,8 @@ void SigSession::read_sample_rate(const sr_dev_inst *const sdi) } // Set the sample rate of all data - const set< shared_ptr > data_set = get_data(); - BOOST_FOREACH(shared_ptr data, data_set) { + const set< boost::shared_ptr > data_set = get_data(); + BOOST_FOREACH(boost::shared_ptr data, data_set) { assert(data); data->set_samplerate(sample_rate); } @@ -502,6 +667,7 @@ void SigSession::init_signals() assert(_dev_inst); stop_capture(); + vector< boost::shared_ptr > sigs; boost::shared_ptr signal; unsigned int logic_probe_count = 0; unsigned int dso_probe_count = 0; @@ -561,8 +727,6 @@ void SigSession::init_signals() // Make the logic probe list { - _signals.clear(); - for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { const sr_channel *const probe = (const sr_channel *)l->data; @@ -576,19 +740,24 @@ void SigSession::init_signals() break; case SR_CHANNEL_DSO: - signal = shared_ptr( + signal = boost::shared_ptr( new view::DsoSignal(_dev_inst, _dso_data, probe)); break; case SR_CHANNEL_ANALOG: if (probe->enabled) - signal = shared_ptr( + signal = boost::shared_ptr( new view::AnalogSignal(_dev_inst, _analog_data, probe)); break; } if(signal.get()) - _signals.push_back(signal); + sigs.push_back(signal); } + + _signals.clear(); + vector< boost::shared_ptr >().swap(_signals); + _signals = sigs; + signals_changed(); data_updated(); } @@ -601,12 +770,11 @@ void SigSession::reload() if (_capture_state == Running) stop_capture(); + vector< boost::shared_ptr > sigs; boost::shared_ptr signal; // Make the logic probe list { - _signals.clear(); - for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { const sr_channel *const probe = (const sr_channel *)l->data; @@ -614,25 +782,32 @@ void SigSession::reload() signal.reset(); switch(probe->type) { case SR_CHANNEL_LOGIC: - if (probe->enabled) + if (probe->enabled && probe->index < _signals.size()) + signal = boost::shared_ptr( + new view::LogicSignal(*_signals[probe->index].get(), _logic_data, probe)); + else if (probe->enabled) signal = boost::shared_ptr( new view::LogicSignal(_dev_inst, _logic_data, probe)); break; case SR_CHANNEL_DSO: - signal = shared_ptr( + signal = boost::shared_ptr( new view::DsoSignal(_dev_inst,_dso_data, probe)); break; case SR_CHANNEL_ANALOG: if (probe->enabled) - signal = shared_ptr( + signal = boost::shared_ptr( new view::AnalogSignal(_dev_inst, _analog_data, probe)); break; } if (signal.get()) - _signals.push_back(signal); + sigs.push_back(signal); } + + _signals.clear(); + vector< boost::shared_ptr >().swap(_signals); + _signals = sigs; } signals_changed(); @@ -718,7 +893,7 @@ void SigSession::feed_in_logic(const sr_datafeed_logic &logic) return; } - receive_data(logic.length/logic.unitsize); + emit receive_data(logic.length/logic.unitsize); data_received(); //data_updated(); } @@ -844,7 +1019,7 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, _cur_analog_snapshot.reset(); } #ifdef ENABLE_DECODE - for (vector< shared_ptr >::iterator i = + for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); i != _decode_traces.end(); i++) @@ -977,7 +1152,7 @@ uint16_t SigSession::get_ch_num(int type) uint16_t dso_ch_num = 0; uint16_t analog_ch_num = 0; if (_dev_inst->dev_inst()) { - BOOST_FOREACH(const shared_ptr s, _signals) + BOOST_FOREACH(const boost::shared_ptr s, _signals) { assert(s); if (dynamic_pointer_cast(s) && s->enabled()) { @@ -1013,15 +1188,15 @@ uint16_t SigSession::get_ch_num(int type) bool SigSession::add_decoder(srd_decoder *const dec) { bool ret = false; - map > probes; - shared_ptr decoder_stack; + map > probes; + boost::shared_ptr decoder_stack; try { //lock_guard lock(_signals_mutex); // Create the decoder - decoder_stack = shared_ptr( + decoder_stack = boost::shared_ptr( new data::DecoderStack(*this, dec)); // Make a list of all the probes @@ -1037,7 +1212,7 @@ bool SigSession::add_decoder(srd_decoder *const dec) decoder_stack->stack().front()->set_probes(probes); // Create the decode signal - shared_ptr d( + boost::shared_ptr d( new view::DecodeTrace(*this, decoder_stack, _decode_traces.size())); if (d->create_popup()) { @@ -1060,7 +1235,7 @@ bool SigSession::add_decoder(srd_decoder *const dec) return ret; } -vector< shared_ptr > SigSession::get_decode_signals() const +vector< boost::shared_ptr > SigSession::get_decode_signals() const { lock_guard lock(_signals_mutex); return _decode_traces; @@ -1068,7 +1243,7 @@ vector< shared_ptr > SigSession::get_decode_signals() const void SigSession::remove_decode_signal(view::DecodeTrace *signal) { - for (vector< shared_ptr >::iterator i = + for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); i != _decode_traces.end(); i++) @@ -1083,7 +1258,7 @@ void SigSession::remove_decode_signal(view::DecodeTrace *signal) void SigSession::remove_decode_signal(int index) { int cur_index = 0; - for (vector< shared_ptr >::iterator i = + for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); i != _decode_traces.end(); i++) @@ -1101,7 +1276,7 @@ void SigSession::remove_decode_signal(int index) void SigSession::rst_decoder(int index) { int cur_index = 0; - for (vector< shared_ptr >::iterator i = + for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); i != _decode_traces.end(); i++) @@ -1122,7 +1297,7 @@ void SigSession::rst_decoder(int index) void SigSession::rst_decoder(view::DecodeTrace *signal) { - for (vector< shared_ptr >::iterator i = + for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); i != _decode_traces.end(); i++) diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index cf4674dd..fd406950 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #include #include #include +#include #include #include @@ -86,8 +88,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 +118,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 +138,7 @@ public: std::vector< boost::shared_ptr > get_group_signals(); + QList getSuportedExportFormats(); #ifdef ENABLE_DECODE bool add_decoder(srd_decoder *const dec); @@ -282,11 +289,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..9efc9cf7 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,38 @@ 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 { + 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 +169,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/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index b7414547..289fce77 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -667,19 +667,21 @@ void SamplingBar::on_run_stop() const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; - GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); - if (gvar != NULL) { - bool zero = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - if (zero) { - QMessageBox msg(this); - msg.setText("Zero Adjustment"); - msg.setInformativeText("Please adjust zero skew and save the result!"); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); - msg.exec(); - zero_adj(); - return; + if (dev_inst->dev_inst()->mode == DSO) { + GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); + if (gvar != NULL) { + bool zero = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + if (zero) { + QMessageBox msg(this); + msg.setText("Zero Adjustment"); + msg.setInformativeText("Please adjust zero skew and save the result!"); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); + zero_adj(); + return; + } } } run_stop(); @@ -695,22 +697,24 @@ void SamplingBar::on_instant_stop() const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; - GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); - if (gvar != NULL) { - bool zero = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - if (zero) { - QMessageBox msg(this); - msg.setText("Zero Adjustment"); - if(strcmp(dev_inst->dev_inst()->driver->name, "DSLogic") == 0) - msg.setInformativeText("Please adjust zero skew and save the result!\nPlease left both of channels unconnect for zero adjustment!"); - else - msg.setInformativeText("Please adjust zero skew and save the result!"); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); - msg.exec(); - zero_adj(); - return; + if (dev_inst->dev_inst()->mode == DSO) { + GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); + if (gvar != NULL) { + bool zero = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + if (zero) { + QMessageBox msg(this); + msg.setText("Zero Adjustment"); + if(strcmp(dev_inst->dev_inst()->driver->name, "DSLogic") == 0) + msg.setInformativeText("Please adjust zero skew and save the result!\nPlease left both of channels unconnect for zero adjustment!"); + else + msg.setInformativeText("Please adjust zero skew and save the result!"); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); + zero_adj(); + return; + } } } instant_stop(); 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/cursor.cpp b/DSView/pv/view/cursor.cpp index 0945a6e8..d93b1495 100644 --- a/DSView/pv/view/cursor.cpp +++ b/DSView/pv/view/cursor.cpp @@ -25,6 +25,7 @@ #include "ruler.h" #include "view.h" +#include "../device/device.h" #include #include @@ -51,15 +52,16 @@ const int Cursor::ArrowSize = 10; const int Cursor::CloseSize = 10; -Cursor::Cursor(View &view, QColor color, double time) : - TimeMarker(view, color, time), +Cursor::Cursor(View &view, QColor color, uint64_t index) : + TimeMarker(view, color, index), _other(*this) { } QRectF Cursor::get_label_rect(const QRect &rect) const { - const float x = (_time - _view.offset()) / _view.scale(); + const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + const double x = _index/samples_per_pixel - (_view.offset() / _view.scale()); const QSizeF label_size( _text_size.width() + View::LabelPadding.width() * 2, @@ -114,7 +116,7 @@ void Cursor::paint_label(QPainter &p, const QRect &rect, p.drawLine(close.left() + 2, close.bottom() - 2, close.right() - 2, close.top() + 2); p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, - Ruler::format_time(_time, prefix, 2)); + Ruler::format_real_time(_index, _view.session().get_device()->get_sample_rate())); const QRectF arrowRect = QRectF(r.bottomLeft().x(), r.bottomLeft().y(), r.width(), ArrowSize); p.drawText(arrowRect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(index)); @@ -141,7 +143,7 @@ void Cursor::paint_fix_label(QPainter &p, const QRect &rect, p.setPen(Qt::white); p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, - Ruler::format_time(_time, prefix, 2)); + Ruler::format_real_time(_index, _view.session().get_device()->get_sample_rate())); const QRectF arrowRect = QRectF(r.bottomLeft().x(), r.bottomLeft().y(), r.width(), ArrowSize); p.drawText(arrowRect, Qt::AlignCenter | Qt::AlignVCenter, label); @@ -150,7 +152,7 @@ void Cursor::paint_fix_label(QPainter &p, const QRect &rect, void Cursor::compute_text_size(QPainter &p, unsigned int prefix) { _text_size = p.boundingRect(QRectF(), 0, - Ruler::format_time(_time, prefix, 2)).size(); + Ruler::format_real_time(_index, _view.session().get_device()->get_sample_rate())).size(); } } // namespace view diff --git a/DSView/pv/view/cursor.h b/DSView/pv/view/cursor.h index d06c4186..d1b284b7 100644 --- a/DSView/pv/view/cursor.h +++ b/DSView/pv/view/cursor.h @@ -55,7 +55,8 @@ public: * @param time The time to set the flag to. * @param other A reference to the other cursor. */ - Cursor(View &view, QColor color, double time); + Cursor(View &view, QColor color, uint64_t index); + public: /** diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 6a4b080c..ecac15c1 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -117,10 +117,12 @@ const QColor DecodeTrace::OutlineColours[16] = { DecodeTrace::DecodeTrace(pv::SigSession &session, boost::shared_ptr decoder_stack, int index) : Trace(QString::fromUtf8( - decoder_stack->stack().front()->decoder()->name), Trace::DS_DECODER), + decoder_stack->stack().front()->decoder()->name), index, Trace::DS_DECODER), _session(session), _decoder_stack(decoder_stack), - _show_hide_mapper(this) + _show_hide_mapper(this), + _popup_form(NULL), + _popup() { assert(_decoder_stack); @@ -297,21 +299,11 @@ void DecodeTrace::paint_fore(QPainter &p, int left, int right) bool DecodeTrace::create_popup() { - // Clear the layout - - // Transfer the layout and the child widgets to a temporary widget - // which then goes out of scope destroying the layout and all the child - // widgets. - //if (_popup_form) - // QWidget().setLayout(_popup_form); - int ret = false; - QDialog popup; - QFormLayout popup_form; - popup.setLayout(&popup_form); - populate_popup_form(&popup, &popup_form); + _popup = new QDialog(); + create_popup_form(); - if (QDialog::Accepted == popup.exec()) + if (QDialog::Accepted == _popup->exec()) { BOOST_FOREACH(shared_ptr dec, _decoder_stack->stack()) @@ -321,12 +313,28 @@ bool DecodeTrace::create_popup() ret = true; } } - return ret; } - else - return false; + + _popup = NULL; + _popup_form = NULL; + + return ret; } +void DecodeTrace::create_popup_form() +{ + // Clear the layout + + // Transfer the layout and the child widgets to a temporary widget + // which then goes out of scope destroying the layout and all the child + // widgets. + if (_popup_form) + QWidget().setLayout(_popup_form); + + _popup_form = new QFormLayout(_popup); + _popup->setLayout(_popup_form); + populate_popup_form(_popup, _popup_form); +} void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) { @@ -368,8 +376,8 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) new pv::widgets::DecoderMenu(parent); connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)), this, SLOT(on_stack_decoder(srd_decoder*))); - connect(decoder_menu, SIGNAL(selected()), - parent, SLOT(accept())); + //connect(decoder_menu, SIGNAL(selected()), + // parent, SLOT(accept())); QPushButton *const stack_button = new QPushButton(tr("Stack Decoder"), parent); @@ -547,7 +555,8 @@ bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, shared_ptr data; shared_ptr logic_signal; - const int64_t sample_count = _session.get_device()->get_sample_limit(); + //const int64_t sample_count = _session.get_device()->get_sample_limit(); + const int64_t sample_count = _decoder_stack->sample_count(); if (sample_count == 0) return true; @@ -768,7 +777,7 @@ void DecodeTrace::on_stack_decoder(srd_decoder *decoder) new data::decode::Decoder(decoder))); //_decoder_stack->begin_decode(); - create_popup(); + create_popup_form(); } void DecodeTrace::on_show_hide_decoder(int index) diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index fa1c3db3..a7532dd0 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -128,6 +128,8 @@ protected: void paint_type_options(QPainter &p, int right, bool hover, int action); private: + void create_popup_form(); + void populate_popup_form(QWidget *parent, QFormLayout *form); void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, @@ -196,6 +198,8 @@ private: std::vector _cur_row_headings; QSignalMapper _show_hide_mapper; + QFormLayout *_popup_form; + QDialog *_popup; }; } // namespace view 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/dsldial.cpp b/DSView/pv/view/dsldial.cpp index 15574966..f19c7561 100644 --- a/DSView/pv/view/dsldial.cpp +++ b/DSView/pv/view/dsldial.cpp @@ -5,12 +5,12 @@ namespace pv { namespace view { -dslDial::dslDial(const quint64 div, const quint64 step, - const QVector value, const QVector unit) +dslDial::dslDial(const uint64_t div, const uint64_t step, + const QVector value, const QVector unit) { assert(div > 0); assert(step > 0); - assert((quint64)value.count() == div); + assert((uint64_t)value.count() == div); assert(unit.count() > 0); _div = div; @@ -18,6 +18,7 @@ dslDial::dslDial(const quint64 div, const quint64 step, _value = value; _unit = unit; _sel = 0; + _factor = 1; } dslDial::~dslDial() @@ -39,11 +40,11 @@ void dslDial::paint(QPainter &p, QRectF dialRect, QColor dialColor) p.save(); p.translate(dialRect.center()); p.rotate(45); - for (quint64 i = 0; i < _div; i++) { + for (uint64_t i = 0; i < _div; i++) { // draw major ticks p.drawLine(0, dialRect.width()/2+3, 0, dialRect.width()/2+8); // draw minor ticks - for (quint64 j = 0; (j < 5) && (i < _div - 1); j++) { + for (uint64_t j = 0; (j < 5) && (i < _div - 1); j++) { p.drawLine(0, dialRect.width()/2+3, 0, dialRect.width()/2+5); p.rotate(54.0/(_div-1)); } @@ -55,8 +56,8 @@ void dslDial::paint(QPainter &p, QRectF dialRect, QColor dialColor) p.drawLine(-3, 0, 0, dialRect.width()/2-3); p.restore(); // draw value - quint64 displayValue = _value[_sel]; - quint64 displayIndex = 0; + uint64_t displayValue = _value[_sel]*_factor; + uint64_t displayIndex = 0; while(displayValue / _step >= 1) { displayValue = displayValue / _step; displayIndex++; @@ -67,14 +68,14 @@ void dslDial::paint(QPainter &p, QRectF dialRect, QColor dialColor) } -void dslDial::set_sel(quint64 sel) +void dslDial::set_sel(uint64_t sel) { assert(sel < _div); _sel = sel; } -quint64 dslDial::get_sel() +uint64_t dslDial::get_sel() { return _sel; } @@ -95,16 +96,28 @@ bool dslDial::isMax() return false; } -quint64 dslDial::get_value() +uint64_t dslDial::get_value() { return _value[_sel]; } -bool dslDial::set_value(quint64 value) +bool dslDial::set_value(uint64_t value) { assert(_value.contains(value)); _sel = _value.indexOf(value, 0); } +void dslDial::set_factor(uint64_t factor) +{ + if (_factor != factor) { + _factor = factor; + } +} + +uint64_t dslDial::get_factor() +{ + return _factor; +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/dsldial.h b/DSView/pv/view/dsldial.h index a0034cf8..35701959 100644 --- a/DSView/pv/view/dsldial.h +++ b/DSView/pv/view/dsldial.h @@ -10,8 +10,8 @@ namespace view { class dslDial { public: - dslDial(const quint64 div, const quint64 step, - const QVector value, const QVector unit); + dslDial(const uint64_t div, const uint64_t step, + const QVector value, const QVector unit); virtual ~dslDial(); public: @@ -23,23 +23,28 @@ public: void paint(QPainter &p, QRectF dialRect, QColor dialColor); // set/get current select - void set_sel(quint64 sel); - quint64 get_sel(); + void set_sel(uint64_t sel); + uint64_t get_sel(); // boundary detection bool isMin(); bool isMax(); // get current value - quint64 get_value(); - bool set_value(quint64 value); + uint64_t get_value(); + bool set_value(uint64_t value); + + // set/get factor + void set_factor(uint64_t factor); + uint64_t get_factor(); private: - quint64 _div; - quint64 _step; - QVector _value; + uint64_t _div; + uint64_t _step; + QVector _value; QVector _unit; - quint64 _sel; + uint64_t _sel; + uint64_t _factor; }; } // namespace view diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index b08c409f..62a22168 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -103,13 +103,14 @@ const QColor DsoSignal::SignalColours[4] = { }; const float DsoSignal::EnvelopeThreshold = 256.0f; +const double DsoSignal::TrigMargin = 0.02; const int DsoSignal::UpMargin = 30; 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), @@ -120,21 +121,26 @@ DsoSignal::DsoSignal(boost::shared_ptr dev_inst, //_zeroPos(probe->index * 0.5 + 0.25) _trig_vpos(0.5), _zeroPos(0.5), + _zero_off(255/2.0), _autoV(false), - _autoH(false) + _autoH(false), + _hover_en(false), + _hover_index(0), + _hover_point(QPointF(0, 0)), + _hover_value(0) { - QVector vValue; + QVector vValue; QVector vUnit; - QVector hValue; + QVector hValue; QVector hUnit; - for(quint64 i = 0; i < vDialValueCount; i++) + for(uint64_t i = 0; i < vDialValueCount; i++) vValue.append(vDialValue[i]); - for(quint64 i = 0; i < vDialUnitCount; i++) + for(uint64_t i = 0; i < vDialUnitCount; i++) vUnit.append(vDialUnit[i]); - for(quint64 i = 0; i < hDialValueCount; i++) + for(uint64_t i = 0; i < hDialValueCount; i++) hValue.append(hDialValue[i]); - for(quint64 i = 0; i < hDialUnitCount; i++) + for(uint64_t i = 0; i < hDialUnitCount; i++) hUnit.append(hDialUnit[i]); _vDial = new dslDial(vDialValueCount, vDialValueStep, vValue, vUnit); @@ -173,7 +179,7 @@ DsoSignal::~DsoSignal() { } -shared_ptr DsoSignal::data() const +boost::shared_ptr DsoSignal::data() const { return _data; } @@ -244,10 +250,15 @@ void DsoSignal::set_vDialActive(bool active) bool DsoSignal::go_vDialPre() { if (enabled() && !_vDial->isMin()) { + const double pre_vdiv = _vDial->get_value(); _vDial->set_sel(_vDial->get_sel() - 1); _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, g_variant_new_uint64(_vDial->get_value())); + if (_view->session().get_capture_state() == SigSession::Stopped) + _scale *= pre_vdiv/_vDial->get_value(); update_zeroPos(); + _view->set_need_update(true); + _view->update(); return true; } else { _autoV = false; @@ -258,10 +269,15 @@ bool DsoSignal::go_vDialPre() bool DsoSignal::go_vDialNext() { if (enabled() && !_vDial->isMax()) { + const double pre_vdiv = _vDial->get_value(); _vDial->set_sel(_vDial->get_sel() + 1); _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, g_variant_new_uint64(_vDial->get_value())); + if (_view->session().get_capture_state() == SigSession::Stopped) + _scale *= pre_vdiv/_vDial->get_value(); update_zeroPos(); + _view->set_need_update(true); + _view->update(); return true; } else { _autoV = false; @@ -448,16 +464,16 @@ void DsoSignal::set_trig_vpos(int pos) assert(_view); int trig_value; if (enabled()) { - double delta = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0f / get_view_rect().height(); + double delta = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); if (isDSCope) { - _trig_vpos = delta; + _trig_vpos = min(max(delta, 0+TrigMargin), 1-TrigMargin); trig_value = delta * 255; } else { delta = delta - _zeroPos; delta = min(delta, 0.5); delta = max(delta, -0.5); - _trig_vpos = _zeroPos + delta; + _trig_vpos = min(max(_zeroPos + delta, 0+TrigMargin), 1-TrigMargin); trig_value = (delta * 255.0f + 0x80); } _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, @@ -475,18 +491,41 @@ void DsoSignal::set_zeroPos(int pos) if (enabled()) { double delta = _trig_vpos - _zeroPos; set_trig_vpos(get_trig_vpos() + pos - get_zeroPos()); - _zeroPos = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0f / get_view_rect().height(); - _trig_vpos = min(max(_zeroPos + delta, 0.0), 1.0); + _zeroPos = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); + _trig_vpos = min(max(_zeroPos + delta, 0+TrigMargin), 1-TrigMargin); update_zeroPos(); } } +void DsoSignal::set_factor(uint64_t factor) +{ + if (enabled()) { + GVariant* gvar; + uint64_t prefactor = 0; + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_FACTOR); + if (gvar != NULL) { + prefactor = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } else { + qDebug() << "ERROR: config_get SR_CONF_FACTOR failed."; + return; + } + if (prefactor != factor) { + _dev_inst->set_config(_probe, NULL, SR_CONF_FACTOR, + g_variant_new_uint64(factor)); + _vDial->set_factor(factor); + _view->set_need_update(true); + _view->update(); + } + } +} + void DsoSignal::update_zeroPos() { if (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0) { //double vpos_off = (0.5 - _zeroPos) * _vDial->get_value() * DS_CONF_DSO_VDIVS; - double vpos_off = (0.5 - (get_zeroPos() - UpMargin) * 1.0f/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; + double vpos_off = (0.5 - (get_zeroPos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, g_variant_new_double(vpos_off)); } @@ -505,7 +544,7 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) assert(_view); int i, j; - const int height = _view->viewport()->height() - UpMargin - DownMargin; + const int height = get_view_rect().height(); const int width = right - left; p.setPen(Qt::NoPen); @@ -517,7 +556,7 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) const uint64_t sample_len = _dev_inst->get_sample_limit(); const double samplerate = _dev_inst->get_sample_rate(); const double samples_per_pixel = samplerate * _view->scale(); - const double shown_rate = min(samples_per_pixel * width * 1.0f / sample_len, 1.0); + const double shown_rate = min(samples_per_pixel * width * 1.0 / sample_len, 1.0); const double start_time = _data->get_start_time(); const double start = samplerate * (_view->offset() - start_time); const double shown_offset = min(start / sample_len, 1.0) * width; @@ -538,7 +577,7 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) QPen pen(Signal::dsFore); pen.setStyle(Qt::DotLine); p.setPen(pen); - const double spanY =height * 1.0f / DS_CONF_DSO_VDIVS; + const double spanY =height * 1.0 / DS_CONF_DSO_VDIVS; for (i = 1; i <= DS_CONF_DSO_VDIVS; i++) { const double posY = spanY * i + UpMargin; p.drawLine(left, posY, right, posY); @@ -548,7 +587,7 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) width / 2.0f + 5, posY - miniSpanY * j); } } - const double spanX = width * 1.0f / DS_CONF_DSO_HDIVS; + const double spanX = width * 1.0 / DS_CONF_DSO_HDIVS; for (i = 1; i <= DS_CONF_DSO_HDIVS; i++) { const double posX = spanX * i; p.drawLine(posX, UpMargin, @@ -568,7 +607,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) assert(right >= left); if (enabled()) { - const int height = _view->viewport()->height() - UpMargin - DownMargin; + const int height = get_view_rect().height(); const int width = right - left; const int y = get_zeroPos() + height * 0.5; @@ -581,8 +620,9 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) if (snapshots.empty()) return; - _scale = height * 1.0f / 256; - const shared_ptr &snapshot = + if (_view->session().get_capture_state() == SigSession::Running) + _scale = height * 1.0f / 256; + const boost::shared_ptr &snapshot = snapshots.front(); const uint16_t number_channels = snapshot->get_channel_num(); @@ -647,9 +687,19 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) p.setBrush(hover ? _colour.dark() : _colour); p.drawPolygon(points, countof(points)); + // paint the trig voltage + int trigp = get_trig_vpos(); + float t_vol = (_zeroPos - _trig_vpos) * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS; + QString t_vol_s = (_vDial->get_value() >= 500) ? QString::number(t_vol/1000.0f, 'f', 2)+"V" : QString::number(t_vol, 'f', 2)+"mV"; + int vol_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, t_vol_s).width(); + const QRectF t_vol_rect = QRectF(right-vol_width, trigp-10, vol_width, 20); + p.setPen(Qt::white); + p.drawText(t_vol_rect, Qt::AlignRight | Qt::AlignVCenter, t_vol_s); + // paint the _trig_vpos line p.setPen(QPen(_colour, 1, Qt::DotLine)); - p.drawLine(left, get_trig_vpos(), right - label_rect.width()*1.5, get_trig_vpos()); + p.drawLine(left, trigp, right - p.boundingRect(t_vol_rect, Qt::AlignLeft, t_vol_s).width(), trigp); // Paint the text p.setPen(Qt::white); @@ -687,9 +737,10 @@ void DsoSignal::paint_trace(QPainter &p, float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); - float zeroP = 0; - if (strcmp(_dev_inst->dev_inst()->driver->name, "DSLogic") == 0) - zeroP = (_zeroPos - 0.5) * get_view_rect().height(); + float zeroP = _zeroPos * get_view_rect().height() + top;; + if (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0 && + _view->session().get_capture_state() == SigSession::Running) + _zero_off = _zeroPos * 255; float x = (start / samples_per_pixel - pixels_offset) + left; double pixels_per_sample = 1.0/samples_per_pixel; uint8_t offset; @@ -700,7 +751,7 @@ void DsoSignal::paint_trace(QPainter &p, //offset = samples[(sample - start)*num_channels]; offset = samples[sample]; - const float y = min(max(top, top + offset * _scale + zeroP), bottom); + const float y = min(max(top, zeroP + (offset - _zero_off) * _scale), bottom); *point++ = QPointF(x, y); x += pixels_per_sample; //*point++ = QPointF(x, top + offset); @@ -738,9 +789,10 @@ void DsoSignal::paint_envelope(QPainter &p, QRectF *rect = rects; float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); - float zeroP = 0; - if (strcmp(_dev_inst->dev_inst()->driver->name, "DSLogic") == 0) - zeroP = (_zeroPos - 0.5) * get_view_rect().height(); + float zeroP = _zeroPos * get_view_rect().height() + top; + if (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0 && + _view->session().get_capture_state() == SigSession::Running) + _zero_off = _zeroPos * 255; for(uint64_t sample = 0; sample < e.length-1; sample++) { const float x = ((e.scale * sample + e.start) / samples_per_pixel - pixels_offset) + left; @@ -749,8 +801,8 @@ void DsoSignal::paint_envelope(QPainter &p, // We overlap this sample with the next so that vertical // gaps do not appear during steep rising or falling edges - const float b = min(max(top, (top + max(s->max, (s+1)->min) * _scale + zeroP)), bottom); - const float t = min(max(top, (top + min(s->min, (s+1)->max) * _scale + zeroP)), bottom); + const float b = min(max(top, ((max(s->max, (s+1)->min) - _zero_off) * _scale + zeroP)), bottom); + const float t = min(max(top, ((min(s->min, (s+1)->max) - _zero_off) * _scale + zeroP)), bottom); float h = b - t; if(h >= 0.0f && h <= 1.0f) @@ -776,6 +828,9 @@ void DsoSignal::paint_type_options(QPainter &p, int right, bool hover, int actio { int y = get_y(); const QRectF vDial_rect = get_rect("vDial", y, right); + const QRectF x1_rect = get_rect("x1", y, right); + const QRectF x10_rect = get_rect("x10", y, right); + const QRectF x100_rect = get_rect("x100", y, right); const QRectF hDial_rect = get_rect("hDial", y, right); const QRectF acdc_rect = get_rect("acdc", y, right); const QRectF chEn_rect = get_rect("chEn", y, right); @@ -797,6 +852,31 @@ void DsoSignal::paint_type_options(QPainter &p, int right, bool hover, int actio p.setPen(Qt::white); p.drawText(acdc_rect, Qt::AlignCenter | Qt::AlignVCenter, (_acCoupling == SR_GND_COUPLING) ? "GND" : (_acCoupling == SR_DC_COUPLING) ? "DC" : "AC"); + + // paint the probe factor selector + GVariant* gvar; + uint64_t factor; + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_FACTOR); + if (gvar != NULL) { + factor = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } else { + qDebug() << "ERROR: config_get SR_CONF_FACTOR failed."; + return; + } + + p.setPen(Qt::white); + p.setBrush((enabled() && (factor == 100)) ? ((hover && action == X100) ? _colour.darker() : _colour) : ((hover && action == X100) ? _colour.darker() : dsDisable)); + p.drawRect(x100_rect); + p.drawText(x100_rect, Qt::AlignLeft | Qt::AlignVCenter, "x100"); + + p.setBrush((enabled() && (factor == 10)) ? ((hover && action == X10) ? _colour.darker() : _colour) : ((hover && action == X10) ? _colour.darker() : dsDisable)); + p.drawRect(x10_rect); + p.drawText(x10_rect, Qt::AlignLeft | Qt::AlignVCenter, "x10"); + + p.setBrush((enabled() && (factor == 1)) ? ((hover && action == X1) ? _colour.darker() : _colour) : ((hover && action == X1) ? _colour.darker() : dsDisable)); + p.drawRect(x1_rect); + p.drawText(x1_rect, Qt::AlignLeft | Qt::AlignVCenter, "x1"); } void DsoSignal::paint_measure(QPainter &p) @@ -808,26 +888,26 @@ void DsoSignal::paint_measure(QPainter &p) if (sr_status_get(_dev_inst->dev_inst(), &status, st_begin, st_end) == SR_OK) { _max = (index == 0) ? status.ch0_max : status.ch1_max; _min = (index == 0) ? status.ch0_min : status.ch1_min; - const uint32_t period = (index == 0) ? status.ch0_period : status.ch1_period; + const uint64_t period = (index == 0) ? status.ch0_period : status.ch1_period; const uint32_t count = (index == 0) ? status.ch0_pcnt : status.ch1_pcnt; - double value_max = ((0xff - _min - (1-_zeroPos)*0xff) * _vDial->get_value() * DS_CONF_DSO_VDIVS) / 0xff; - double value_min = ((0xff - _max - (1-_zeroPos)*0xff) * _vDial->get_value() * DS_CONF_DSO_VDIVS) / 0xff; - _period = (count == 0) ? period * 10 : period * 10.0f / count; + double value_max = (_zero_off - _min) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + double value_min = (_zero_off - _max) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + _period = (count == 0) ? period * 10.0 : period * 10.0 / count; const int channel_count = _view->session().get_ch_num(SR_CHANNEL_DSO); uint64_t sample_rate = _dev_inst->get_sample_rate(); - _period = _period * 200 / (channel_count * sample_rate * 1.0f/ SR_MHZ(1)); - QString max_string = abs(value_max) > 1000 ? QString::number(value_max/1000.0) + "V" : QString::number(value_max) + "mV"; - QString min_string = abs(value_min) > 1000 ? QString::number(value_min/1000.0) + "V" : QString::number(value_min) + "mV"; - QString period_string = abs(_period) > 1000000000 ? QString::number(_period/1000000000) + "S" : - abs(_period) > 1000000 ? QString::number(_period/1000000) + "mS" : - abs(_period) > 1000 ? QString::number(_period/1000) + "uS" : QString::number(_period) + "nS"; - QString freq_string = abs(_period) > 1000000 ? QString::number(1000000000/_period) + "Hz" : - abs(_period) > 1000 ? QString::number(1000000/_period) + "kHz" : QString::number(1000/_period) + "MHz"; + _period = _period * 200.0 / (channel_count * sample_rate * 1.0 / SR_MHZ(1)); + QString max_string = abs(value_max) > 1000 ? QString::number(value_max/1000.0, 'f', 2) + "V" : QString::number(value_max, 'f', 2) + "mV"; + QString min_string = abs(value_min) > 1000 ? QString::number(value_min/1000.0, 'f', 2) + "V" : QString::number(value_min, 'f', 2) + "mV"; + QString period_string = abs(_period) > 1000000000 ? QString::number(_period/1000000000, 'f', 2) + "S" : + abs(_period) > 1000000 ? QString::number(_period/1000000, 'f', 2) + "mS" : + abs(_period) > 1000 ? QString::number(_period/1000, 'f', 2) + "uS" : QString::number(_period, 'f', 2) + "nS"; + QString freq_string = abs(_period) > 1000000 ? QString::number(1000000000/_period, 'f', 2) + "Hz" : + abs(_period) > 1000 ? QString::number(1000000/_period, 'f', 2) + "kHz" : QString::number(1000/_period, 'f', 2) + "MHz"; p.setPen(_colour); - p.drawText(QRectF(0, 100*index + UpMargin, get_view_rect().width(), 20), Qt::AlignRight | Qt::AlignVCenter, "Max: "+max_string+" "); - p.drawText(QRectF(0, 100*index + UpMargin + 20, get_view_rect().width(), 20), Qt::AlignRight | Qt::AlignVCenter, "Min: "+min_string+" "); - p.drawText(QRectF(0, 100*index + UpMargin + 40, get_view_rect().width(), 20), Qt::AlignRight | Qt::AlignVCenter, "Period: "+period_string+" "); - p.drawText(QRectF(0, 100*index + UpMargin + 60, get_view_rect().width(), 20), Qt::AlignRight | Qt::AlignVCenter, "Frequency: "+freq_string+" "); + p.drawText(QRectF(0, 100*index + UpMargin, get_view_rect().width()*0.9, 20), Qt::AlignRight | Qt::AlignVCenter, "Max: "+max_string+" "); + p.drawText(QRectF(0, 100*index + UpMargin + 20, get_view_rect().width()*0.9, 20), Qt::AlignRight | Qt::AlignVCenter, "Min: "+min_string+" "); + p.drawText(QRectF(0, 100*index + UpMargin + 40, get_view_rect().width()*0.9, 20), Qt::AlignRight | Qt::AlignVCenter, "Period: "+period_string+" "); + p.drawText(QRectF(0, 100*index + UpMargin + 60, get_view_rect().width()*0.9, 20), Qt::AlignRight | Qt::AlignVCenter, "Frequency: "+freq_string+" "); if (_autoV) { const uint8_t vscale = abs(_max - _min); @@ -846,8 +926,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 +936,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; @@ -880,5 +960,78 @@ void DsoSignal::auto_set() } } +bool DsoSignal::measure(const QPointF &p) +{ + _hover_en = false; + if (!enabled()) + return false; + + const deque< boost::shared_ptr > &snapshots = + _data->get_snapshots(); + if (snapshots.empty()) + return false; + + const boost::shared_ptr &snapshot = + snapshots.front(); + if (snapshot->buf_null()) + return false; + + const double scale = _view->scale(); + assert(scale > 0); + const double offset = _view->offset(); + const double pixels_offset = offset / scale; + const double samplerate = _dev_inst->get_sample_rate(); + const double samples_per_pixel = samplerate * scale; + + _hover_index = floor((p.x() + pixels_offset) * samples_per_pixel+0.5); + if (_hover_index >= snapshot->get_sample_count()) + return false; + + uint64_t pre_index; + uint64_t nxt_index; + if (_hover_index > 0) + pre_index = _hover_index - 1; + else + pre_index = _hover_index; + if (_hover_index < snapshot->get_sample_count() - 1) + nxt_index = _hover_index + 1; + else + nxt_index = _hover_index; + const uint8_t pre_sample = *snapshot->get_samples(pre_index, pre_index, get_index()); + const uint8_t cur_sample = *snapshot->get_samples(_hover_index, _hover_index, get_index()); + const uint8_t nxt_sample = *snapshot->get_samples(nxt_index, nxt_index, get_index()); + + _hover_value = (_zero_off - cur_sample) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + + float top = get_view_rect().top(); + float bottom = get_view_rect().bottom(); + float zeroP = _zeroPos * get_view_rect().height() + top; + float pre_x = (pre_index / samples_per_pixel - pixels_offset); + const float pre_y = min(max(top, zeroP + (pre_sample - _zero_off)* _scale), bottom); + float x = (_hover_index / samples_per_pixel - pixels_offset); + const float y = min(max(top, zeroP + (cur_sample - _zero_off)* _scale), bottom); + float nxt_x = (nxt_index / samples_per_pixel - pixels_offset); + const float nxt_y = min(max(top, zeroP + (nxt_sample - _zero_off)* _scale), bottom); + const QRectF slope_rect = QRectF(QPointF(pre_x - 10, pre_y - 10), QPointF(nxt_x + 10, nxt_y + 10)); + if (abs(y-p.y()) < 20 || slope_rect.contains(p)) { + _hover_point = QPointF(x, y); + _hover_en = true; + return true; + } else { + return false; + } +} + +bool DsoSignal::get_hover(uint64_t &index, QPointF &p, double &value) +{ + if (_hover_en) { + index = _hover_index; + p = _hover_point; + value = _hover_value; + return true; + } + return false; +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h index f030f973..af474c91 100644 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -43,10 +43,11 @@ class DsoSignal : public Signal private: static const QColor SignalColours[4]; static const float EnvelopeThreshold; + static const double TrigMargin; static const int HitCursorMargin = 3; - static const quint64 vDialValueCount = 8; - static const quint64 vDialValueStep = 1000; + static const uint64_t vDialValueCount = 8; + static const uint64_t vDialValueStep = 1000; static const uint64_t vDialUnitCount = 2; static const uint64_t hDialValueCount = 28; static const uint64_t hDialValueStep = 1000; @@ -94,6 +95,13 @@ public: void set_acCoupling(uint8_t coupling); void set_trig_vpos(int pos); int get_trig_vpos() const; + void set_factor(uint64_t factor); + + /** + * + */ + bool measure(const QPointF &p); + bool get_hover(uint64_t &index, QPointF &p, double &value); /** * auto set the vertical and Horizontal scale @@ -171,12 +179,18 @@ private: double _trig_vpos; double _zeroPos; + float _zero_off; uint8_t _max; uint8_t _min; double _period; bool _autoV; bool _autoH; + + bool _hover_en; + uint64_t _hover_index; + QPointF _hover_point; + double _hover_value; }; } // namespace view 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..5bcc9ae0 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,24 +270,39 @@ 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); else dsoSig->set_acCoupling((dsoSig->get_acCoupling()+1)%3); } + } else if (action == Trace::X1 && mTrace) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(mTrace)) { + dsoSig->set_factor(1); + } + } else if (action == Trace::X10 && mTrace) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(mTrace)) { + dsoSig->set_factor(10); + } + } else if (action == Trace::X100 && mTrace) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(mTrace)) { + dsoSig->set_factor(100); + } } 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 +345,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 +422,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 df0bbb4e..bd95cc0a 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -76,6 +76,15 @@ LogicSignal::LogicSignal(boost::shared_ptr dev_inst, _colour = SignalColours[probe->index % countof(SignalColours)]; } +LogicSignal::LogicSignal(const Signal &s, + boost::shared_ptr data, + const sr_channel * const probe) : + Signal(s, probe), + _data(data) +{ + assert(probe->index >= 0); +} + LogicSignal::~LogicSignal() { } @@ -85,12 +94,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; } @@ -273,5 +282,48 @@ void LogicSignal::paint_type_options(QPainter &p, int right, bool hover, int act edgeTrig_rect.right() - 5, edgeTrig_rect.bottom() - 5); } +bool LogicSignal::measure(const QPointF &p, uint64_t &index0, uint64_t &index1, uint64_t &index2) const +{ + const float gap = abs(p.y() - get_y()); + if (gap < get_signalHeight() * 0.5) { + const deque< boost::shared_ptr > &snapshots = + _data->get_snapshots(); + if (snapshots.empty()) + return false; + + const boost::shared_ptr &snapshot = + snapshots.front(); + if (snapshot->buf_null()) + return false; + + uint64_t index = _data->samplerate() * (_view->offset() - _data->get_start_time() + p.x() * _view->scale()); + if (index == 0 || index >= (snapshot->get_sample_count() - 1)) + return false; + + const uint64_t sig_mask = 1ULL << get_index(); + bool sample = snapshot->get_sample(index) & sig_mask; + index--; + if (!snapshot->get_pre_edge(index, sample, 1, get_index())) + return false; + + index0 = index; + sample = snapshot->get_sample(index) & sig_mask; + index++; + if (!snapshot->get_nxt_edge(index, sample, snapshot->get_sample_count(), 1, get_index())) + return false; + + index1 = index; + sample = snapshot->get_sample(index) & sig_mask; + index++; + if (!snapshot->get_nxt_edge(index, sample, snapshot->get_sample_count(), 1, get_index())) + index2 = 0; + else + index2 = index; + + return true; + } + return false; +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/logicsignal.h b/DSView/pv/view/logicsignal.h index 47b46431..9a03464f 100644 --- a/DSView/pv/view/logicsignal.h +++ b/DSView/pv/view/logicsignal.h @@ -58,6 +58,10 @@ public: boost::shared_ptr data, const sr_channel * const probe); + LogicSignal(const Signal &s, + boost::shared_ptr data, + const sr_channel * const probe); + virtual ~LogicSignal(); const sr_channel* probe() const; @@ -76,6 +80,8 @@ public: const std::vector< std::pair > cur_edges() const; + bool measure(const QPointF &p, uint64_t &index0, uint64_t &index1, uint64_t &index2) const; + protected: void paint_type_options(QPainter &p, int right, bool hover, int action); diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index a96a8561..0a3b8015 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -102,9 +102,8 @@ QString Ruler::format_freq(double period, unsigned precision) } else { const int order = ceil(log10f(period)); assert(order >= FirstSIPrefixPower); - const unsigned int prefix = ceil((order - FirstSIPrefixPower) / 3.0f); - const double multiplier = pow(10.0, - static_cast(- prefix * 3 - FirstSIPrefixPower)); + const int prefix = ceil((order - FirstSIPrefixPower) / 3.0f); + const double multiplier = pow(10.0, max(-prefix * 3.0 - FirstSIPrefixPower, 0.0)); QString s; QTextStream ts(&s); @@ -115,11 +114,10 @@ QString Ruler::format_freq(double period, unsigned precision) } } -QString Ruler::format_time(double t, unsigned int prefix, +QString Ruler::format_time(double t, int prefix, unsigned int precision) { - const double multiplier = pow(10.0, - static_cast(- prefix * 3 - FirstSIPrefixPower + 6)); + const double multiplier = pow(10.0, -prefix * 3 - FirstSIPrefixPower + 6.0); QString s; QTextStream ts(&s); @@ -134,6 +132,29 @@ QString Ruler::format_time(double t) return format_time(t, _cur_prefix); } +QString Ruler::format_real_time(uint64_t delta_index, uint64_t sample_rate) +{ + uint64_t delta_time = std::pow(10, 12) / sample_rate * delta_index; + + if (delta_time == 0) + return "0"; + + int zero = 0; + int prefix = (int)floor(log10(delta_time)); + while(delta_time == (delta_time/10*10)) { + delta_time /= 10; + zero++; + } + + return format_time(delta_time / std::pow(10.0, 12-zero), prefix/3+1, prefix/3*3 > zero ? prefix/3*3 - zero : 0); +} + +QString Ruler::format_real_freq(uint64_t delta_index, uint64_t sample_rate) +{ + const double delta_period = delta_index * 1.0 / sample_rate; + return format_freq(delta_period); +} + TimeMarker* Ruler::get_grabbed_cursor() { return _grabbed_marker; @@ -182,8 +203,8 @@ void Ruler::mouseMoveEvent(QMouseEvent *e) (void)e; if (_grabbed_marker) { - _grabbed_marker->set_time(_view.offset() + - _view.hover_point().x() * _view.scale()); + _grabbed_marker->set_index((_view.offset() + + _view.hover_point().x() * _view.scale()) * _view.session().get_device()->get_sample_rate()); } update(); @@ -242,19 +263,17 @@ void Ruler::mouseReleaseEvent(QMouseEvent *event) _cursor_sel_visible = true; } else { int overCursor; - double time = _view.offset() + (_cursor_sel_x + 0.5) * _view.scale(); + uint64_t index = (_view.offset() + (_cursor_sel_x + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); overCursor = in_cursor_sel_rect(event->pos()); if (overCursor == 0) { - //Cursor *newCursor = new Cursor(_view, CursorColor[_view.get_cursorList().size() % 8], time); - //_view.get_cursorList().push_back(newCursor); - _view.add_cursor(CursorColor[_view.get_cursorList().size() % 8], time); + _view.add_cursor(CursorColor[_view.get_cursorList().size() % 8], index); _view.show_cursors(true); addCursor = true; } else if (overCursor > 0) { list::iterator i = _view.get_cursorList().begin(); while (--overCursor != 0) i++; - (*i)->set_time(time); + (*i)->set_index(index); } _cursor_sel_visible = false; } @@ -262,10 +281,6 @@ void Ruler::mouseReleaseEvent(QMouseEvent *event) int overCursor; overCursor = in_cursor_sel_rect(event->pos()); if (overCursor > 0) { -// list::iterator i = _view.get_cursorList().begin(); -// while (--overCursor != 0) -// i++; -// _view.set_scale_offset(_view.scale(), (*i)->time() - _view.scale() * _view.viewport()->width() / 2); _view.set_cursor_middle(overCursor - 1); } @@ -307,8 +322,8 @@ void Ruler::draw_tick_mark(QPainter &p) { using namespace Qt; - const double SpacingIncrement = 32.0f; - const double MinValueSpacing = 16.0f; + const double SpacingIncrement = 32.0; + const double MinValueSpacing = 16.0; const int ValueMargin = 15; double min_width = SpacingIncrement, typical_width; @@ -406,11 +421,11 @@ void Ruler::draw_logic_tick_mark(QPainter &p) { using namespace Qt; - const double SpacingIncrement = 32.0f; - const double MinValueSpacing = 16.0f; + const double SpacingIncrement = 32.0; + const double MinValueSpacing = 16.0; const int ValueMargin = 5; - const double abs_min_period = 10.0f / _view.session().get_device()->get_sample_rate(); + const double abs_min_period = 10.0 / _view.session().get_device()->get_sample_rate(); double min_width = SpacingIncrement; double typical_width; @@ -488,13 +503,13 @@ void Ruler::draw_logic_tick_mark(QPainter &p) else { // Draw a minor tick - if (minor_tick_period / _view.scale() > 2 * typical_width || - tick_period / _view.scale() > _view.get_view_width()) + if (minor_tick_period / _view.scale() > 2 * typical_width) p.drawText(x, 2 * ValueMargin, 0, text_height, AlignCenter | AlignTop | TextDontClip, format_time(t, prefix)); //else if ((tick_period / _view.scale() > width() / 4) && (minor_tick_period / _view.scale() > inc_text_width)) - else if (minor_tick_period / _view.scale() > 1.1 * inc_text_width) + else if (minor_tick_period / _view.scale() > 1.1 * inc_text_width || + tick_period / _view.scale() > _view.get_view_width()) p.drawText(x, 2 * ValueMargin, 0, minor_tick_y1 + ValueMargin, AlignCenter | AlignTop | TextDontClip, format_time(t - major_t, minor_prefix)); diff --git a/DSView/pv/view/ruler.h b/DSView/pv/view/ruler.h index 114ff9ff..c22d8cce 100644 --- a/DSView/pv/view/ruler.h +++ b/DSView/pv/view/ruler.h @@ -25,6 +25,7 @@ #define DSVIEW_PV_VIEW_RULER_H #include +#include namespace pv { namespace view { @@ -62,10 +63,12 @@ public: public: Ruler(View &parent); - static QString format_time(double t, unsigned int prefix, + static QString format_time(double t, int prefix, unsigned precision = pricision); static QString format_freq(double period, unsigned precision = pricision); QString format_time(double t); + static QString format_real_time(uint64_t delta_index, uint64_t sample_rate); + static QString format_real_freq(uint64_t delta_index, uint64_t sample_rate); TimeMarker* get_grabbed_cursor(); void set_grabbed_cursor(TimeMarker* grabbed_marker); diff --git a/DSView/pv/view/signal.cpp b/DSView/pv/view/signal.cpp index 4c9c09a5..e4cba490 100644 --- a/DSView/pv/view/signal.cpp +++ b/DSView/pv/view/signal.cpp @@ -42,6 +42,13 @@ Signal::Signal(boost::shared_ptr dev_inst, { } +Signal::Signal(const Signal &s, const sr_channel * const probe) : + Trace((const Trace &)s), + _dev_inst(s._dev_inst), + _probe(probe) +{ +} + bool Signal::enabled() const { return _probe->enabled; diff --git a/DSView/pv/view/signal.h b/DSView/pv/view/signal.h index b599a07c..3ad90f44 100644 --- a/DSView/pv/view/signal.h +++ b/DSView/pv/view/signal.h @@ -59,6 +59,11 @@ protected: Signal(boost::shared_ptr dev_inst, const sr_channel * const probe, int type); + /** + * Copy constructor + */ + Signal(const Signal &s, const sr_channel * const probe); + public: virtual boost::shared_ptr data() const = 0; diff --git a/DSView/pv/view/timemarker.cpp b/DSView/pv/view/timemarker.cpp index 13856d9c..34a0e7d7 100644 --- a/DSView/pv/view/timemarker.cpp +++ b/DSView/pv/view/timemarker.cpp @@ -24,6 +24,7 @@ #include "timemarker.h" #include "view.h" +#include "../device/device.h" #include @@ -31,10 +32,10 @@ namespace pv { namespace view { TimeMarker::TimeMarker(View &view, QColor &colour, - double time) : + uint64_t index) : _view(view), - _time(time), - _grabbed(false), + _index(index), + _grabbed(false), _colour(colour) { } @@ -42,7 +43,7 @@ TimeMarker::TimeMarker(View &view, QColor &colour, TimeMarker::TimeMarker(const TimeMarker &s) : QObject(), _view(s._view), - _time(s._time), + _index(s._index), _colour(s._colour) { } @@ -56,20 +57,21 @@ void TimeMarker::set_grabbed(bool grabbed) _grabbed = grabbed; } -double TimeMarker::time() const +uint64_t TimeMarker::index() const { - return _time; + return _index; } -void TimeMarker::set_time(double time) +void TimeMarker::set_index(uint64_t index) { - _time = time; - time_changed(); + _index = index; + time_changed(); } void TimeMarker::paint(QPainter &p, const QRect &rect, const bool highlight) { - const float x = (_time - _view.offset()) / _view.scale(); + const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + const double x = _index/samples_per_pixel - (_view.offset() / _view.scale()); p.setPen((_grabbed | highlight) ? QPen(_colour.lighter(), 2, Qt::DashLine) : QPen(_colour, 1, Qt::DashLine)); p.drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom())); } diff --git a/DSView/pv/view/timemarker.h b/DSView/pv/view/timemarker.h index ad0ca7e6..b6de934f 100644 --- a/DSView/pv/view/timemarker.h +++ b/DSView/pv/view/timemarker.h @@ -28,6 +28,8 @@ #include #include +#include + class QPainter; class QRect; @@ -47,7 +49,7 @@ protected: * @param colour A reference to the colour of this cursor. * @param time The time to set the flag to. */ - TimeMarker(View &view, QColor &colour, double time); + TimeMarker(View &view, QColor &colour, uint64_t index); /** * Copy constructor @@ -59,11 +61,12 @@ public: * Gets the time of the marker. */ double time() const; + uint64_t index() const; /** * Sets the time of the marker. */ - void set_time(double time); + void set_index(uint64_t index); /* * @@ -103,7 +106,7 @@ signals: protected: View &_view; - double _time; + uint64_t _index; QSizeF _text_size; diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index 515932d4..e234a723 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -53,19 +53,9 @@ const QPen Trace::SignalAxisPen = QColor(128, 128, 128, 64); const QPen Trace::AxisPen(QColor(128, 128, 128, 64)); const int Trace::LabelHitPadding = 2; -Trace::Trace(QString name, int type) : - _name(name), - _v_offset(0), - _type(type), - _sec_index(0), - _signalHeight(30), - _trig(0) -{ -} - Trace::Trace(QString name, int index, int type) : _name(name), - _v_offset(0), + _v_offset(INT_MAX), _type(type), _sec_index(0), _signalHeight(30), @@ -76,7 +66,7 @@ Trace::Trace(QString name, int index, int type) : Trace::Trace(QString name, std::list index_list, int type, int sec_index) : _name(name), - _v_offset(0), + _v_offset(INT_MAX), _type(type), _index_list(index_list), _sec_index(sec_index), @@ -85,6 +75,21 @@ Trace::Trace(QString name, std::list index_list, int type, int sec_index) : { } +Trace::Trace(const Trace &t) : + _view(t._view), + _name(t._name), + _colour(t._colour), + _v_offset(t._v_offset), + _type(t._type), + _index_list(t._index_list), + _sec_index(t._sec_index), + _old_v_offset(t._old_v_offset), + _signalHeight(t._signalHeight), + _trig(t._trig), + _text_size(t._text_size) +{ +} + QString Trace::get_name() const { return _name; @@ -300,6 +305,9 @@ int Trace::pt_in_rect(int y, int right, const QPoint &point) const QRectF edgeTrig = get_rect("edgeTrig", y, right); const QRectF label = get_rect("label", get_zeroPos(), right); const QRectF vDial = get_rect("vDial", y, right); + const QRectF x1 = get_rect("x1", y, right); + const QRectF x10 = get_rect("x10", y, right); + const QRectF x100 = get_rect("x100", y, right); const QRectF hDial = get_rect("hDial", y, right); const QRectF chEn = get_rect("chEn", y, right); const QRectF acdc = get_rect("acdc", y, right); @@ -323,6 +331,12 @@ int Trace::pt_in_rect(int y, int right, const QPoint &point) return LABEL; else if (vDial.contains(point) && _type == DS_DSO && enabled()) return VDIAL; + else if (x1.contains(point) && _type == DS_DSO && enabled()) + return X1; + else if (x10.contains(point) && _type == DS_DSO && enabled()) + return X10; + else if (x100.contains(point) && _type == DS_DSO && enabled()) + return X100; else if (hDial.contains(point) && _type == DS_DSO && enabled()) return HDIAL; else if (chEn.contains(point) && _type == DS_DSO) @@ -400,6 +414,21 @@ QRectF Trace::get_rect(const char *s, int y, int right) get_leftWidth() + name_size.width() + SquareWidth*0.5 + Margin, y - SquareWidth * SquareNum, SquareWidth * (SquareNum-1), SquareWidth * (SquareNum-1)); + else if (!strcmp(s, "x1")) + return QRectF( + get_leftWidth() + name_size.width() + SquareWidth*0.5 + Margin - 45, + y - SquareWidth - SquareWidth * (SquareNum-1) * 0.85, + SquareWidth * 1.75, SquareWidth); + else if (!strcmp(s, "x10")) + return QRectF( + get_leftWidth() + name_size.width() + SquareWidth*0.5 + Margin - 45, + y - SquareWidth - SquareWidth * (SquareNum-1) * 0.55, + SquareWidth * 1.75, SquareWidth); + else if (!strcmp(s, "x100")) + return QRectF( + get_leftWidth() + name_size.width() + SquareWidth*0.5 + Margin - 45, + y - SquareWidth - SquareWidth * (SquareNum-1) * 0.25, + SquareWidth * 1.75, SquareWidth); else if (!strcmp(s, "hDial")) return QRectF( get_leftWidth() + name_size.width() + SquareWidth*0.5 + Margin, diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index 597818dc..031f94a7 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -66,6 +66,9 @@ public: static const int CHEN = 11; static const int ACDC = 12; static const int DSOTRIG = 13; + static const int X1 = 14; + static const int X10 = 15; + static const int X100 = 16; static const QColor dsBlue; static const QColor dsYellow; @@ -81,12 +84,16 @@ public: static const QPen SignalAxisPen; protected: - Trace(QString name, int type); Trace(QString name, int index, int type); Trace(QString name, std::list index_list, int type, int sec_index); + /** + * Copy constructor + */ + Trace(const Trace &t); + public: - enum {DS_LOGIC = 0, DS_ANALOG, DS_GROUP, DS_DSO, DS_DECODER}; + enum {DS_LOGIC = 0, DS_ANALOG, DS_DSO, DS_GROUP, DS_DECODER}; public: /** diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index 1a912d40..ef1e2f69 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -27,11 +27,12 @@ #include -#include +#include #include #include #include +#include "groupsignal.h" #include "decodetrace.h" #include "header.h" #include "devmode.h" @@ -96,12 +97,10 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget setViewportMargins(headerWidth(), RulerHeight, 0, 0); setViewport(_viewport); - connect(&_session, SIGNAL(signals_changed()), - this, SLOT(signals_changed())); - connect(&_session, SIGNAL(data_updated()), - this, SLOT(data_updated())); - connect(&_session, SIGNAL(receive_data(quint64)), - this, SLOT(receive_data(quint64))); + connect(&_session, SIGNAL(signals_changed()), + this, SLOT(signals_changed())); + connect(&_session, SIGNAL(data_updated()), + this, SLOT(data_updated())); connect(&_session, SIGNAL(receive_trigger(quint64)), this, SLOT(set_trig_pos(quint64))); @@ -211,10 +210,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,34 +272,39 @@ 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()); + const vector< boost::shared_ptr > groups(_session.get_group_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( - sigs.size() + decode_sigs.size()); + vector< boost::shared_ptr > traces( + sigs.size() + groups.size() + decode_sigs.size()); #else - vector< shared_ptr > traces(sigs.size()); + vector< boost::shared_ptr > traces(sigs.size() + groups.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); #endif + i = copy(groups.begin(), groups.end(), i); stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets); 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); - return a->get_v_offset() < b->get_v_offset(); + if (a->get_type() != b->get_type()) + return a->get_type() > b->get_type(); + else + return a->get_v_offset() < b->get_v_offset(); } bool View::cursors_shown() const @@ -341,9 +345,9 @@ void View::show_search_cursor(bool show) void View::set_trig_pos(quint64 trig_pos) { - const double time = trig_pos * 1.0f / _session.get_device()->get_sample_rate(); + const double time = trig_pos * 1.0 / _session.get_device()->get_sample_rate(); _trig_pos = trig_pos; - _trig_cursor->set_time(time); + _trig_cursor->set_index(trig_pos); _show_trig_cursor = true; set_scale_offset(_scale, time - _scale * get_view_width() / 2); _ruler->update(); @@ -354,9 +358,9 @@ void View::set_search_pos(uint64_t search_pos) { //assert(search_pos >= 0); - const double time = search_pos * 1.0f / _session.get_device()->get_sample_rate(); + const double time = search_pos * 1.0 / _session.get_device()->get_sample_rate(); _search_pos = search_pos; - _search_cursor->set_time(time); + _search_cursor->set_index(search_pos); set_scale_offset(_scale, time - _scale * get_view_width() / 2); _ruler->update(); _viewport->update(); @@ -379,14 +383,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 +410,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; @@ -452,20 +456,19 @@ void View::update_scale() assert(sample_rate > 0); if (_session.get_device()->dev_inst()->mode != DSO) { - _scale = (1.0f / sample_rate) / WellPixelsPerSample; + _scale = (1.0 / sample_rate) / WellPixelsPerSample; _maxscale = _session.get_device()->get_sample_time() / (get_view_width() * MaxViewRate); } else { - _scale = _session.get_device()->get_time_base() * 10.0f / get_view_width() * std::pow(10.0, -9.0); + _scale = _session.get_device()->get_time_base() * 10.0 / get_view_width() * std::pow(10.0, -9.0); _maxscale = 1e9; } - _minscale = (1.0f / sample_rate) / MaxPixelsPerSample; + _minscale = (1.0 / sample_rate) / MaxPixelsPerSample; _offset = 0; _preScale = _scale; _preOffset = _offset; - const double time = _trig_pos * 1.0f / sample_rate; - _trig_cursor->set_time(time); + _trig_cursor->set_index(_trig_pos); _ruler->update(); _viewport->update(); @@ -474,8 +477,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) || @@ -490,7 +493,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); @@ -551,16 +554,16 @@ bool View::viewportEvent(QEvent *e) int View::headerWidth() { int headerWidth; - int maxNameWidth = 0; + int maxNameWidth = 25; int maxLeftWidth = 0; int maxRightWidth = 0; 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); @@ -581,7 +584,11 @@ void View::resizeEvent(QResizeEvent*) if (_session.get_device()->dev_inst()->mode == DSO) _scale = _session.get_device()->get_time_base() * std::pow(10.0, -9.0) * DS_CONF_DSO_HDIVS / get_view_width(); - _maxscale = _session.get_device()->get_sample_time() / (get_view_width() * MaxViewRate); + if (_session.get_device()->dev_inst()->mode != DSO) + _maxscale = _session.get_device()->get_sample_time() / (get_view_width() * MaxViewRate); + else + _maxscale = 1e9; + _scale = min(_scale, _maxscale); signals_changed(); @@ -697,9 +704,9 @@ Ruler* View::get_ruler() return _ruler; } -void View::add_cursor(QColor color, double time) +void View::add_cursor(QColor color, uint64_t index) { - Cursor *newCursor = new Cursor(*this, color, time); + Cursor *newCursor = new Cursor(*this, color, index); _cursorList.push_back(newCursor); cursor_update(); } @@ -720,32 +727,17 @@ void View::set_cursor_middle(int index) list::iterator i = _cursorList.begin(); while (index-- != 0) i++; - set_scale_offset(_scale, (*i)->time() - _scale * get_view_width() / 2); + set_scale_offset(_scale, (*i)->index() * 1.0 / _session.get_device()->get_sample_rate() - _scale * get_view_width() / 2); } -void View::receive_data(quint64 length) +Viewport * View::get_viewport() { - _viewport->set_receive_len(length); -} - -QString View::get_mm_width() -{ - return _viewport->get_mm_width(); -} - -QString View::get_mm_period() -{ - return _viewport->get_mm_period(); -} - -QString View::get_mm_freq() -{ - return _viewport->get_mm_freq(); + return _viewport; } QString View::get_cm_time(int index) { - return _ruler->format_time(get_cursor_time(index)); + return _ruler->format_real_time(get_cursor_samples(index), _session.get_device()->get_sample_rate()); } QString View::get_cm_delta(int index1, int index2) @@ -753,11 +745,13 @@ QString View::get_cm_delta(int index1, int index2) if (index1 == index2) return "0"; - return _ruler->format_time(abs(get_cursor_time(index1) - - get_cursor_time(index2))); + uint64_t samples1 = get_cursor_samples(index1); + uint64_t samples2 = get_cursor_samples(index2); + uint64_t delta_sample = (samples1 > samples2) ? samples1 - samples2 : samples2 - samples1; + return _ruler->format_real_time(delta_sample, _session.get_device()->get_sample_rate()); } -double View::get_cursor_time(int index) +uint64_t View::get_cursor_samples(int index) { assert(index < (int)_cursorList.size()); @@ -765,25 +759,12 @@ double View::get_cursor_time(int index) for (list::iterator i = _cursorList.begin(); i != _cursorList.end(); i++) { if (index == curIndex) { - return (*i)->time(); + return (*i)->index(); } curIndex++; } } -uint64_t View::get_cursor_samples(int index) -{ - const double time = get_cursor_time(index); - const uint64_t sample_rate = _session.get_device()->get_sample_limit(); - assert(sample_rate !=0); - - return time*sample_rate; -} - -void View::on_mouse_moved() -{ - mouse_moved(); -} void View::on_cursor_moved() { cursor_moved(); @@ -804,8 +785,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 2f82a01e..81bffcb2 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -74,9 +74,9 @@ public: static const QSizeF LabelPadding; - static const int WellPixelsPerSample = 10.0f; - static const double MaxViewRate = 1.0f; - static const int MaxPixelsPerSample = 100.0f; + static const int WellPixelsPerSample = 10; + static constexpr double MaxViewRate = 1.0; + static const int MaxPixelsPerSample = 100; public: explicit View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent = 0); @@ -140,7 +140,7 @@ public: * cursorList */ std::list& get_cursorList(); - void add_cursor(QColor color, double time); + void add_cursor(QColor color, uint64_t index); void del_cursor(Cursor* cursor); void set_cursor_middle(int index); @@ -163,13 +163,10 @@ public: bool need_update() const; uint64_t get_cursor_samples(int index); - QString get_mm_width(); - QString get_mm_period(); - QString get_mm_freq(); + Viewport * get_viewport(); QString get_cm_time(int index); QString get_cm_delta(int index1, int index2); - void on_mouse_moved(); void on_cursor_moved(); void on_state_changed(bool stop); @@ -189,7 +186,6 @@ signals: void cursor_update(); - void mouse_moved(); void cursor_moved(); void mode_changed(); @@ -201,8 +197,6 @@ private: void update_margins(); - double get_cursor_time(int index); - static bool compare_trace_v_offsets( const boost::shared_ptr &a, const boost::shared_ptr &b); @@ -231,8 +225,6 @@ private slots: void header_updated(); - void receive_data(quint64 length); - void set_trig_pos(quint64 trig_pos); private: diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index c8aa4299..87737fc2 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -27,6 +27,7 @@ #include "signal.h" #include "dsosignal.h" +#include "logicsignal.h" #include "../device/devinst.h" #include "../data/logic.h" #include "../data/logicsnapshot.h" @@ -35,6 +36,8 @@ #include #include +#include + #include using namespace boost; @@ -49,11 +52,14 @@ Viewport::Viewport(View &parent) : _total_receive_len(0), _zoom_rect_visible(false), _measure_shown(false), + _measure_type(LOGIC), _cur_sample(0), _nxt_sample(1), _cur_preX(0), _cur_aftX(1), - _cur_midY(0) + _cur_midY(0), + _hover_index(0), + _hover_hit(false) { setMouseTracking(true); setAutoFillBackground(true); @@ -63,6 +69,7 @@ Viewport::Viewport(View &parent) : _mm_width = "#####"; _mm_period = "#####"; _mm_freq = "#####"; + _mm_duty = "#####"; _measure_en = true; triggered = false; timer_cnt = 0; @@ -71,14 +78,17 @@ Viewport::Viewport(View &parent) : this, SLOT(on_traces_moved())); connect(&trigger_timer, SIGNAL(timeout()), this, SLOT(on_trigger_timer())); + + connect(&_view.session(), &SigSession::receive_data, + this, &Viewport::set_receive_len); } 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()); } @@ -103,14 +113,14 @@ 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()); } - p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::Antialiasing, false); if (_view.session().get_device()->dev_inst()->mode == LOGIC || _view.session().get_instant()) { switch(_view.session().get_capture_state()) { @@ -122,22 +132,23 @@ void Viewport::paintEvent(QPaintEvent *event) break; case SigSession::Running: - //p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::Antialiasing); paintProgress(p); + p.setRenderHint(QPainter::Antialiasing, false); break; } } else { paintSignals(p); } - BOOST_FOREACH(const shared_ptr t, traces) + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (t->enabled()) t->paint_fore(p, 0, _view.get_view_width()); } - p.setRenderHint(QPainter::Antialiasing, false); + //p.setRenderHint(QPainter::Antialiasing, false); if (_view.get_signalHeight() != _curSignalHeight) _curSignalHeight = _view.get_signalHeight(); @@ -146,7 +157,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 || @@ -159,8 +170,8 @@ void Viewport::paintSignals(QPainter &p) pixmap.fill(Qt::transparent); QPainter dbp(&pixmap); dbp.initFrom(this); - p.setRenderHint(QPainter::Antialiasing, false); - BOOST_FOREACH(const shared_ptr t, traces) + //p.setRenderHint(QPainter::Antialiasing, false); + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (t->enabled()) @@ -175,8 +186,9 @@ void Viewport::paintSignals(QPainter &p) if (_view.cursors_shown()) { list::iterator i = _view.get_cursorList().begin(); double cursorX; + const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); while (i != _view.get_cursorList().end()) { - cursorX = ((*i)->time() - _view.offset()) / _view.scale(); + cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); if (rect().contains(_view.hover_point().x(), _view.hover_point().y()) && qAbs(cursorX - _view.hover_point().x()) <= HitCursorMargin) (*i)->paint(p, rect(), 1); @@ -209,8 +221,8 @@ void Viewport::paintProgress(QPainter &p) { using pv::view::Signal; - const quint64 _total_sample_len = _view.session().get_device()->get_sample_limit(); - double progress = -(_total_receive_len * 1.0f / _total_sample_len * 360 * 16); + const uint64_t _total_sample_len = _view.session().get_device()->get_sample_limit(); + double progress = -(_total_receive_len * 1.0 / _total_sample_len * 360 * 16); int captured_progress = 0; p.setPen(Qt::gray); @@ -297,7 +309,10 @@ void Viewport::paintProgress(QPainter &p) (status.captured_cnt1 << 8) + (status.captured_cnt2 << 16) + (status.captured_cnt3 << 24)); - captured_progress = captured_cnt * 100.0 / _total_sample_len; + if (triggred) + captured_progress = (_total_sample_len - captured_cnt) * 100.0 / _total_sample_len; + else + captured_progress = captured_cnt * 100.0 / _total_sample_len; p.setPen(Trace::dsLightBlue); @@ -338,13 +353,15 @@ void Viewport::mousePressEvent(QMouseEvent *event) _mouse_down_point = event->pos(); _mouse_down_offset = _view.offset(); + _measure_shown = false; if (event->buttons() & Qt::LeftButton) { if (_view.cursors_shown()) { list::iterator i = _view.get_cursorList().begin(); double cursorX; + const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); while (i != _view.get_cursorList().end()) { - cursorX = ((*i)->time() - _view.offset()) / _view.scale(); + cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); if ((*i)->grabbed()) _view.get_ruler()->rel_grabbed_cursor(); else if (qAbs(cursorX - event->pos().x()) <= HitCursorMargin) { @@ -355,14 +372,11 @@ void Viewport::mousePressEvent(QMouseEvent *event) } } -// if (!_view.get_ruler()->get_grabbed_cursor()) { -// _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; @@ -378,6 +392,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) { assert(event); _mouse_point = event->pos(); + _hover_hit = false; if (event->buttons() & Qt::RightButton) { _zoom_rect = QRectF(_mouse_down_point, event->pos()); _zoom_rect_visible = true; @@ -385,7 +400,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 { @@ -403,13 +418,11 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) if (_view.cursors_shown() && grabbed_marker) { const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); const double pos = cur_time * sample_rate; - const double pos_delta = pos - (int)pos; - if ( pos_delta < HitCursorTimeMargin) - grabbed_marker->set_time(1.0 / sample_rate * floor(pos)); - else if (pos_delta > (1.0 - HitCursorTimeMargin)) - grabbed_marker->set_time(1.0 / sample_rate * ceil(pos)); + const double pos_delta = pos - (uint64_t)pos; + if ( pos_delta < 0.5) + grabbed_marker->set_index((uint64_t)floor(pos)); else - grabbed_marker->set_time(cur_time); + grabbed_marker->set_index((uint64_t)ceil(pos)); } measure(); } @@ -424,7 +437,7 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) if (_zoom_rect_visible) { _zoom_rect_visible = false; const double newOffset = _view.offset() + (min(event->pos().x(), _mouse_down_point.x()) + 0.5) * _view.scale(); - const double newScale = max(min(_view.scale() * (event->pos().x() - _mouse_down_point.x()) / _view.get_view_width(), + const double newScale = max(min(_view.scale() * abs(event->pos().x() - _mouse_down_point.x()) / _view.get_view_width(), _view.get_maxscale()), _view.get_minscale()); if (newScale != _view.scale()) _view.set_scale_offset(newScale, newOffset); @@ -433,6 +446,13 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) if(_drag_sig) _drag_sig.reset(); + + if (_hover_hit){ + _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], _hover_index); + _view.show_cursors(true); + _hover_hit = false; + } + update(); } @@ -457,7 +477,7 @@ void Viewport::wheelEvent(QWheelEvent *event) if (event->orientation() == Qt::Vertical) { // Vertical scrolling is interpreted as zooming in/out - const double offset = (_view.session().get_capture_state() == SigSession::Running) ? 0 : event->x(); + const double offset = event->x(); _view.zoom(event->delta() / 80, offset); } else if (event->orientation() == Qt::Horizontal) { // Horizontal scrolling is interpreted as moving left/right @@ -499,156 +519,178 @@ void Viewport::set_receive_len(quint64 length) void Viewport::measure() { - uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + if (_view.session().get_capture_state() == SigSession::Running) + return; + _measure_shown = false; + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + boost::shared_ptr logicSig; + boost::shared_ptr dsoSig; + if (logicSig = dynamic_pointer_cast(s)) { + if (logicSig->measure(_view.hover_point(), _cur_sample, _nxt_sample, _thd_sample)) { + _measure_shown = true; + _measure_type = LOGIC; - const vector< boost::shared_ptr > sigs(_view.session().get_signals()); - BOOST_FOREACH(const boost::shared_ptr s, sigs) { - assert(s); - const int curY = _view.hover_point().y(); - const double curX = _view.hover_point().x(); - if (curY <= View::SignalMargin || s->get_type() != Trace::DS_LOGIC) { - _measure_shown = false; - break; - } else if ( curY < s->get_y() + _view.get_signalHeight() * 0.5 && - curY > (s->get_y() - _view.get_signalHeight() * 0.5)) { - if (s->cur_edges().size() > 2) { - const double pixels_offset = _view.offset() / _view.scale(); - const double samples_per_pixel = sample_rate * _view.scale(); + _mm_width = _view.get_ruler()->format_real_time(_nxt_sample - _cur_sample, sample_rate); + _mm_period = _thd_sample != 0 ? _view.get_ruler()->format_real_time(_thd_sample - _cur_sample, sample_rate) : "#####"; + _mm_freq = _thd_sample != 0 ? _view.get_ruler()->format_real_freq(_thd_sample - _cur_sample, sample_rate) : "#####"; - uint64_t findIndex = curX / _view.get_view_width() * s->cur_edges().size(); - uint64_t left_findIndex = 0; - uint64_t right_findIndex = s->cur_edges().size() - 1; - int times = 0; - while(!s->cur_edges().empty() && times < 20) { - findIndex = min(findIndex, (uint64_t)(s->cur_edges().size() - 2)); - const double pre_edge_x = - s->cur_edges().at(findIndex).first / samples_per_pixel - pixels_offset; - const double aft_edge_x = - s->cur_edges().at(findIndex + 1).first / samples_per_pixel - pixels_offset; - if ( curX >= pre_edge_x && curX <= aft_edge_x) { - if (aft_edge_x - pre_edge_x < 2 || - findIndex == 0 || - findIndex == s->cur_edges().size() - 2) { - _measure_shown = false; - break; - } else { - _measure_shown = true; - _cur_sample = s->cur_edges().at(findIndex).first; - _nxt_sample = s->cur_edges().at(findIndex + 1).first; - _cur_preX = pre_edge_x; - _cur_aftX = aft_edge_x; - //if (findIndex >= 0 && findIndex <= s->cur_edges().size() - 4) { - if(findIndex <= s->cur_edges().size() - 4) { - _thd_sample = s->cur_edges().at(findIndex + 2).first; - _cur_thdX = - s->cur_edges().at(findIndex + 2).first / samples_per_pixel - pixels_offset; - } else { - _thd_sample = 0; - _cur_thdX = 0; + const double pixels_offset = _view.offset() / _view.scale(); + const double samples_per_pixel = sample_rate * _view.scale(); + _cur_preX = _cur_sample / samples_per_pixel - pixels_offset; + _cur_aftX = _nxt_sample / samples_per_pixel - pixels_offset; + _cur_thdX = _thd_sample / samples_per_pixel - pixels_offset; + _cur_midY = logicSig->get_y(); - } - _cur_midY = s->get_y(); - break; - } - } else if (curX < pre_edge_x) { - right_findIndex = findIndex; - findIndex = (left_findIndex + findIndex) / 2; - } else if (curX > aft_edge_x) { - left_findIndex = findIndex; - findIndex = (right_findIndex + findIndex) / 2; - } - times++; - } - } - break; - } else if (curY >= s->get_y() + _view.get_signalHeight() && - curY <= (s->get_y() + _view.get_signalHeight() + 2 * View::SignalMargin)){ - _measure_shown = false; - break; - }else { - _measure_shown = false; - } + _mm_duty = _thd_sample != 0 ? QString::number((_nxt_sample - _cur_sample) * 100.0 / (_thd_sample - _cur_sample), 'f', 2)+"%" : + "#####"; + mouse_measure(); + break; + } else { + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + } + mouse_measure(); + } else if (dsoSig = dynamic_pointer_cast(s)) { + if (_measure_en && dsoSig->measure(_view.hover_point())) { + _measure_shown = true; + _measure_type = DSO; + } + } } - - if (_measure_shown == true) { - const uint64_t delta_sample = _nxt_sample - _cur_sample; - const uint64_t delta1_sample = _thd_sample - _cur_sample; - //assert(delta_sample >= 0); - const double delta_time = delta_sample * 1.0f / sample_rate; - const double delta1_time = delta1_sample * 1.0f / sample_rate; - const int order = (int)floorf(log10f(delta_time)); - unsigned int prefix = (15 + order) / 3; - assert(prefix < 9); - - _mm_width = _view.get_ruler()->format_time(delta_time, prefix); - _mm_period = _thd_sample != 0 ? _view.get_ruler()->format_time(delta1_time, prefix) : - "#####"; - _mm_freq = _thd_sample != 0 ? _view.get_ruler()->format_freq(delta1_time) : - "#####"; - } else { - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - } - - _view.on_mouse_moved(); } void Viewport::paintMeasure(QPainter &p) { - p.setPen(QColor(17, 133, 209, 255)); - p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); - p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY - 2)); - p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY + 2)); - p.drawLine(QLineF(_cur_aftX - 2, _cur_midY - 2, _cur_aftX, _cur_midY)); - p.drawLine(QLineF(_cur_aftX - 2, _cur_midY + 2, _cur_aftX, _cur_midY)); - if (_thd_sample != 0) { - p.drawLine(QLineF(_cur_aftX, _cur_midY, _cur_thdX, _cur_midY)); - p.drawLine(QLineF(_cur_aftX, _cur_midY, _cur_aftX + 2, _cur_midY - 2)); - p.drawLine(QLineF(_cur_aftX, _cur_midY, _cur_aftX + 2, _cur_midY + 2)); - p.drawLine(QLineF(_cur_thdX - 2, _cur_midY - 2, _cur_thdX, _cur_midY)); - p.drawLine(QLineF(_cur_thdX - 2, _cur_midY + 2, _cur_thdX, _cur_midY)); - } + _hover_hit = false; + if (_measure_type == LOGIC) { + p.setPen(QColor(17, 133, 209, 255)); + p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); + p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY - 2)); + p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY + 2)); + p.drawLine(QLineF(_cur_aftX - 2, _cur_midY - 2, _cur_aftX, _cur_midY)); + p.drawLine(QLineF(_cur_aftX - 2, _cur_midY + 2, _cur_aftX, _cur_midY)); + if (_thd_sample != 0) { + p.drawLine(QLineF(_cur_aftX, _cur_midY, _cur_thdX, _cur_midY)); + p.drawLine(QLineF(_cur_aftX, _cur_midY, _cur_aftX + 2, _cur_midY - 2)); + p.drawLine(QLineF(_cur_aftX, _cur_midY, _cur_aftX + 2, _cur_midY + 2)); + p.drawLine(QLineF(_cur_thdX - 2, _cur_midY - 2, _cur_thdX, _cur_midY)); + p.drawLine(QLineF(_cur_thdX - 2, _cur_midY + 2, _cur_thdX, _cur_midY)); + } - if (_measure_en) { - double typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, _mm_width).width() + 150; - QRectF measure_rect = QRectF(_view.hover_point().x(), _view.hover_point().y(), - (double)typical_width, 60.0); - QRectF measure1_rect = QRectF(_view.hover_point().x(), _view.hover_point().y(), - (double)typical_width, 20.0); - QRectF measure2_rect = QRectF(_view.hover_point().x(), _view.hover_point().y() + 20, - (double)typical_width, 20.0); - QRectF measure3_rect = QRectF(_view.hover_point().x(), _view.hover_point().y() + 40, - (double)typical_width, 20.0); + if (_measure_en) { + int typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _mm_width).width(); + typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _mm_period).width()); + typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _mm_freq).width()); + typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _mm_duty).width()); + typical_width = typical_width + 100; - p.setPen(Qt::NoPen); - p.setBrush(QColor(17, 133, 209, 150)); - p.drawRect(measure_rect); + const double width = _view.get_view_width(); + const double height = _view.viewport()->height(); + const double left = _view.hover_point().x(); + const double top = _view.hover_point().y(); + const double right = left + typical_width; + const double bottom = top + 80; + QPointF org_pos = QPointF(right > width ? left - typical_width : left, bottom > height ? top - 80 : top); + QRectF measure_rect = QRectF(org_pos.x(), org_pos.y(), (double)typical_width, 80.0); + QRectF measure1_rect = QRectF(org_pos.x(), org_pos.y(), (double)typical_width, 20.0); + QRectF measure2_rect = QRectF(org_pos.x(), org_pos.y()+20, (double)typical_width, 20.0); + QRectF measure3_rect = QRectF(org_pos.x(), org_pos.y()+40, (double)typical_width, 20.0); + QRectF measure4_rect = QRectF(org_pos.x(), org_pos.y()+60, (double)typical_width, 20.0); - p.setPen(Qt::black); - p.drawText(measure1_rect, Qt::AlignRight | Qt::AlignVCenter, - "Width: " + _mm_width); - p.drawText(measure2_rect, Qt::AlignRight | Qt::AlignVCenter, - "Period: " + _mm_period); - p.drawText(measure3_rect, Qt::AlignRight | Qt::AlignVCenter, - "Frequency: " + _mm_freq); + p.setPen(Qt::NoPen); + p.setBrush(QColor(17, 133, 209, 150)); + p.drawRect(measure_rect); + + p.setPen(Qt::black); + p.drawText(measure1_rect, Qt::AlignRight | Qt::AlignVCenter, + "Width: " + _mm_width); + p.drawText(measure2_rect, Qt::AlignRight | Qt::AlignVCenter, + "Period: " + _mm_period); + p.drawText(measure3_rect, Qt::AlignRight | Qt::AlignVCenter, + "Frequency: " + _mm_freq); + p.drawText(measure4_rect, Qt::AlignRight | Qt::AlignVCenter, + "Duty Cycle: " + _mm_duty); + } + } else if (_measure_type == DSO) { + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + uint64_t index; + double value; + QPointF hpoint; + const int arrow_size = 5; + const int mark_radius = 10; + const int mark_width = 20; + const int mark_cursor_height = 30; + if (dsoSig->get_hover(index, hpoint, value)) { + p.setPen(dsoSig->get_colour()); + const QRectF hpoint_rect = QRectF(hpoint.x()-mark_radius/2, hpoint.y()-mark_radius/2, mark_radius, mark_radius); + if (hpoint_rect.contains(_view.hover_point())) { + p.setBrush(dsoSig->get_colour()); + const int cursor_up = hpoint.y()-mark_cursor_height; + const int cursor_dn = hpoint.y()+mark_cursor_height; + const int cursor_lf = hpoint.x()-arrow_size; + const int cursor_md = hpoint.x(); + const int cursor_rt = hpoint.x()+arrow_size; + + const QPointF up_arrow[3] = { + QPointF(cursor_lf, cursor_up+arrow_size), + QPointF(cursor_md, cursor_up), + QPointF(cursor_rt, cursor_up+arrow_size), + }; + const QPointF dn_arrow[3] = { + QPointF(cursor_lf, cursor_dn-arrow_size), + QPointF(cursor_md, cursor_dn), + QPointF(cursor_rt, cursor_dn-arrow_size), + }; + p.drawPolyline(up_arrow, 3); + p.drawPolyline(dn_arrow, 3); + p.drawLine(cursor_md, cursor_up, cursor_md, cursor_dn); + _hover_hit = true; + _hover_index = index; + } else { + p.setBrush(Qt::NoBrush); + } + p.drawEllipse(hpoint, mark_radius, mark_radius); + QString value_c = abs(value) > 1000 ? QString::number(value/1000.0, 'f', 2) + "V" : QString::number(value, 'f', 2) + "mV"; + int value_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, value_c).width(); + const bool right = dsoSig->get_index()%2 ? hpoint.x() < value_width : hpoint.x() < _view.get_view_width() - value_width; + const bool up = hpoint.y() > 50; + const QPointF hpoint_sec = QPointF(hpoint.x() - (right ? -mark_width : mark_width), hpoint.y() - (up ? mark_width : -mark_width)); + p.drawLine(hpoint, hpoint_sec); + p.drawLine(hpoint_sec, QPointF(hpoint_sec.x() + (right ? value_width : -value_width), hpoint_sec.y())); + p.drawText(QRectF(right ? hpoint_sec.x() : hpoint_sec.x() - value_width, hpoint_sec.y() - mark_width, value_width, mark_width), + Qt::AlignLeft | Qt::AlignBottom, + value_c); + } + } + } } } -QString Viewport::get_mm_width() +QString Viewport::get_measure(QString option) { - return _mm_width; -} - -QString Viewport::get_mm_period() -{ - return _mm_period; -} - -QString Viewport::get_mm_freq() -{ - return _mm_freq; + if(option.compare("width") == 0) + return _mm_width; + else if (option.compare("period") == 0) + return _mm_period; + else if (option.compare("frequency") == 0) + return _mm_freq; + else if (option.compare("duty") == 0) + return _mm_duty; + else + return "#####"; } void Viewport::set_measure_en(int enable) diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 1f8157a9..4899577b 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -47,7 +47,7 @@ class Viewport : public QWidget public: static const int HitCursorMargin = 10; - static const double HitCursorTimeMargin = 0.3; + static const double HitCursorTimeMargin; public: explicit Viewport(View &parent); @@ -56,11 +56,7 @@ public: QPoint get_mouse_point() const; - void set_receive_len(quint64 length); - - QString get_mm_width(); - QString get_mm_period(); - QString get_mm_freq(); + QString get_measure(QString option); void set_measure_en(int enable); @@ -87,11 +83,15 @@ private: private slots: void on_traces_moved(); void on_trigger_timer(); + void set_receive_len(quint64 length); + +signals: + void mouse_measure(); private: View &_view; - quint64 _total_receive_len; + uint64_t _total_receive_len; QPoint _mouse_point; QPoint _mouse_down_point; double _mouse_down_offset; @@ -106,22 +106,27 @@ private: bool _measure_en; bool _measure_shown; + int _measure_type; uint64_t _cur_sample; uint64_t _nxt_sample; uint64_t _thd_sample; - int64_t _cur_preX; - int64_t _cur_aftX; - int64_t _cur_thdX; - int64_t _cur_midY; + double _cur_preX; + double _cur_aftX; + double _cur_thdX; + double _cur_midY; QString _mm_width; QString _mm_period; QString _mm_freq; + QString _mm_duty; QTimer trigger_timer; bool triggered; int timer_cnt; boost::shared_ptr _drag_sig; + + uint64_t _hover_index; + bool _hover_hit; }; } // namespace view diff --git a/DSView/res/DSCope.bin b/DSView/res/DSCope.bin index c79028f4..94baf28d 100644 Binary files a/DSView/res/DSCope.bin and b/DSView/res/DSCope.bin differ diff --git a/DSView/res/DSLogic33.bin b/DSView/res/DSLogic33.bin index fa38bcdc..9a3304f6 100644 Binary files a/DSView/res/DSLogic33.bin and b/DSView/res/DSLogic33.bin differ diff --git a/DSView/res/DSLogic50.bin b/DSView/res/DSLogic50.bin index 8174bef5..627020fb 100644 Binary files a/DSView/res/DSLogic50.bin and b/DSView/res/DSLogic50.bin differ diff --git a/DSView/res/DSLogicPro.bin b/DSView/res/DSLogicPro.bin index 6b1184c6..9b6b0982 100644 Binary files a/DSView/res/DSLogicPro.bin and b/DSView/res/DSLogicPro.bin differ 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/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index 7b99c2f0..bcd6e052 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -249,9 +249,9 @@ static int fpga_setting(const struct sr_dev_inst *sdi) ((sdi->mode == ANALOG) << 7) + ((devc->filter == SR_FILTER_1T) << 8) + (devc->instant << 9) + (devc->zero << 10); - setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate); + setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); setting.count = (uint32_t)(devc->limit_samples / (channel_cnt / channel_en_cnt)); - setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0f * devc->limit_samples); + setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples); setting.trig_glb = trigger->trigger_stages; setting.trig_adp = setting.count - setting.trig_pos - 1; setting.trig_sda = 0x0; @@ -601,6 +601,7 @@ static int set_probes(struct sr_dev_inst *sdi, int num_probes) return SR_ERR; if (sdi->mode == DSO) { probe->vdiv = 1000; + probe->vfactor = 1; probe->vpos = 0; probe->coupling = SR_DC_COUPLING; probe->trig_value = 0x80; @@ -846,7 +847,7 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int channel_cnt += probe->enabled; } cmd += 0x18; - uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate); + uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_cnt); cmd += divider << 8; break; case SR_CONF_HORIZ_TRIGGERPOS: @@ -1173,6 +1174,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, return SR_ERR; *data = g_variant_new_uint64(ch->vdiv); break; + case SR_CONF_FACTOR: + if (!ch) + return SR_ERR; + *data = g_variant_new_uint64(ch->vfactor); + break; case SR_CONF_VPOS: if (!ch) return SR_ERR; @@ -1421,6 +1427,8 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, else sr_dbg("%s: setting VDIV of channel %d to %d mv failed", __func__, ch->index, ch->vdiv); + } else if (id == SR_CONF_FACTOR) { + ch->vfactor = g_variant_get_uint64(data); } else if (id == SR_CONF_VPOS) { ch->vpos = g_variant_get_double(data); if (sdi->mode == DSO) { @@ -1485,7 +1493,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, struct sr_channel *probe = (struct sr_channel *)l->data; channel_cnt += probe->enabled; } - devc->trigger_hpos = g_variant_get_uint16(data) * channel_cnt * devc->limit_samples / 200.0f; + devc->trigger_hpos = g_variant_get_uint16(data) * channel_cnt * devc->limit_samples / 200.0; if (sdi->mode == DSO) { ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS)); } @@ -1853,11 +1861,12 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.ch0_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 1*2); mstatus.ch0_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 3); mstatus.ch0_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 2/2); - mstatus.ch0_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2); - mstatus.ch1_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 7*2); - mstatus.ch1_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 15); - mstatus.ch1_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 8/2); - mstatus.ch1_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 10/2); + mstatus.ch0_period += ((uint64_t)*((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2)) << 32; + mstatus.ch0_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 6/2); + mstatus.ch1_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 9*2); + mstatus.ch1_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 19); + mstatus.ch1_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 10/2); + mstatus.ch1_period += ((uint64_t)*((const uint32_t*)cur_buf + mstatus_offset/2 + 12/2)) << 32; mstatus.vlen = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x7fffffff; mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2); @@ -1879,7 +1888,7 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.vlen = instant_buffer_size; } - const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate); + const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); if ((mstatus.sample_divider == divider && mstatus.vlen != 0 && mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || @@ -2001,7 +2010,7 @@ static unsigned int get_number_of_transfers(struct DSL_context *devc) total_buffer_time * to_bytes_per_ms(devc)); /* Total buffer size should be able to hold about 500ms of data. */ //n = 500 * to_bytes_per_ms(devc) / get_buffer_size(devc); - n = ceil(total_size * 1.0f / get_buffer_size(devc)); + n = ceil(total_size * 1.0 / get_buffer_size(devc)); if (n > NUM_SIMUL_TRANSFERS) return NUM_SIMUL_TRANSFERS; diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index 95ee508c..16bf94b6 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -287,9 +287,12 @@ static int fpga_setting(const struct sr_dev_inst *sdi) ((sdi->mode == ANALOG) << 7) + ((devc->filter == SR_FILTER_1T) << 8) + (devc->instant << 9) + (devc->zero << 10); - setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate); + if (sdi->mode == DSO) + setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); + else + setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate); setting.count = (sdi->mode == DSO) ? (uint32_t)(devc->limit_samples / (channel_cnt / channel_en_cnt)) : (uint32_t)(devc->limit_samples); - setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0f * devc->limit_samples); + setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples); setting.trig_glb = trigger->trigger_stages; setting.trig_adp = setting.count - setting.trig_pos - 1; setting.trig_sda = 0x0; @@ -642,6 +645,7 @@ static int set_probes(struct sr_dev_inst *sdi, int num_probes) return SR_ERR; if (sdi->mode == DSO) { probe->vdiv = 1000; + probe->vfactor = 1; probe->coupling = SR_DC_COUPLING; probe->trig_value = 0x80; } @@ -881,7 +885,7 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int channel_cnt += probe->enabled; } cmd += 0x18; - uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate); + uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_cnt); cmd += divider << 8; break; case SR_CONF_HORIZ_TRIGGERPOS: @@ -1156,6 +1160,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, return SR_ERR; *data = g_variant_new_uint64(ch->vdiv); break; + case SR_CONF_FACTOR: + if (!ch) + return SR_ERR; + *data = g_variant_new_uint64(ch->vfactor); + break; case SR_CONF_TIMEBASE: if (!sdi) return SR_ERR; @@ -1518,6 +1527,8 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, else sr_dbg("%s: setting VDIV of channel %d to %d mv failed", __func__, ch->index, ch->vdiv); + } else if (id == SR_CONF_FACTOR) { + ch->vfactor = g_variant_get_uint64(data); } else if (id == SR_CONF_TIMEBASE) { devc->timebase = g_variant_get_uint64(data); } else if (id == SR_CONF_COUPLING) { @@ -1574,9 +1585,9 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, struct sr_channel *probe = (struct sr_channel *)l->data; channel_cnt += probe->enabled; } - devc->trigger_hpos = g_variant_get_uint16(data) * channel_cnt * devc->limit_samples / 200.0f; + devc->trigger_hpos = g_variant_get_uint16(data) * channel_cnt * devc->limit_samples / 200.0; } else { - devc->trigger_hpos = g_variant_get_uint16(data) * devc->limit_samples / 100.0f; + devc->trigger_hpos = g_variant_get_uint16(data) * devc->limit_samples / 100.0; } if (sdi->mode == DSO) { ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS)); @@ -1935,11 +1946,13 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.ch0_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 1*2); mstatus.ch0_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 3); mstatus.ch0_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 2/2); - mstatus.ch0_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2); - mstatus.ch1_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 7*2); - mstatus.ch1_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 15); - mstatus.ch1_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 8/2); - mstatus.ch1_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 10/2); + mstatus.ch0_period += *((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2) << 32; + mstatus.ch0_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 6/2); + mstatus.ch1_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 9*2); + mstatus.ch1_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 19); + mstatus.ch1_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 10/2); + mstatus.ch1_period += *((const uint32_t*)cur_buf + mstatus_offset/2 + 12/2) << 32; + mstatus.ch1_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 14/2); mstatus.vlen = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x7fffffff; mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2); @@ -1954,7 +1967,7 @@ static void receive_transfer(struct libusb_transfer *transfer) } else { mstatus.vlen = instant_buffer_size; } - const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate); + const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); if ((mstatus.sample_divider == divider && mstatus.vlen != 0 && mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index 7e19b0aa..861b38a0 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -344,6 +344,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_VDIV: *data = g_variant_new_uint64(ch->vdiv); break; + case SR_CONF_FACTOR: + *data = g_variant_new_uint64(ch->vfactor); + break; case SR_CONF_TIMEBASE: *data = g_variant_new_uint64(devc->timebase); break; @@ -424,6 +427,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, ret = SR_ERR; else { probe->vdiv = 1000; + probe->vfactor = 1; probe->coupling = SR_DC_COUPLING; probe->trig_value = 0x80; sdi->channels = g_slist_append(sdi->channels, probe); @@ -472,6 +476,11 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting VDIV of channel %d to %" PRIu64, __func__, ch->index, ch->vdiv); ret = SR_OK; + } else if (id == SR_CONF_FACTOR) { + ch->vfactor = g_variant_get_uint64(data); + sr_dbg("%s: setting FACTOR of channel %d to %" PRIu64, __func__, + ch->index, ch->vfactor); + ret = SR_OK; } else if (id == SR_CONF_TIMEBASE) { devc->timebase = g_variant_get_uint64(data); sr_dbg("%s: setting TIMEBASE to %" PRIu64, __func__, @@ -601,8 +610,8 @@ static void samples_generator(uint16_t *buf, uint64_t size, case PATTERN_TRIANGLE: for (i = 0; i < size; i++) { if (i%CONST_LEN == 0) { - demo_data = p > 0x7fff ? 0x40 * (1 + (0x8000 - p * 1.0f) / 0x8000) : - 0x40 * (p * 1.0f / 0x8000); + demo_data = p > 0x7fff ? 0x40 * (1 + (0x8000 - p * 1.0) / 0x8000) : + 0x40 * (p * 1.0 / 0x8000); p += CONST_LEN * 10; } *(buf + i) = demo_data + (demo_data << 8); @@ -654,7 +663,7 @@ static void samples_generator(uint16_t *buf, uint64_t size, case PATTERN_RANDOM: /* Random */ for (i = 0; i < size; i++) { if (i%CONST_LEN == 0) - demo_data = (uint16_t)(rand() * (0x40 * 1.0f / RAND_MAX)); + demo_data = (uint16_t)(rand() * (0x40 * 1.0 / RAND_MAX)); *(buf + i) = demo_data + (demo_data << 8); GSList *l; struct sr_channel *probe; diff --git a/libsigrok4DSL/hwdriver.c b/libsigrok4DSL/hwdriver.c index 239827eb..3b2af0ae 100644 --- a/libsigrok4DSL/hwdriver.c +++ b/libsigrok4DSL/hwdriver.c @@ -83,6 +83,8 @@ static struct sr_config_info sr_config_info_data[] = { "Filter Targets", NULL}, {SR_CONF_VDIV, SR_T_RATIONAL_VOLT, "vdiv", "Volts/div", NULL}, + {SR_CONF_VDIV, SR_T_RATIONAL_VOLT, "factor", + "Probe Factor", NULL}, {SR_CONF_COUPLING, SR_T_CHAR, "coupling", "Coupling", NULL}, {SR_CONF_DATALOG, SR_T_BOOL, "datalog", diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index 9d719bfb..dac954c2 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -32,7 +32,7 @@ #define WINVER 0x0501 #define _WIN32_WINNT WINVER #include -#include +#include #endif #ifdef __cplusplus @@ -323,7 +323,7 @@ struct sr_datafeed_dso { uint64_t mqflags; /** The analog value(s). The data is interleaved according to * the probes list. */ - float *data; + void *data; }; struct sr_datafeed_analog { @@ -389,7 +389,7 @@ struct sr_input_format { /** * Load a file, parsing the input according to the file's format. - * + * * This function will send datafeed packets to the session bus, so * the calling frontend must have registered its session callbacks * beforehand. @@ -404,7 +404,7 @@ struct sr_input_format { * the responsibility of the caller to free it later. * @param filename The name (and path) of the file to use. * - * @return SR_OK upon success, a negative error code upon failure. + * @return SR_OK upon succcess, a negative error code upon failure. */ int (*loadfile) (struct sr_input *in, const char *filename); }; @@ -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, @@ -591,6 +559,7 @@ struct sr_channel { char *name; char *trigger; uint64_t vdiv; + uint16_t vfactor; double vpos; uint8_t coupling; uint8_t trig_value; @@ -641,11 +610,11 @@ struct sr_status { uint8_t ch0_max; uint8_t ch0_min; - uint32_t ch0_period; + uint64_t ch0_period; uint32_t ch0_pcnt; uint8_t ch1_max; uint8_t ch1_min; - uint32_t ch1_period; + uint64_t ch1_period; uint32_t ch1_pcnt; uint32_t vlen; @@ -798,6 +767,9 @@ enum { /** Channel enable for dso channel. */ SR_CONF_EN_CH, + /** probe factor for dso channel. */ + SR_CONF_FACTOR, + /** Trigger types. */ SR_CONF_TRIGGER_TYPE, 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..3617b61f --- /dev/null +++ b/libsigrok4DSL/output/csv.c @@ -0,0 +1,283 @@ +/* + * 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; + float *channel_vdiv; + double *channel_vpos; + uint64_t timebase; + uint64_t mask; + uint64_t pre_data; + uint64_t index; + int type; +}; + +/* + * 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; + + if (!o || !o->sdi) + return SR_ERR_ARG; + + ctx = g_malloc0(sizeof(struct context)); + o->priv = ctx; + ctx->separator = ','; + ctx->mask = 0; + ctx->index = 0; + ctx->type = g_variant_get_int16(g_hash_table_lookup(options, "type")); + ctx->timebase = g_variant_get_uint64(g_hash_table_lookup(options, "timebase")); + + /* Get the number of channels, and the unitsize. */ + for (l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != ctx->type) + continue; + if (!ch->enabled) + continue; + ctx->num_enabled_channels++; + } + ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels); + ctx->channel_vdiv = g_malloc(sizeof(float) * ctx->num_enabled_channels); + ctx->channel_vpos = g_malloc(sizeof(double) * 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 != ctx->type) + continue; + if (!ch->enabled) + continue; + ctx->channel_index[i] = ch->index; + ctx->mask |= (1 << ch->index); + ctx->channel_vdiv[i] = ch->vdiv * ch->vfactor >= 500 ? ch->vdiv * ch->vfactor / 100.0f : ch->vdiv * ch->vfactor * 10.0f; + ctx->channel_vpos[i] = ch->vdiv * ch->vfactor >= 500 ? ch->vpos / 1000 : ch->vpos; + i++; + } + + 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; + + 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 */ + if (ctx->type == SR_CHANNEL_LOGIC) + num_channels = g_slist_length(o->sdi->channels); + else + num_channels = ctx->num_enabled_channels; + g_string_append_printf(header, "; Channels (%d/%d)\n", + 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) { + char *samplerate_s = sr_samplerate_string(ctx->samplerate); + g_string_append_printf(header, "; Sample rate: %s\n", samplerate_s); + g_free(samplerate_s); + } + + if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_LIMIT_SAMPLES, + &gvar) == SR_OK) { + uint64_t depth = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + char *depth_s = sr_samplecount_string(depth); + g_string_append_printf(header, "; Sample count: %s\n", depth_s); + g_free(depth_s); + } + + if (ctx->type == SR_CHANNEL_LOGIC) + g_string_append_printf(header, "Time(s),"); + for (i = 0, l = o->sdi->channels; l; l = l->next, i++) { + ch = l->data; + if (ch->type != ctx->type) + continue; + if (!ch->enabled) + continue; + if (ctx->type == SR_CHANNEL_DSO) { + char *unit_s = (ch->vdiv * ch->vfactor) >= 500 ? "V" : "mV"; + g_string_append_printf(header, " %s (Unit: %s),", ch->name, unit_s); + } else { + 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"); + + 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_datafeed_dso *dso; + const struct sr_config *src; + GSList *l; + struct context *ctx; + int idx; + uint64_t i, j; + unsigned char *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) { + ctx->index++; + if (ctx->index > 1 && (*(uint64_t *)(logic->data + i) & ctx->mask) == ctx->pre_data) + continue; + g_string_append_printf(*out, "%0.10g", (ctx->index-1)*1.0/ctx->samplerate); + 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, ctx->separator); + g_string_append_c(*out, c ? '1' : '0'); + } + g_string_append_printf(*out, "\n"); + ctx->pre_data = (*(uint64_t *)(logic->data + i) & ctx->mask); + } + break; + case SR_DF_DSO: + dso = 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 < dso->num_samples; i++) { + for (j = 0; j < ctx->num_enabled_channels; j++) { + idx = ctx->channel_index[j]; + p = dso->data + i * ctx->num_enabled_channels + idx * ((ctx->num_enabled_channels > 1) ? 1 : 0); + g_string_append_printf(*out, "%0.2f", (128 - *p) * ctx->channel_vdiv[j] / 255 - ctx->channel_vpos[j]); + g_string_append_c(*out, ctx->separator); + } + + /* 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..55cf38ed 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 -------------------------------------------------------------*/ @@ -141,6 +141,7 @@ SR_API char *sr_iec_string_u64(uint64_t x, const char *unit); SR_API char *sr_samplerate_string(uint64_t samplerate); SR_API char *sr_samplecount_string(uint64_t samplecount); SR_API char *sr_period_string(uint64_t frequency); +SR_API char *sr_time_string(uint64_t time); SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q); SR_API char **sr_parse_triggerstring(const struct sr_dev_inst *sdi, const char *triggerstring); diff --git a/libsigrok4DSL/strutil.c b/libsigrok4DSL/strutil.c index 1b05ebc4..24b5e380 100644 --- a/libsigrok4DSL/strutil.c +++ b/libsigrok4DSL/strutil.c @@ -211,6 +211,47 @@ SR_API char *sr_period_string(uint64_t frequency) return o; } +/** + * Convert a numeric time(ns) value to the "natural" string representation + * of its period. + * + * E.g. a value of 3000000 would be converted to "3 ms", 20000 to "20 us". + * + * @param time The time in ns. + * + * @return A g_try_malloc()ed string representation of the time value, + * or NULL upon errors. The caller is responsible to g_free() the + * memory. + */ +SR_API char *sr_time_string(uint64_t time) +{ + char *o; + int r; + + /* Allocate enough for a uint64_t as string + " ms". */ + if (!(o = g_try_malloc0(30 + 1))) { + sr_err("%s: o malloc failed", __func__); + return NULL; + } + + if (time >= 1000000000) + r = snprintf(o, 30, "%" PRIu64 " s", time / 1000000000); + else if (time >= 1000000) + r = snprintf(o, 30, "%" PRIu64 " ms", time / 1000000); + else if (time >= 1000) + r = snprintf(o, 30, "%" PRIu64 " us", time / 1000); + else + r = snprintf(o, 30, "%" PRIu64 " ns", time); + + if (r < 0) { + /* Something went wrong... */ + g_free(o); + return NULL; + } + + return o; +} + /** * Convert a numeric voltage value to the "natural" string representation * of its voltage value. The voltage is specified as a rational number's 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