diff --git a/CMakeLists.txt b/CMakeLists.txt index 935d0cf4..293b4296 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ set(ENABLE_TESTING OFF) FetchContent_Declare( graph-prototype GIT_REPOSITORY https://github.com/fair-acc/graph-prototype.git - GIT_TAG 953501708db91ecda21805b6e2dd8af1da50399c # main as of 2024-01-02 + GIT_TAG 7d22e962c7f67fee95fd98c38bdb32ccde78d616 # main as of 2024-01-17 ) FetchContent_Declare( diff --git a/blocklib/helpers/HelperBlocks.hpp b/blocklib/helpers/HelperBlocks.hpp index 492d2107..f0408d37 100644 --- a/blocklib/helpers/HelperBlocks.hpp +++ b/blocklib/helpers/HelperBlocks.hpp @@ -45,26 +45,6 @@ struct VectorSink : public gr::Block> { } }; -template -struct TagDebug : public gr::Block> { - gr::PortIn in; - gr::PortOut out; - std::vector seen_tags; - std::size_t samples_seen = 0; - - gr::work::Status - processBulk(std::span input, std::span output) noexcept { - std::copy(input.begin(), input.end(), output.begin()); - if (this->input_tags_present()) { - auto tag = this->input_tags()[0]; - tag.index += static_cast(samples_seen); - seen_tags.push_back(std::move(tag)); - } - samples_seen += input.size(); - return gr::work::Status::OK; - } -}; - template struct CountSink : public gr::Block> { gr::PortIn in; @@ -81,7 +61,6 @@ struct CountSink : public gr::Block> { ENABLE_REFLECTION_FOR_TEMPLATE(fair::helpers::VectorSource, out, data); ENABLE_REFLECTION_FOR_TEMPLATE(fair::helpers::VectorSink, in, data); -ENABLE_REFLECTION_FOR_TEMPLATE(fair::helpers::TagDebug, in, out); ENABLE_REFLECTION_FOR_TEMPLATE(fair::helpers::CountSink, in); #endif diff --git a/blocklib/picoscope/Picoscope.hpp b/blocklib/picoscope/Picoscope.hpp index 1862e0f9..d79742be 100644 --- a/blocklib/picoscope/Picoscope.hpp +++ b/blocklib/picoscope/Picoscope.hpp @@ -149,10 +149,8 @@ struct State { double actual_sample_rate = 0; std::thread poller; BufferHelper errors; - std::atomic poller_state = PollerState::Idle; - std::atomic forced_quit = false; // TODO transitional until we found out what - // goes wrong with multithreaded scheduler - std::size_t produced_worker = 0; // poller/callback thread + std::atomic poller_state = PollerState::Idle; + std::size_t produced_worker = 0; // poller/callback thread }; inline AcquisitionMode @@ -242,14 +240,14 @@ struct Picoscope : public gr::Block, gr::Supported A trigger_direction = std::string("Rising"); A trigger_pin = 0; - detail::State state; + detail::State ps_state; detail::Settings ps_settings; ~Picoscope() { stop(); } void settingsChanged(const gr::property_map & /*old_settings*/, const gr::property_map & /*new_settings*/) { - const auto wasStarted = state.started; + const auto wasStarted = ps_state.started; if (wasStarted) { stop(); } @@ -269,14 +267,14 @@ struct Picoscope : public gr::Block, gr::Supported }; std::swap(ps_settings, s); - state.channels.reserve(ps_settings.enabled_channels.size()); + ps_state.channels.reserve(ps_settings.enabled_channels.size()); std::size_t channelIdx = 0; for (const auto &[id, settings] : ps_settings.enabled_channels) { - state.channels.emplace_back(detail::Channel{ .id = id, - .settings = settings, - .driver_buffer = std::vector(detail::kDriverBufferSize), - .data_writer = self().analog_out[channelIdx].streamWriter().buffer().new_writer(), - .tag_writer = self().analog_out[channelIdx].tagWriter().buffer().new_writer() }); + ps_state.channels.emplace_back(detail::Channel{ .id = id, + .settings = settings, + .driver_buffer = std::vector(detail::kDriverBufferSize), + .data_writer = self().analog_out[channelIdx].streamWriter().buffer().new_writer(), + .tag_writer = self().analog_out[channelIdx].tagWriter().buffer().new_writer() }); channelIdx++; } } catch (const std::exception &e) { @@ -291,23 +289,21 @@ struct Picoscope : public gr::Block, gr::Supported gr::work::Result workImpl() noexcept { using enum gr::work::Status; - start(); // TODO should be done by scheduler - if (state.channels.empty()) { + if (ps_state.channels.empty()) { return { 0, 0, ERROR }; } - if (const auto errors_available = state.errors.reader.available(); errors_available > 0) { - auto errors = state.errors.reader.get(errors_available); - std::ignore = state.errors.reader.consume(errors_available); + if (const auto errors_available = ps_state.errors.reader.available(); errors_available > 0) { + auto errors = ps_state.errors.reader.get(errors_available); + std::ignore = ps_state.errors.reader.consume(errors_available); return { 0, 0, ERROR }; } - if (state.forced_quit) { - return { 0, 0, DONE }; - } - - if (state.data_finished) { + if (ps_state.data_finished) { + std::atomic_store_explicit(&this->state, gr::lifecycle::State::STOPPED, std::memory_order_release); + this->state.notify_all(); + this->publishTag({ { gr::tag::END_OF_STREAM, true } }, 0); return { 0, 0, DONE }; } @@ -327,17 +323,12 @@ struct Picoscope : public gr::Block, gr::Supported // TODO only for debugging, maybe remove std::size_t producedWorker() const { - return state.produced_worker; - } - - void - forceQuit() { - state.forced_quit = true; + return ps_state.produced_worker; } void start() noexcept { - if (state.started) { + if (ps_state.started) { return; } @@ -350,7 +341,7 @@ struct Picoscope : public gr::Block, gr::Supported if (ps_settings.acquisition_mode == AcquisitionMode::Streaming) { startPollThread(); } - state.started = true; + ps_state.started = true; } catch (const std::exception &e) { fmt::println(std::cerr, "{}", e.what()); } @@ -358,13 +349,13 @@ struct Picoscope : public gr::Block, gr::Supported void stop() noexcept { - if (!state.started) { + if (!ps_state.started) { return; } - state.started = false; + ps_state.started = false; - if (!state.initialized) { + if (!ps_state.initialized) { return; } @@ -378,12 +369,12 @@ struct Picoscope : public gr::Block, gr::Supported void startPollThread() { - if (state.poller.joinable()) { + if (ps_state.poller.joinable()) { return; } - if (state.poller_state == detail::PollerState::Exit) { - state.poller_state = detail::PollerState::Idle; + if (ps_state.poller_state == detail::PollerState::Exit) { + ps_state.poller_state = detail::PollerState::Idle; } #ifdef GR_PICOSCOPE_POLLER_THREAD const auto pollDuration = std::chrono::seconds(1) * ps_settings.streaming_mode_poll_rate; @@ -413,9 +404,9 @@ struct Picoscope : public gr::Block, gr::Supported void stopPollThread() { - state.poller_state = detail::PollerState::Exit; - if (state.poller.joinable()) { - state.poller.join(); + ps_state.poller_state = detail::PollerState::Exit; + if (ps_state.poller.joinable()) { + ps_state.poller.join(); } } @@ -431,7 +422,7 @@ struct Picoscope : public gr::Block, gr::Supported void initialize() { - if (state.initialized) { + if (ps_state.initialized) { return; } @@ -439,24 +430,24 @@ struct Picoscope : public gr::Block, gr::Supported throw std::runtime_error(fmt::format("Initialization failed: {}", ec.message())); } - state.initialized = true; + ps_state.initialized = true; } void close() { self().driver_close(); - state.closed = true; - state.initialized = false; - state.configured = false; + ps_state.closed = true; + ps_state.initialized = false; + ps_state.configured = false; } void configure() { - if (!state.initialized) { + if (!ps_state.initialized) { throw std::runtime_error("Cannot configure without initialization"); } - if (state.armed) { + if (ps_state.armed) { throw std::runtime_error("Cannot configure armed device"); } @@ -464,12 +455,12 @@ struct Picoscope : public gr::Block, gr::Supported throw std::runtime_error(fmt::format("Configuration failed: {}", ec.message())); } - state.configured = true; + ps_state.configured = true; } void arm() { - if (state.armed) { + if (ps_state.armed) { return; } @@ -477,39 +468,39 @@ struct Picoscope : public gr::Block, gr::Supported throw std::runtime_error(fmt::format("Arming failed: {}", ec.message())); } - state.armed = true; + ps_state.armed = true; if (ps_settings.acquisition_mode == AcquisitionMode::Streaming) { - state.poller_state = detail::PollerState::Running; + ps_state.poller_state = detail::PollerState::Running; } } void disarm() noexcept { - if (!state.armed) { + if (!ps_state.armed) { return; } if (ps_settings.acquisition_mode == AcquisitionMode::Streaming) { - state.poller_state = detail::PollerState::Idle; + ps_state.poller_state = detail::PollerState::Idle; } if (const auto ec = self().driver_disarm()) { fmt::println(std::cerr, "disarm failed: {}", ec.message()); } - state.armed = false; + ps_state.armed = false; } void processDriverData(std::size_t nrSamples, std::size_t offset) { std::vector triggerOffsets; - using ChannelOutputRange = decltype(state.channels[0].data_writer.reserve_output_range(1)); + using ChannelOutputRange = decltype(ps_state.channels[0].data_writer.reserve_output_range(1)); std::vector channelOutputs; - channelOutputs.reserve(state.channels.size()); + channelOutputs.reserve(ps_state.channels.size()); - for (std::size_t channelIdx = 0; channelIdx < state.channels.size(); ++channelIdx) { - auto &channel = state.channels[channelIdx]; + for (std::size_t channelIdx = 0; channelIdx < ps_state.channels.size(); ++channelIdx) { + auto &channel = ps_state.channels[channelIdx]; channelOutputs.push_back(channel.data_writer.reserve_output_range(nrSamples)); auto &output = channelOutputs[channelIdx]; @@ -519,7 +510,7 @@ struct Picoscope : public gr::Block, gr::Supported if constexpr (std::is_same_v) { std::copy(driverData.begin(), driverData.end(), output.begin()); } else { - const auto voltageMultiplier = static_cast(channel.settings.range / state.max_value); + const auto voltageMultiplier = static_cast(channel.settings.range / ps_state.max_value); // TODO use SIMD for (std::size_t i = 0; i < nrSamples; ++i) { output[i] = voltageMultiplier * driverData[i]; @@ -540,12 +531,13 @@ struct Picoscope : public gr::Block, gr::Supported triggerTags.reserve(triggerOffsets.size()); for (const auto triggerOffset : triggerOffsets) { - triggerTags.emplace_back(static_cast(state.produced_worker + triggerOffset), gr::property_map{ // TODO use data from timing message - { gr::tag::TRIGGER_NAME, "PPS" }, - { gr::tag::TRIGGER_TIME, static_cast(now.count()) } }); + // raw index is index - 1 + triggerTags.emplace_back(static_cast(ps_state.produced_worker + triggerOffset - 1), gr::property_map{ // TODO use data from timing message + { gr::tag::TRIGGER_NAME, "PPS" }, + { gr::tag::TRIGGER_TIME, static_cast(now.count()) } }); } - for (auto &channel : state.channels) { + for (auto &channel : ps_state.channels) { const auto writeSignalInfo = !channel.signal_info_written; const auto tagsToWrite = triggerTags.size() + (writeSignalInfo ? 1 : 0); if (tagsToWrite == 0) { @@ -553,7 +545,8 @@ struct Picoscope : public gr::Block, gr::Supported } auto writeTags = channel.tag_writer.reserve_output_range(tagsToWrite); if (writeSignalInfo) { - writeTags[0].index = static_cast(state.produced_worker); + // raw index is index - 1 + writeTags[0].index = static_cast(ps_state.produced_worker - 1); writeTags[0].map = channel.signalInfo(); static const auto kSampleRate = std::string(gr::tag::SAMPLE_RATE.key()); writeTags[0].map[kSampleRate] = static_cast(sample_rate); @@ -568,7 +561,7 @@ struct Picoscope : public gr::Block, gr::Supported output.publish(nrSamples); } - state.produced_worker += nrSamples; + ps_state.produced_worker += nrSamples; } void @@ -608,7 +601,7 @@ struct Picoscope : public gr::Block, gr::Supported } if (ps_settings.trigger_once) { - state.data_finished = true; + ps_state.data_finished = true; } } @@ -620,7 +613,7 @@ struct Picoscope : public gr::Block, gr::Supported std::vector triggerOffsets; // relative offset of detected triggers const auto band = triggerChannel.settings.range / 100.; - const auto voltageMultiplier = triggerChannel.settings.range / state.max_value; + const auto voltageMultiplier = triggerChannel.settings.range / ps_state.max_value; const auto toDouble = [&voltageMultiplier](T raw) { if constexpr (std::is_same_v) { @@ -635,21 +628,21 @@ struct Picoscope : public gr::Block, gr::Supported if (ps_settings.trigger.direction == TriggerDirection::Rising || ps_settings.trigger.direction == TriggerDirection::High) { for (std::size_t i = 0; i < samples.size(); i++) { const auto value = toDouble(samples[i]); - if (state.trigger_state == 0 && value >= ps_settings.trigger.threshold) { - state.trigger_state = 1; + if (ps_state.trigger_state == 0 && value >= ps_settings.trigger.threshold) { + ps_state.trigger_state = 1; triggerOffsets.push_back(i); - } else if (state.trigger_state == 1 && value <= ps_settings.trigger.threshold - band) { - state.trigger_state = 0; + } else if (ps_state.trigger_state == 1 && value <= ps_settings.trigger.threshold - band) { + ps_state.trigger_state = 0; } } } else if (ps_settings.trigger.direction == TriggerDirection::Falling || ps_settings.trigger.direction == TriggerDirection::Low) { for (std::size_t i = 0; i < samples.size(); i++) { const auto value = toDouble(samples[i]); - if (state.trigger_state == 1 && value <= ps_settings.trigger.threshold) { - state.trigger_state = 0; + if (ps_state.trigger_state == 1 && value <= ps_settings.trigger.threshold) { + ps_state.trigger_state = 0; triggerOffsets.push_back(i); - } else if (state.trigger_state == 0 && value >= ps_settings.trigger.threshold + band) { - state.trigger_state = 1; + } else if (ps_state.trigger_state == 0 && value >= ps_settings.trigger.threshold + band) { + ps_state.trigger_state = 1; } } } @@ -659,8 +652,8 @@ struct Picoscope : public gr::Block, gr::Supported void reportError(Error ec) { - auto out = state.errors.writer.reserve_output_range(1); - out[0] = { state.produced_worker, ec }; + auto out = ps_state.errors.writer.reserve_output_range(1); + out[0] = { ps_state.produced_worker, ec }; out.publish(1); } diff --git a/blocklib/picoscope/Picoscope4000a.hpp b/blocklib/picoscope/Picoscope4000a.hpp index 7c763229..edfdeb85 100644 --- a/blocklib/picoscope/Picoscope4000a.hpp +++ b/blocklib/picoscope/Picoscope4000a.hpp @@ -225,12 +225,12 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> Error setBuffers(size_t samples, uint32_t blockNumber) { - for (auto &channel : this->state.channels) { + for (auto &channel : this->ps_state.channels) { const auto channelIndex = detail::convertToPs4000aChannel(channel.id); assert(channelIndex); channel.driver_buffer.resize(std::max(samples, channel.driver_buffer.size())); - const auto status = ps4000aSetDataBuffer(this->state.handle, *channelIndex, channel.driver_buffer.data(), static_cast(samples), blockNumber, PS4000A_RATIO_MODE_NONE); + const auto status = ps4000aSetDataBuffer(this->ps_state.handle, *channelIndex, channel.driver_buffer.data(), static_cast(samples), blockNumber, PS4000A_RATIO_MODE_NONE); if (status != PICO_OK) { fmt::println(std::cerr, "ps4000aSetDataBuffer (chan {}): {}", static_cast(*channelIndex), detail::getErrorMessage(status)); @@ -255,7 +255,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> std::string driver_driverVersion() const { const std::string prefix = "PS4000A Linux Driver, "; - auto version = detail::getUnitInfoTopic(this->state.handle, PICO_DRIVER_VERSION); + auto version = detail::getUnitInfoTopic(this->ps_state.handle, PICO_DRIVER_VERSION); if (auto i = version.find(prefix); i != std::string::npos) version.erase(i, prefix.length()); return version; @@ -263,8 +263,8 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> std::string driver_hardwareVersion() const { - if (!this->state.initialized) return {}; - return detail::getUnitInfoTopic(this->state.handle, PICO_HARDWARE_VERSION); + if (!this->ps_state.initialized) return {}; + return detail::getUnitInfoTopic(this->ps_state.handle, PICO_HARDWARE_VERSION); } fair::picoscope::GetValuesResult @@ -275,7 +275,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> auto nrSamples = static_cast(samples); int16_t overflow = 0; - const auto status = ps4000aGetValues(this->state.handle, + const auto status = ps4000aGetValues(this->ps_state.handle, 0, // offset &nrSamples, 1, PS4000A_RATIO_MODE_NONE, static_cast(capture), &overflow); if (status != PICO_OK) { @@ -295,16 +295,16 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> // take any if serial number is not provided (useful for testing purposes) if (this->ps_settings.serial_number.empty()) { - status = ps4000aOpenUnit(&this->state.handle, nullptr); + status = ps4000aOpenUnit(&this->ps_state.handle, nullptr); } else { - status = ps4000aOpenUnit(&this->state.handle, const_cast(reinterpret_cast(this->ps_settings.serial_number.data()))); + status = ps4000aOpenUnit(&this->ps_state.handle, const_cast(reinterpret_cast(this->ps_settings.serial_number.data()))); } // ignore ext. power not connected error/warning if (status == PICO_POWER_SUPPLY_NOT_CONNECTED || status == PICO_USB3_0_DEVICE_NON_USB3_0_PORT) { - status = ps4000aChangePowerSource(this->state.handle, status); + status = ps4000aChangePowerSource(this->ps_state.handle, status); if (status == PICO_POWER_SUPPLY_NOT_CONNECTED || status == PICO_USB3_0_DEVICE_NON_USB3_0_PORT) { - status = ps4000aChangePowerSource(this->state.handle, status); + status = ps4000aChangePowerSource(this->ps_state.handle, status); } } @@ -314,9 +314,9 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> } // maximum value is used for conversion to volts - status = ps4000aMaximumValue(this->state.handle, &this->state.max_value); + status = ps4000aMaximumValue(this->ps_state.handle, &this->ps_state.max_value); if (status != PICO_OK) { - ps4000aCloseUnit(this->state.handle); + ps4000aCloseUnit(this->ps_state.handle); fmt::println(std::cerr, "ps4000aMaximumValue: {}", detail::getErrorMessage(status)); return { status }; } @@ -326,12 +326,12 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> Error driver_close() { - if (this->state.handle == -1) { + if (this->ps_state.handle == -1) { return {}; } - auto status = ps4000aCloseUnit(this->state.handle); - this->state.handle = -1; + auto status = ps4000aCloseUnit(this->ps_state.handle); + this->ps_state.handle = -1; if (status != PICO_OK) { fmt::println(std::cerr, "ps4000aCloseUnit: {}", detail::getErrorMessage(status)); @@ -342,14 +342,14 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> Error driver_configure() { int32_t maxSamples; - auto status = ps4000aMemorySegments(this->state.handle, static_cast(this->ps_settings.rapid_block_nr_captures), &maxSamples); + auto status = ps4000aMemorySegments(this->ps_state.handle, static_cast(this->ps_settings.rapid_block_nr_captures), &maxSamples); if (status != PICO_OK) { fmt::println(std::cerr, "ps4000aMemorySegments: {}", detail::getErrorMessage(status)); return { status }; } if (this->ps_settings.acquisition_mode == AcquisitionMode::RapidBlock) { - status = ps4000aSetNoOfCaptures(this->state.handle, static_cast(this->ps_settings.rapid_block_nr_captures)); + status = ps4000aSetNoOfCaptures(this->ps_state.handle, static_cast(this->ps_settings.rapid_block_nr_captures)); if (status != PICO_OK) { fmt::println(std::cerr, "ps4000aSetNoOfCaptures: {}", detail::getErrorMessage(status)); return { status }; @@ -358,16 +358,16 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> // configure analog channels for (std::size_t i = 0; i <= PS4000A_MAX_CHANNELS; ++i) { - ps4000aSetChannel(this->state.handle, static_cast(i), false, PS4000A_AC, PICO_X10_ACTIVE_PROBE_100MV, 0.); + ps4000aSetChannel(this->ps_state.handle, static_cast(i), false, PS4000A_AC, PICO_X10_ACTIVE_PROBE_100MV, 0.); } - for (const auto &channel : this->state.channels) { + for (const auto &channel : this->ps_state.channels) { const auto idx = detail::convertToPs4000aChannel(channel.id); assert(idx); const auto coupling = detail::convertToPs4000aCoupling(channel.settings.coupling); const auto range = detail::convertToPs4000aRange(channel.settings.range); - status = ps4000aSetChannel(this->state.handle, *idx, true, coupling, static_cast(range), static_cast(channel.settings.offset)); + status = ps4000aSetChannel(this->ps_state.handle, *idx, true, coupling, static_cast(range), static_cast(channel.settings.offset)); if (status != PICO_OK) { fmt::println(std::cerr, "ps4000aSetChannel (chan '{}'): {}", channel.id, detail::getErrorMessage(status)); return { status }; @@ -378,7 +378,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> if (this->ps_settings.trigger.isAnalog() && this->ps_settings.acquisition_mode == AcquisitionMode::RapidBlock) { const auto channel = detail::convertToPs4000aChannel(this->ps_settings.trigger.source); assert(channel); - status = ps4000aSetSimpleTrigger(this->state.handle, + status = ps4000aSetSimpleTrigger(this->ps_state.handle, true, // enable *channel, detail::convertVoltageToPs4000aRawLogicValue(this->ps_settings.trigger.threshold), detail::convertToPs4000aThresholdDirection(this->ps_settings.trigger.direction), @@ -394,7 +394,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> PS4000A_CONDITION cond; cond.source = static_cast(i); cond.condition = PS4000A_CONDITION_DONT_CARE; - status = ps4000aSetTriggerChannelConditions(this->state.handle, &cond, 1, PS4000A_CLEAR); + status = ps4000aSetTriggerChannelConditions(this->ps_state.handle, &cond, 1, PS4000A_CLEAR); if (status != PICO_OK) { fmt::println(std::cerr, "ps4000aSetTriggerChannelConditionsV2: {}", detail::getErrorMessage(status)); return { status }; @@ -404,7 +404,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> // In order to validate desired frequency before startup double actual_freq; - detail::convertFrequencyToPs4000aTimebase(this->state.handle, this->ps_settings.sample_rate, actual_freq); + detail::convertFrequencyToPs4000aTimebase(this->ps_state.handle, this->ps_settings.sample_rate, actual_freq); return {}; } @@ -412,11 +412,11 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> Error driver_arm() { if (this->ps_settings.acquisition_mode == AcquisitionMode::RapidBlock) { - uint32_t timebase = detail::convertFrequencyToPs4000aTimebase(this->state.handle, this->ps_settings.sample_rate, this->state.actual_sample_rate); + uint32_t timebase = detail::convertFrequencyToPs4000aTimebase(this->ps_state.handle, this->ps_settings.sample_rate, this->ps_state.actual_sample_rate); static auto redirector = [](int16_t, PICO_STATUS status, void *vobj) { static_cast(vobj)->rapidBlockCallback({ status }); }; - auto status = ps4000aRunBlock(this->state.handle, static_cast(this->ps_settings.pre_samples), static_cast(this->ps_settings.post_samples), + auto status = ps4000aRunBlock(this->ps_state.handle, static_cast(this->ps_settings.pre_samples), static_cast(this->ps_settings.post_samples), timebase, // timebase nullptr, // time indispossed 0, // segment index @@ -429,9 +429,9 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> using fair::picoscope::detail::kDriverBufferSize; setBuffers(kDriverBufferSize, 0); - auto unit_int = detail::convertFrequencyToPs4000aTimeUnitsAndInterval(this->ps_settings.sample_rate, this->state.actual_sample_rate); + auto unit_int = detail::convertFrequencyToPs4000aTimeUnitsAndInterval(this->ps_settings.sample_rate, this->ps_state.actual_sample_rate); - auto status = ps4000aRunStreaming(this->state.handle, + auto status = ps4000aRunStreaming(this->ps_state.handle, &unit_int.interval, // sample interval unit_int.unit, // time unit of sample interval 0, // pre-triggersamples (unused) @@ -450,7 +450,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> Error driver_disarm() noexcept { - if (const auto status = ps4000aStop(this->state.handle); status != PICO_OK) { + if (const auto status = ps4000aStop(this->ps_state.handle); status != PICO_OK) { fmt::println(std::cerr, "ps4000aStop: {}", detail::getErrorMessage(status)); return { status }; } @@ -468,7 +468,7 @@ struct Picoscope4000a : public fair::picoscope::Picoscope> static_cast(vobj)->streamingCallback(noOfSamples, startIndex, overflow); }; - const auto status = ps4000aGetStreamingLatestValues(this->state.handle, static_cast(redirector), this); + const auto status = ps4000aGetStreamingLatestValues(this->ps_state.handle, static_cast(redirector), this); if (status == PICO_BUSY || status == PICO_DRIVER_FUNCTION) { return {}; } diff --git a/blocklib/picoscope/test/CMakeLists.txt b/blocklib/picoscope/test/CMakeLists.txt index 3bd5e24f..414d5306 100644 --- a/blocklib/picoscope/test/CMakeLists.txt +++ b/blocklib/picoscope/test/CMakeLists.txt @@ -4,7 +4,7 @@ function(add_ut_test_tool TEST_NAME) target_compile_options(${TEST_NAME} PRIVATE -Wall) target_link_options(${TEST_NAME} PRIVATE -Wall) target_include_directories(${TEST_NAME} PRIVATE ${CMAKE_BINARY_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries(${TEST_NAME} PRIVATE gnuradio-core fair-picoscope fair-helpers ut) + target_link_libraries(${TEST_NAME} PRIVATE gnuradio-core gr-testing fair-picoscope fair-helpers ut) endfunction() add_ut_test_tool(qa_Picoscope4000a) diff --git a/blocklib/picoscope/test/qa_Picoscope4000a.cc b/blocklib/picoscope/test/qa_Picoscope4000a.cc index 146eb2f7..61aec7b8 100644 --- a/blocklib/picoscope/test/qa_Picoscope4000a.cc +++ b/blocklib/picoscope/test/qa_Picoscope4000a.cc @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -66,35 +67,31 @@ testStreamingBasics() { { "channel_ranges", std::vector{ 5. } }, { "channel_couplings", std::vector{ "AC_1M" } } } }); - auto &tagTracker = flowGraph.emplaceBlock>(); + auto &tagMonitor = flowGraph.emplaceBlock>(); auto &sink = flowGraph.emplaceBlock>(); - expect(eq(ConnectionResult::SUCCESS, flowGraph.connect<"analog_out", 0>(ps).template to<"in">(tagTracker))); - expect(eq(ConnectionResult::SUCCESS, flowGraph.connect<"out">(tagTracker).template to<"in">(sink))); + expect(eq(ConnectionResult::SUCCESS, flowGraph.connect<"analog_out", 0>(ps).template to<"in">(tagMonitor))); + expect(eq(ConnectionResult::SUCCESS, flowGraph.connect<"out">(tagMonitor).template to<"in">(sink))); // Explicitly start unit because it takes quite some time expect(nothrow([&ps] { ps.start(); })); - // This either hangs or terminates without producing anything if I increase the number of threads - constexpr std::size_t kMinThreads = 2; - constexpr std::size_t kMaxThreads = 2; - scheduler::Simple sched{ std::move(flowGraph), - std::make_shared("simple-scheduler-pool", thread_pool::CPU_BOUND, kMinThreads, kMaxThreads) }; + scheduler::Simple sched{ std::move(flowGraph) }; + sched.init(); sched.start(); std::this_thread::sleep_for(kDuration); - ps.forceQuit(); // needed, otherwise stop() doesn't terminate (no matter if the PS works blocking or not) sched.stop(); - const auto measuredRate = sink.samples_seen / duration(kDuration).count(); + const auto measuredRate = static_cast(sink.samples_seen) / duration(kDuration).count(); fmt::println("Produced in worker: {}", ps.producedWorker()); fmt::println("Configured rate: {}, Measured rate: {} ({:.2f}%), Duration: {} ms", kSampleRate, static_cast(measuredRate), measuredRate / kSampleRate * 100., duration_cast(kDuration).count()); fmt::println("Total: {}", sink.samples_seen); - expect(ge(sink.samples_seen, std::size_t{ 80000 })); - expect(le(sink.samples_seen, std::size_t{ 160000 })); - expect(eq(tagTracker.seen_tags.size(), std::size_t{ 1 })); - const auto &tag = tagTracker.seen_tags[0]; + expect(ge(sink.samples_seen, 80000UZ)); + expect(le(sink.samples_seen, 160000UZ)); + expect(eq(tagMonitor.tags.size(), 1UZ)); + const auto &tag = tagMonitor.tags[0]; expect(eq(tag.index, int64_t{ 0 })); expect(eq(std::get(tag.at(std::string(tag::SAMPLE_RATE.key()))), static_cast(kSampleRate))); expect(eq(std::get(tag.at(std::string(tag::SIGNAL_NAME.key()))), "Test signal"s)); @@ -188,17 +185,12 @@ const boost::ut::suite Picoscope4000aTests = [] { expect(eq(ConnectionResult::SUCCESS, flowGraph.connect<"analog_out", 0>(ps).to<"in">(sink0))); - ps.start(); + scheduler::Simple sched{ std::move(flowGraph) }; + sched.init(); + sched.start(); + std::this_thread::sleep_for(std::chrono::seconds(3)); + sched.stop(); - // TODO tried multi_threaded scheduler with start(); sleep; stop(), something goes - // wrong there (scheduler doesn't shovel data reliably) - scheduler::Simple sched{ std::move(flowGraph) }; - auto quitter = std::async([&ps] { - std::this_thread::sleep_for(std::chrono::seconds(3)); - ps.forceQuit(); - }); - - sched.runAndWait(); expect(ge(sink0.samples_seen, std::size_t{ 2000 })); expect(le(sink0.samples_seen, std::size_t{ 10000 })); };