Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions blocklib/picoscope/include/fair/picoscope/Picoscope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct Picoscope : gr::Block<Picoscope<T, TPSImpl, TTagMatcher>, gr::SupportedTy
A<bool, "Enable digital inputs"> digital_port_enable = false; // only used if digital ports are available: 3000a, 5000a series
A<bool, "invert digital port output"> digital_port_invert_output = false; // only used if digital ports are available: 3000a, 5000a series
A<gr::Size_t, "Timeout after which to not match trigger pulses", gr::Unit<"ns">> matcher_timeout = 10'000'000z;
A<std::uint64_t, "Polling interval for streaming mode", gr::Unit<"ns">> polling_interval = 0ULL;
A<bool, "verbose console"> verbose_console = false;

gr::PortIn<std::uint8_t, gr::Async> timingIn;
Expand All @@ -151,8 +152,11 @@ struct Picoscope : gr::Block<Picoscope<T, TPSImpl, TTagMatcher>, gr::SupportedTy
detail::TriggerNameAndCtx _armTriggerNameAndCtx; // store parsed information to optimise performance
detail::TriggerNameAndCtx _disarmTriggerNameAndCtx;

GR_MAKE_REFLECTABLE(Picoscope, timingIn, out, digitalOut, serial_number, sample_rate, pre_samples, post_samples, n_captures, auto_arm, trigger_once, channel_ids, signal_names, signal_units, signal_quantities, //
channel_ranges, channel_analog_offsets, signal_scales, signal_offsets, channel_couplings, trigger_source, trigger_threshold, trigger_direction, digital_port_enable, digital_port_invert_output, trigger_arm, trigger_disarm, matcher_timeout, verbose_console);
std::chrono::steady_clock::time_point _lastTry; // saves the timestamp of the last polling

GR_MAKE_REFLECTABLE(Picoscope, timingIn, out, digitalOut, serial_number, sample_rate, pre_samples, post_samples, n_captures, auto_arm, trigger_once, channel_ids, signal_names, signal_units, signal_quantities, //
channel_ranges, channel_analog_offsets, signal_scales, signal_offsets, channel_couplings, trigger_source, trigger_threshold, trigger_direction, digital_port_enable, digital_port_invert_output, trigger_arm, //
trigger_disarm, matcher_timeout, verbose_console, polling_interval);

private:
std::optional<PicoscopeWrapper<TPSImpl>> _picoscope;
Expand All @@ -174,6 +178,14 @@ struct Picoscope : gr::Block<Picoscope<T, TPSImpl, TTagMatcher>, gr::SupportedTy
template<gr::OutputSpanLike TOutSpan>
requires(acquisitionMode == AcquisitionMode::Streaming)
gr::work::Status processBulk(gr::InputSpanLike auto& timingInSpan, std::span<TOutSpan>& outputs, gr::OutputSpanLike auto& digitalOutSpan) {
if (polling_interval != 0ULL) {
const auto intervalNs = std::chrono::nanoseconds{polling_interval.value};
if (_lastTry + intervalNs > std::chrono::steady_clock::now()) {
return gr::work::Status::OK;
}
_lastTry = std::chrono::steady_clock::now();
}

std::size_t nSamples = 0UZ;
std::size_t samplesDropped = 0UZ;
const std::size_t availableBuffer = std::min(std::ranges::min(outputs | std::views::transform(&TOutSpan::size)), digitalOutSpan.size());
Expand Down
8 changes: 4 additions & 4 deletions blocklib/picoscope/include/fair/picoscope/TimingMatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ struct TimingMatcher {
break; // tag will be moved outside the current data chunk and has to be handled in the next iteration
}
if (realignedTag) {
result.processedSamples = std::max(result.processedSamples, realignedTag->index);
result.processedSamples = std::max(result.processedSamples, realignedTag->index + 1);
pushTagOrdered(std::move(*realignedTag), "realign/no-trigger-left");
} else {
result.messages.emplace_back(std::format("Failed to realign tag relative to last matched trigger: {}", currentTag));
Expand Down Expand Up @@ -220,7 +220,7 @@ struct TimingMatcher {
if (realignedTag->index >= nSamples) {
break;
}
result.processedSamples = std::max(result.processedSamples, realignedTag->index);
result.processedSamples = std::max(result.processedSamples, realignedTag->index + 1);
pushTagOrdered(std::move(*realignedTag), "realign/no-hw-or-flank-too-far");
} else {
result.messages.emplace_back(std::format("Failed to realign tag relative to last matched trigger: {}", currentTag));
Expand All @@ -234,7 +234,7 @@ struct TimingMatcher {

if ((currentFlankTime + timeout) < currentTagLocalTime) { // hw edge without corresponding event
pushTagOrdered(createUnknownEventTag(currentFlankIndex, currentFlankTime), "unknown-event/no-matching-tag");
result.processedSamples = std::max(result.processedSamples, currentFlankIndex);
result.processedSamples = std::max(result.processedSamples, currentFlankIndex + 1);
triggerIndex++; // skip outdated timing message(s)
continue;
}
Expand All @@ -259,7 +259,7 @@ struct TimingMatcher {
result.processedTags++;
}
pushTagOrdered(getOffsetAdjustedTag(currentFlankIndex, currentTagOffset, currentTag), "regular-match");
result.processedSamples = std::max(result.processedSamples, currentFlankIndex);
result.processedSamples = std::max(result.processedSamples, currentFlankIndex + 1);
result.processedTags++;
triggerIndex++;
} // (result.processedTags < tags.size() || triggerIndex < triggerSampleIndices.size())
Expand Down
28 changes: 27 additions & 1 deletion blocklib/picoscope/test/qa_TimingMatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ const boost::ut::suite<"TimingMatchers"> TimingMatcherTests = [] {
auto result = matcher.match(tags, triggerSampleIndices, 8UZ, std::chrono::nanoseconds(acqTimestamp));

expect(eq(2UZ, result.processedTags));
expect(eq(4UZ, result.processedSamples)); // processed up to the last matching tag
expect(eq(5UZ, result.processedSamples)); // processed up to the last matching tag
expectRangesEquals(
std::vector<gr::Tag>{
{1, generateTimingTag("EVT_CMD1"s, acqTimestamp + 1'000, 0.0f, true)},
Expand Down Expand Up @@ -604,6 +604,32 @@ const boost::ut::suite<"TimingMatchers"> TimingMatcherTests = [] {
expect(eq(110UZ, result.tags[3].index));
expect(std::ranges::any_of(result.messages, [](const auto& m) { return m.contains("Dropping unordered tag"); }));
};

"processedSamples test"_test = [&] {
using namespace boost::ut;

unsigned long acqTimestamp = 123456789;

// timeout=10us, sampleRate=1e6 -> maxDelaySamples = 10, nSamples=250 -> safeSamples = 240
constexpr std::size_t nSamples = 250UZ;
constexpr std::size_t safeIndex = 240UZ;
constexpr std::uint64_t tagTimeNs = 240'000; // 240 samples * 1000ns @ 1MHz

std::vector<gr::property_map> tags{
generateTimingTag("EVT_BOUNDARY"s, acqTimestamp + tagTimeNs, 0.0f, true),
};
std::vector<std::size_t> triggerSampleIndices{safeIndex};

TimingMatcher matcher{.timeout = 10us, .sampleRate = 1e6f};
auto result = matcher.match(tags, triggerSampleIndices, nSamples, std::chrono::nanoseconds(acqTimestamp));

expect(eq(1UZ, result.processedTags));
expect(eq(1UZ, result.tags.size()));
expect(eq(safeIndex, result.tags[0].index));

expect(eq(safeIndex + 1UZ, result.processedSamples));
expect(result.tags[0].index < result.processedSamples);
};
};
} // namespace fair::picoscope::test

Expand Down
Loading