ngscopeclient 0.1-dev+51fbda87c
Filter.h
Go to the documentation of this file.
1/***********************************************************************************************************************
2* *
3* libscopehal *
4* *
5* Copyright (c) 2012-2024 Andrew D. Zonenberg and contributors *
6* All rights reserved. *
7* *
8* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
9* following conditions are met: *
10* *
11* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the *
12* following disclaimer. *
13* *
14* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the *
15* following disclaimer in the documentation and/or other materials provided with the distribution. *
16* *
17* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
18* derived from this software without specific prior written permission. *
19* *
20* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
21* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
22* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
23* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
24* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
26* POSSIBILITY OF SUCH DAMAGE. *
27* *
28***********************************************************************************************************************/
29
37#ifndef Filter_h
38#define Filter_h
39
40#include "OscilloscopeChannel.h"
41#include "FlowGraphNode.h"
42
43class QueueHandle;
44
52{
53public:
54
57 : m_wfm(nullptr)
58 , m_rev(0)
59 {}
60
67 : m_wfm(wfm)
68 , m_rev(wfm->m_revision)
69 {}
70
71 bool operator==(WaveformBase* wfm)
72 { return (m_wfm == wfm) && (m_rev == wfm->m_revision); }
73
74 bool operator==(WaveformCacheKey wfm)
75 { return (m_wfm == wfm.m_wfm) && (m_rev == wfm.m_rev); }
76
77 bool operator!=(WaveformBase* wfm)
78 { return (m_wfm != wfm) || (m_rev != wfm->m_revision); }
79
80 bool operator!=(WaveformCacheKey wfm)
81 { return (m_wfm != wfm.m_wfm) || (m_rev != wfm.m_rev); }
82
85
87 uint64_t m_rev;
88};
89
95{
96public:
97
99 // Construction and enumeration
100
108 {
109 CAT_ANALYSIS, //Signal integrity analysis
110 CAT_BUS, //Buses
111 CAT_CLOCK, //Clock stuff
112 CAT_MATH, //Basic math functions
113 CAT_MEASUREMENT, //Measurement functions
114 CAT_MEMORY, //Memory buses
115 CAT_SERIAL, //Serial communications
116 CAT_MISC, //anything not otherwise categorized
117 CAT_POWER, //Power analysis
118 CAT_RF, //Frequency domain analysis (FFT etc) and other RF stuff
119 CAT_GENERATION, //Waveform generation and synthesis
120 CAT_EXPORT, //Waveform export
121 CAT_OPTICAL, //Optics
122
123 CAT_COUNT //current max number of categories (will increase over time as more are added)
124 };
125
126 Filter(
127 const std::string& color,
128 Category cat,
129 Unit xunit = Unit::UNIT_FS);
130 virtual ~Filter();
131
133 static std::set<Filter*> GetAllInstances()
134 { return m_filters; }
135
137 static size_t GetNumInstances()
138 { return m_filters.size(); }
139
147 {
148 m_filters.erase(this);
149 m_instanceCount[GetProtocolDisplayName()] --;
150 }
151
152 virtual void ClearStreams() override;
153 virtual size_t AddStream(
154 Unit yunit,
155 const std::string& name,
156 Stream::StreamType stype,
157 uint8_t flags = 0) override;
158
164 void AddProtocolStream(const std::string& name)
165 { AddStream(Unit(Unit::UNIT_COUNTS), name, Stream::STREAM_TYPE_PROTOCOL); }
166
172 void AddDigitalStream(const std::string& name)
173 { AddStream(Unit(Unit::UNIT_COUNTS), name, Stream::STREAM_TYPE_DIGITAL); }
174
176 // Name generation
177
178 virtual void SetDefaultName();
179
185 void UseDefaultName(bool use)
186 {
187 m_usingDefault = use;
188 if(use)
190 }
191
194 { return m_usingDefault; }
195
197 // Reference counting
198
199 virtual void AddRef() override;
200 virtual void Release() override;
201
203 size_t GetRefCount()
204 { return m_refcount; }
205
207 // Accessors
208
211 { return m_category; }
212
213 virtual bool NeedsConfig();
214
218 virtual std::string GetProtocolDisplayName() =0;
219
220public:
226 virtual void ClearSweeps();
227
228 [[deprecated]]
229 virtual void Refresh() override;
230
231 //GPU accelerated refresh method
232 virtual void Refresh(vk::raii::CommandBuffer& cmdBuf, std::shared_ptr<QueueHandle> queue) override;
233
235 // Vertical scaling
236
237 virtual void AutoscaleVertical(size_t stream);
238
239 virtual float GetVoltageRange(size_t stream) override;
240 virtual void SetVoltageRange(float range, size_t stream) override;
241
242 virtual float GetOffset(size_t stream) override;
243 virtual void SetOffset(float offset, size_t stream) override;
244
245protected:
246
248 std::vector<float> m_ranges;
249
251 std::vector<float> m_offsets;
252
253public:
255 // Serialization
256
257 virtual YAML::Node SerializeConfiguration(IDTable& table) override;
258
259 virtual void LoadParameters(const YAML::Node& node, IDTable& table) override;
260 virtual void LoadInputs(const YAML::Node& node, IDTable& table) override;
261
262 virtual bool ShouldPersistWaveform() override;
263
264protected:
265
268
271
272 bool VerifyAllInputsOK(bool allowEmpty = false);
273 bool VerifyInputOK(size_t i, bool allowEmpty = false);
278
279public:
280 static int64_t GetNextEventTimestamp(SparseWaveformBase* wfm, size_t i, size_t len, int64_t timestamp);
281 static int64_t GetNextEventTimestamp(UniformWaveformBase* wfm, size_t i, size_t len, int64_t timestamp);
282
286 static int64_t GetNextEventTimestamp(
287 SparseWaveformBase* swfm, UniformWaveformBase* uwfm, size_t i, size_t len, int64_t timestamp)
288 {
289 if(swfm)
290 return GetNextEventTimestamp(swfm, i, len, timestamp);
291 else
292 return GetNextEventTimestamp(uwfm, i, len, timestamp);
293 }
294
295 static void AdvanceToTimestamp(SparseWaveformBase* wfm, size_t& i, size_t len, int64_t timestamp);
296 static void AdvanceToTimestamp(UniformWaveformBase* wfm, size_t& i, size_t len, int64_t timestamp);
297
298 static void AdvanceToTimestamp(
299 SparseWaveformBase* swfm, UniformWaveformBase* uwfm, size_t& i, size_t len, int64_t timestamp)
300 {
301 if(swfm)
302 AdvanceToTimestamp(swfm, i, len, timestamp);
303 else
304 AdvanceToTimestamp(uwfm, i, len, timestamp);
305 }
306
307 static int64_t GetNextEventTimestampScaled(SparseWaveformBase* wfm, size_t i, size_t len, int64_t timestamp);
308 static int64_t GetNextEventTimestampScaled(UniformWaveformBase* wfm, size_t i, size_t len, int64_t timestamp);
309 static void AdvanceToTimestampScaled(SparseWaveformBase* wfm, size_t& i, size_t len, int64_t timestamp);
310 static void AdvanceToTimestampScaled(UniformWaveformBase* wfm, size_t& i, size_t len, int64_t timestamp);
311
312 static void AdvanceToTimestampScaled(
313 SparseWaveformBase* swfm, UniformWaveformBase* uwfm, size_t& i, size_t len, int64_t timestamp)
314 {
315 if(swfm)
316 AdvanceToTimestampScaled(swfm, i, len, timestamp);
317 else
318 AdvanceToTimestampScaled(uwfm, i, len, timestamp);
319 }
320
321 static int64_t GetNextEventTimestampScaled(
322 SparseWaveformBase* swfm, UniformWaveformBase* uwfm, size_t i, size_t len, int64_t timestamp)
323 {
324 if(swfm)
325 return GetNextEventTimestampScaled(swfm, i, len, timestamp);
326 else
327 return GetNextEventTimestampScaled(uwfm, i, len, timestamp);
328 }
329
330protected:
331 UniformAnalogWaveform* SetupEmptyUniformAnalogOutputWaveform(WaveformBase* din, size_t stream, bool clear=true);
332 SparseAnalogWaveform* SetupEmptySparseAnalogOutputWaveform(WaveformBase* din, size_t stream, bool clear=true);
335 SparseAnalogWaveform* SetupSparseOutputWaveform(SparseWaveformBase* din, size_t stream, size_t skipstart, size_t skipend);
336 SparseDigitalWaveform* SetupSparseDigitalOutputWaveform(SparseWaveformBase* din, size_t stream, size_t skipstart, size_t skipend);
337
338public:
339 //Helpers for sub-sample interpolation
340
350 template<class T>
351 __attribute__((noinline))
352 static float InterpolateTime(T* cap, size_t a, float voltage)
353 {
354 AssertTypeIsAnalogWaveform(cap);
355
356 //If the voltage isn't between the two points, abort
357 float fa = cap->m_samples[a];
358 float fb = cap->m_samples[a+1];
359 bool ag = (fa > voltage);
360 bool bg = (fb > voltage);
361 if( (ag && bg) || (!ag && !bg) )
362 return 0;
363
364 //no need to divide by time, sample spacing is normalized to 1 timebase unit
365 float slope = (fb - fa);
366 float delta = voltage - fa;
367 return delta / slope;
368 }
369
370 static float InterpolateTime(SparseAnalogWaveform* s, UniformAnalogWaveform* u, size_t a, float voltage)
371 {
372 if(s)
373 return InterpolateTime(s, a, voltage);
374 else
375 return InterpolateTime(u, a, voltage);
376 }
377
378 static float InterpolateTime(UniformAnalogWaveform* p, UniformAnalogWaveform* n, size_t a, float voltage);
379 static float InterpolateTime(SparseAnalogWaveform* p, SparseAnalogWaveform* n, size_t a, float voltage);
380
381 static float InterpolateTime(
386 size_t a, float voltage)
387 {
388 if(sp)
389 return InterpolateTime(sp, sn, a, voltage);
390 else
391 return InterpolateTime(up, un, a, voltage);
392 }
393
394 static float InterpolateValue(SparseAnalogWaveform* cap, size_t index, float frac_ticks);
395 static float InterpolateValue(UniformAnalogWaveform* cap, size_t index, float frac_ticks);
396
397 //Helpers for more complex measurements
398 //TODO: create some process for caching this so we don't waste CPU time
399
403 template<class T>
404 __attribute__((noinline))
405 static void GetMinMaxVoltage(T* cap, float& vmin, float& vmax)
406 {
407 AssertTypeIsAnalogWaveform(cap);
408
409 vmin = FLT_MAX;
410 vmax = FLT_MIN;
411 for(float f : cap->m_samples)
412 {
413 if(f < vmin)
414 vmin = f;
415 if(f > vmax)
416 vmax = f;
417 }
418 }
419
423 template<class T>
424 __attribute__((noinline))
425 static float GetMinVoltage(T* cap)
426 {
427 AssertTypeIsAnalogWaveform(cap);
428
429 //Loop over samples and find the minimum
430 float tmp = FLT_MAX;
431 for(float f : cap->m_samples)
432 {
433 if(f < tmp)
434 tmp = f;
435 }
436 return tmp;
437 }
438
443 {
444 if(s)
445 return GetMinVoltage(s);
446 else
447 return GetMinVoltage(u);
448 }
449
453 template<class T>
454 __attribute__((noinline))
455 static float GetMaxVoltage(T* cap)
456 {
457 AssertTypeIsAnalogWaveform(cap);
458
459 //Loop over samples and find the maximum
460 float tmp = -FLT_MAX;
461 for(float f : cap->m_samples)
462 {
463 if(f > tmp)
464 tmp = f;
465 }
466 return tmp;
467 }
468
473 {
474 if(s)
475 return GetMaxVoltage(s);
476 else
477 return GetMaxVoltage(u);
478 }
479
483 template<class T>
484 __attribute__((noinline))
485 static float GetBaseVoltage(T* cap)
486 {
487 AssertTypeIsAnalogWaveform(cap);
488
489 float vmin = GetMinVoltage(cap);
490 float vmax = GetMaxVoltage(cap);
491 float delta = vmax - vmin;
492 const int nbins = 100;
493 auto hist = MakeHistogram(cap, vmin, vmax, nbins);
494
495 //Find the highest peak in the first quarter of the histogram
496 size_t binval = 0;
497 int idx = 0;
498 for(int i=0; i<(nbins/4); i++)
499 {
500 if(hist[i] > binval)
501 {
502 binval = hist[i];
503 idx = i;
504 }
505 }
506
507 float fbin = (idx + 0.5f)/nbins;
508 return fbin*delta + vmin;
509 }
510
515 {
516 if(swfm)
517 return GetBaseVoltage(swfm);
518 else
519 return GetBaseVoltage(uwfm);
520 }
521
525 template<class T>
526 __attribute__((noinline))
527 static float GetTopVoltage(T* cap)
528 {
529 AssertTypeIsAnalogWaveform(cap);
530
531 float vmin = GetMinVoltage(cap);
532 float vmax = GetMaxVoltage(cap);
533 float delta = vmax - vmin;
534 const int nbins = 100;
535 auto hist = MakeHistogram(cap, vmin, vmax, nbins);
536
537 //Find the highest peak in the third quarter of the histogram
538 size_t binval = 0;
539 int idx = 0;
540 for(int i=(nbins*3)/4; i<nbins; i++)
541 {
542 if(hist[i] > binval)
543 {
544 binval = hist[i];
545 idx = i;
546 }
547 }
548
549 float fbin = (idx + 0.5f)/nbins;
550 return fbin*delta + vmin;
551 }
552
557 {
558 if(swfm)
559 return GetTopVoltage(swfm);
560 else
561 return GetTopVoltage(uwfm);
562 }
563
567 template<class T>
568 __attribute__((noinline))
569 static float GetAvgVoltage(T* cap)
570 {
571 AssertTypeIsAnalogWaveform(cap);
572
573 //Loop over samples and find the average
574 //TODO: more numerically stable summation algorithm for deep captures
575 double sum = 0;
576 for(float f : cap->m_samples)
577 sum += f;
578 return sum / cap->m_samples.size();
579 }
580
585 {
586 if(swfm)
587 return GetAvgVoltage(swfm);
588 else
589 return GetAvgVoltage(uwfm);
590 }
591
601 template<class T>
602 __attribute__((noinline))
603 static std::vector<size_t> MakeHistogram(T* cap, float low, float high, size_t bins)
604 {
605 AssertTypeIsAnalogWaveform(cap);
606
607 std::vector<size_t> ret;
608 for(size_t i=0; i<bins; i++)
609 ret.push_back(0);
610
611 //Early out if we have zero span
612 if(bins == 0)
613 return ret;
614
615 float delta = high-low;
616
617 for(float v : cap->m_samples)
618 {
619 float fbin = (v-low) / delta;
620 size_t bin = floor(fbin * bins);
621 if(fbin < 0)
622 bin = 0;
623 else
624 bin = std::min(bin, bins-1);
625 ret[bin] ++;
626 }
627
628 return ret;
629 }
630
640 static std::vector<size_t> MakeHistogram(
641 SparseAnalogWaveform* s, UniformAnalogWaveform* u, float low, float high, size_t bins)
642 {
643 if(s)
644 return MakeHistogram(s, low, high, bins);
645 else
646 return MakeHistogram(u, low, high, bins);
647 }
648
658 template<class T>
659 __attribute__((noinline))
660 static std::vector<size_t> MakeHistogramClipped(T* cap, float low, float high, size_t bins)
661 {
662 AssertTypeIsAnalogWaveform(cap);
663
664 std::vector<size_t> ret;
665 for(size_t i=0; i<bins; i++)
666 ret.push_back(0);
667
668 //Early out if we have zero span
669 if(bins == 0)
670 return ret;
671
672 float delta = high-low;
673
674 for(float v : cap->m_samples)
675 {
676 float fbin = (v-low) / delta;
677 // must cast through a signed int type to avoid UB (e.g. saturates to 0 on arm64) [conv.fpint]
678 size_t bin = static_cast<ssize_t>(floor(fbin * bins));
679 if(bin >= bins) //negative values wrap to huge positive and get caught here
680 continue;
681 ret[bin] ++;
682 }
683
684 return ret;
685 }
686
699 template<class T, class R, class S>
700 __attribute__((noinline))
701 static void SampleOnAnyEdges(T* data, R* clock, SparseWaveform<S>& samples)
702 {
703 //Compile-time check to make sure inputs are correct types
704 AssertTypeIsDigitalWaveform(clock);
705 AssertSampleTypesAreSame(data, &samples);
706
707 samples.clear();
708 samples.SetGpuAccessHint(AcceleratorBuffer<S>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
709 samples.PrepareForCpuAccess();
710
711 //TODO: split up into blocks and multithread?
712 //TODO: AVX vcompress?
713
714 size_t len = clock->size();
715 size_t dlen = data->size();
716
717 size_t ndata = 0;
718 for(size_t i=1; i<len; i++)
719 {
720 //Throw away clock samples until we find an edge
721 if(clock->m_samples[i] == clock->m_samples[i-1])
722 continue;
723
724 //Throw away data samples until the data is synced with us
725 int64_t clkstart = GetOffsetScaled(clock, i);
726 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
727 ndata ++;
728 if(ndata >= dlen)
729 break;
730
731 //Add the new sample
732 samples.m_offsets.push_back(clkstart);
733 samples.m_samples.push_back(data->m_samples[ndata]);
734 }
735
736 //Compute sample durations
737 #ifdef __x86_64__
738 if(g_hasAvx2)
739 FillDurationsAVX2(samples);
740 else
741 #endif
742 FillDurationsGeneric(samples);
743
744 samples.MarkModifiedFromCpu();
745 }
746
759 template<class T>
760 __attribute__((noinline))
761 static void SampleOnAnyEdgesBase(WaveformBase* data, WaveformBase* clock, SparseWaveform<T>& samples)
762 {
763 data->PrepareForCpuAccess();
764 clock->PrepareForCpuAccess();
765 samples.PrepareForCpuAccess();
766
767 auto udata = dynamic_cast<UniformWaveform<T>*>(data);
768 auto sdata = dynamic_cast<SparseWaveform<T>*>(data);
769
770 auto uclock = dynamic_cast<UniformDigitalWaveform*>(clock);
771 auto sclock = dynamic_cast<SparseDigitalWaveform*>(clock);
772
773 if(udata && uclock)
774 SampleOnAnyEdges(udata, uclock, samples);
775 else if(udata && sclock)
776 SampleOnAnyEdges(udata, sclock, samples);
777 else if(sdata && sclock)
778 SampleOnAnyEdges(sdata, sclock, samples);
779 else if(sdata && uclock)
780 SampleOnAnyEdges(sdata, uclock, samples);
781 }
782
795 template<class T, class R, class S>
796 __attribute__((noinline))
797 static void SampleOnRisingEdges(T* data, R* clock, SparseWaveform<S>& samples)
798 {
799 //Compile-time check to make sure inputs are correct types
800 AssertTypeIsDigitalWaveform(clock);
801 AssertTypeIsSparseWaveform(&samples);
802 AssertSampleTypesAreSame(data, &samples);
803
804 samples.clear();
805 samples.SetGpuAccessHint(AcceleratorBuffer<S>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
806
807 //TODO: split up into blocks and multithread?
808 //TODO: AVX vcompress?
809
810 size_t len = clock->size();
811 size_t dlen = data->size();
812
813 size_t ndata = 0;
814 for(size_t i=1; i<len; i++)
815 {
816 //Throw away clock samples until we find a rising edge
817 if(!(clock->m_samples[i] && !clock->m_samples[i-1]))
818 continue;
819
820 //Throw away data samples until the data is synced with us
821 int64_t clkstart = GetOffsetScaled(clock, i);
822 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
823 ndata ++;
824 if(ndata >= dlen)
825 break;
826
827 //Add the new sample
828 samples.m_offsets.push_back(clkstart);
829 samples.m_samples.push_back(data->m_samples[ndata]);
830 }
831
832 //Compute sample durations
833 #ifdef __x86_64__
834 if(g_hasAvx2)
835 FillDurationsAVX2(samples);
836 else
837 #endif
838 FillDurationsGeneric(samples);
839
840 samples.MarkModifiedFromCpu();
841 }
842
855 template<class T>
856 __attribute__((noinline))
857 static void SampleOnRisingEdgesBase(WaveformBase* data, WaveformBase* clock, SparseWaveform<T>& samples)
858 {
859 data->PrepareForCpuAccess();
860 clock->PrepareForCpuAccess();
861 samples.PrepareForCpuAccess();
862
863 auto udata = dynamic_cast<UniformWaveform<T>*>(data);
864 auto sdata = dynamic_cast<SparseWaveform<T>*>(data);
865
866 auto uclock = dynamic_cast<UniformDigitalWaveform*>(clock);
867 auto sclock = dynamic_cast<SparseDigitalWaveform*>(clock);
868
869 if(udata && uclock)
870 SampleOnRisingEdges(udata, uclock, samples);
871 else if(udata && sclock)
872 SampleOnRisingEdges(udata, sclock, samples);
873 else if(sdata && sclock)
874 SampleOnRisingEdges(sdata, sclock, samples);
875 else if(sdata && uclock)
876 SampleOnRisingEdges(sdata, uclock, samples);
877 }
878
891 template<class T, class R, class S>
892 __attribute__((noinline))
893 static void SampleOnFallingEdges(T* data, R* clock, SparseWaveform<S>& samples)
894 {
895 //Compile-time check to make sure inputs are correct types
896 AssertTypeIsDigitalWaveform(clock);
897 AssertTypeIsSparseWaveform(&samples);
898 AssertSampleTypesAreSame(data, &samples);
899
900 samples.clear();
901 samples.SetGpuAccessHint(AcceleratorBuffer<S>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
902
903 //TODO: split up into blocks and multithread?
904 //TODO: AVX vcompress?
905
906 size_t len = clock->size();
907 size_t dlen = data->size();
908
909 size_t ndata = 0;
910 for(size_t i=1; i<len; i++)
911 {
912 //Throw away clock samples until we find a falling edge
913 if(!(!clock->m_samples[i] && clock->m_samples[i-1]))
914 continue;
915
916 //Throw away data samples until the data is synced with us
917 int64_t clkstart = GetOffsetScaled(clock, i);
918 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
919 ndata ++;
920 if(ndata >= dlen)
921 break;
922
923 //Add the new sample
924 samples.m_offsets.push_back(clkstart);
925 samples.m_samples.push_back(data->m_samples[ndata]);
926 }
927
928 //Compute sample durations
929 #ifdef __x86_64__
930 if(g_hasAvx2)
931 FillDurationsAVX2(samples);
932 else
933 #endif
934 FillDurationsGeneric(samples);
935
936 samples.MarkModifiedFromCpu();
937 }
938
950 template<class T, class R>
951 __attribute__((noinline))
952 static void SampleOnAnyEdgesWithInterpolation(T* data, R* clock, SparseAnalogWaveform& samples)
953 {
954 //Compile-time check to make sure inputs are correct types
955 AssertTypeIsAnalogWaveform(data);
956 AssertTypeIsDigitalWaveform(clock);
957
958 samples.clear();
959 samples.SetGpuAccessHint(AcceleratorBuffer<float>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
960
961 //TODO: split up into blocks and multithread?
962 //TODO: AVX vcompress
963
964 size_t len = clock->size();
965 size_t dlen = data->size();
966
967 size_t ndata = 0;
968 for(size_t i=1; i<len; i++)
969 {
970 //Throw away clock samples until we find an edge
971 if(clock->m_samples[i] == clock->m_samples[i-1])
972 continue;
973
974 //Throw away data samples until the data is synced with us
975 int64_t clkstart = GetOffsetScaled(clock, i);
976 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
977 ndata ++;
978 if(ndata >= dlen)
979 break;
980
981 //Find the fractional position of the clock edge
982 int64_t tsample = GetOffsetScaled(data, ndata);
983 int64_t delta = clkstart - tsample;
984 float frac = delta * 1.0 / data->m_timescale;
985
986 //Add the new sample
987 samples.m_offsets.push_back(clkstart);
988 samples.m_samples.push_back(InterpolateValue(data, ndata, frac));
989 }
990
991 //Compute sample durations
992 #ifdef __x86_64__
993 if(g_hasAvx2)
994 FillDurationsAVX2(samples);
995 else
996 #endif
997 FillDurationsGeneric(samples);
998
999 samples.MarkModifiedFromCpu();
1000 }
1001
1014 template<class T>
1015 __attribute__((noinline))
1016 static void SampleOnAnyEdgesBaseWithInterpolation(WaveformBase* data, WaveformBase* clock, SparseWaveform<T>& samples)
1017 {
1018 data->PrepareForCpuAccess();
1019 clock->PrepareForCpuAccess();
1020 samples.PrepareForCpuAccess();
1021
1022 auto udata = dynamic_cast<UniformWaveform<T>*>(data);
1023 auto sdata = dynamic_cast<SparseWaveform<T>*>(data);
1024
1025 auto uclock = dynamic_cast<UniformDigitalWaveform*>(clock);
1026 auto sclock = dynamic_cast<SparseDigitalWaveform*>(clock);
1027
1028 if(udata && uclock)
1029 SampleOnAnyEdgesWithInterpolation(udata, uclock, samples);
1030 else if(udata && sclock)
1031 SampleOnAnyEdgesWithInterpolation(udata, sclock, samples);
1032 else if(sdata && sclock)
1033 SampleOnAnyEdgesWithInterpolation(sdata, sclock, samples);
1034 else if(sdata && uclock)
1035 SampleOnAnyEdgesWithInterpolation(sdata, uclock, samples);
1036 }
1037
1041 template<class T>
1043 {
1044 if(s)
1046 else
1048 }
1049
1053 template<class T>
1055 {
1056 if(s)
1058 else
1060 }
1061
1062 static void FindRisingEdges(UniformAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1063 static void FindRisingEdges(SparseAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1064 static void FindZeroCrossings(SparseAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1065 static void FindZeroCrossings(UniformAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1066 static void FindZeroCrossings(UniformDigitalWaveform* data, std::vector<int64_t>& edges);
1067 static void FindZeroCrossings(SparseDigitalWaveform* data, std::vector<int64_t>& edges);
1068 static void FindRisingEdges(UniformDigitalWaveform* data, std::vector<int64_t>& edges);
1069 static void FindRisingEdges(SparseDigitalWaveform* data, std::vector<int64_t>& edges);
1070 static void FindFallingEdges(UniformDigitalWaveform* data, std::vector<int64_t>& edges);
1071 static void FindFallingEdges(SparseDigitalWaveform* data, std::vector<int64_t>& edges);
1072 static void FindPeaks(UniformAnalogWaveform* data, float peak_threshold, std::vector<int64_t>& peak_indices);
1073 static void FindPeaks(SparseAnalogWaveform* data, float peak_threshold, std::vector<int64_t>& peak_indices);
1074
1075 static void FindZeroCrossingsBase(WaveformBase* data, float threshold, std::vector<int64_t>& edges)
1076 {
1077 auto udata = dynamic_cast<UniformAnalogWaveform*>(data);
1078 auto sdata = dynamic_cast<SparseAnalogWaveform*>(data);
1079
1080 if(udata)
1081 FindZeroCrossings(udata, threshold, edges);
1082 else
1083 FindZeroCrossings(sdata, threshold, edges);
1084 }
1085
1086 static void FindRisingEdges(
1087 SparseDigitalWaveform* sdata, UniformDigitalWaveform* udata, std::vector<int64_t>& edges)
1088 {
1089 if(sdata)
1090 FindRisingEdges(sdata, edges);
1091 else
1092 FindRisingEdges(udata, edges);
1093 }
1094
1095 static void FindFallingEdges(
1096 SparseDigitalWaveform* sdata, UniformDigitalWaveform* udata, std::vector<int64_t>& edges)
1097 {
1098 if(sdata)
1099 FindFallingEdges(sdata, edges);
1100 else
1101 FindFallingEdges(udata, edges);
1102 }
1103
1104 static void FindPeaks(
1105 SparseAnalogWaveform* sdata, UniformAnalogWaveform* udata, float peak_threshold, std::vector<int64_t>& peak_indices)
1106 {
1107 if(sdata)
1108 FindPeaks(sdata, peak_threshold, peak_indices);
1109 else
1110 FindPeaks(udata, peak_threshold, peak_indices);
1111 }
1112
1113 static void FindZeroCrossings(
1114 SparseAnalogWaveform* sdata, UniformAnalogWaveform* udata, float threshold, std::vector<int64_t>& edges)
1115 {
1116 if(sdata)
1117 FindZeroCrossings(sdata, threshold, edges);
1118 else
1119 FindZeroCrossings(udata, threshold, edges);
1120 }
1121
1122 static void FindZeroCrossings(
1123 SparseDigitalWaveform* sdata, UniformDigitalWaveform* udata, std::vector<int64_t>& edges)
1124 {
1125 if(sdata)
1126 FindZeroCrossings(sdata, edges);
1127 else
1128 FindZeroCrossings(udata, edges);
1129 }
1130
1131 static void ClearAnalysisCache();
1132
1133protected:
1134 //Helpers for sparse waveforms
1135 static void FillDurationsGeneric(SparseWaveformBase& wfm);
1136#ifdef __x86_64__
1137 static void FillDurationsAVX2(SparseWaveformBase& wfm);
1138#endif
1139
1140public:
1141 sigc::signal<void()> signal_outputsChanged()
1142 { return m_outputsChangedSignal; }
1143
1144protected:
1146 sigc::signal<void()> m_outputsChangedSignal;
1147
1153 unsigned int m_instanceNum;
1154
1155public:
1156 typedef Filter* (*CreateProcType)(const std::string&);
1157 static void DoAddDecoderClass(const std::string& name, CreateProcType proc);
1158
1159 static void EnumProtocols(std::vector<std::string>& names);
1160 static Filter* CreateFilter(const std::string& protocol, const std::string& color = "#ffffff");
1161
1162protected:
1163 //Class enumeration
1164 typedef std::map< std::string, CreateProcType > CreateMapType;
1165 static CreateMapType m_createprocs;
1166
1167 //Object enumeration
1168 static std::set<Filter*> m_filters;
1169
1170 //Instance naming
1171 static std::map<std::string, unsigned int> m_instanceCount;
1172
1173 //Caching
1174 static std::mutex m_cacheMutex;
1175 static std::map<std::pair<WaveformBase*, float>, std::vector<int64_t> > m_zeroCrossingCache;
1176};
1177
1178#define PROTOCOL_DECODER_INITPROC(T) \
1179 static Filter* CreateInstance(const std::string& color) \
1180 { \
1181 return new T(color); \
1182 } \
1183 virtual std::string GetProtocolDisplayName() override \
1184 { return GetProtocolName(); }
1185
1186#define AddDecoderClass(T) Filter::DoAddDecoderClass(T::GetProtocolName(), T::CreateInstance)
1187
1188#endif
Declaration of FlowGraphNode.
Declaration of OscilloscopeChannel.
A buffer of memory which may be used by GPU acceleration.
Definition: AcceleratorBuffer.h:158
void push_back(const T &value)
Adds a new element to the end of the container, allocating space if needed.
Definition: AcceleratorBuffer.h:720
Abstract base class for all filter graph blocks which are not physical instrument channels.
Definition: Filter.h:95
void AddDigitalStream(const std::string &name)
Helper method for constructors that adds a new STREAM_TYPE_DIGITAL output stream.
Definition: Filter.h:172
static float GetMaxVoltage(SparseAnalogWaveform *s, UniformAnalogWaveform *u)
Gets the lowest voltage of a waveform.
Definition: Filter.h:472
virtual void Refresh() override
Evaluates a filter graph node.
Definition: Filter.cpp:816
virtual size_t AddStream(Unit yunit, const std::string &name, Stream::StreamType stype, uint8_t flags=0) override
Adds a new data stream to the channel.
Definition: Filter.cpp:1513
void UseDefaultName(bool use)
Specifies whether we're using an auto-generated name or not.
Definition: Filter.h:185
bool VerifyAllInputsOKAndSparseOrUniformDigital()
Returns true if every input to the filter is non-NULL and has a non-empty, digital waveform present.
Definition: Filter.cpp:241
SparseDigitalWaveform * SetupEmptySparseDigitalOutputWaveform(WaveformBase *din, size_t stream)
Sets up an digital output waveform and copies basic metadata from the input.
Definition: Filter.cpp:1163
static void FindZeroCrossings(SparseAnalogWaveform *data, float threshold, std::vector< int64_t > &edges)
Find zero crossings in a waveform, interpolating as necessary.
Definition: Filter.cpp:395
__attribute__((noinline)) static float GetAvgVoltage(T *cap)
Gets the average voltage of a waveform.
Definition: Filter.h:568
static void FindRisingEdges(UniformAnalogWaveform *data, float threshold, std::vector< int64_t > &edges)
Find rising edges in a waveform, interpolating to sub-sample resolution as necessary.
Definition: Filter.cpp:318
virtual bool ShouldPersistWaveform() override
Determine whether the channel's waveform(s) should be persisted to a session file.
Definition: Filter.cpp:937
bool VerifyInputOK(size_t i, bool allowEmpty=false)
Returns true if a given input to the filter is non-NULL (and, optionally has a non-empty waveform pre...
Definition: Filter.cpp:130
static int64_t GetNextEventTimestamp(SparseWaveformBase *wfm, size_t i, size_t len, int64_t timestamp)
Gets the timestamp of the next event (if any) on a waveform.
Definition: Filter.cpp:1271
static void PrepareForCpuAccess(SparseWaveform< T > *s, UniformWaveform< T > *u)
Prepares a sparse or uniform analog waveform for CPU access.
Definition: Filter.h:1042
__attribute__((noinline)) static void SampleOnRisingEdges(T *data
Samples a waveform on the rising edges of a clock.
__attribute__((noinline)) static void SampleOnAnyEdges(T *data
Samples a waveform on all edges of a clock.
static std::vector< size_t > MakeHistogram(SparseAnalogWaveform *s, UniformAnalogWaveform *u, float low, float high, size_t bins)
Makes a histogram from a waveform with the specified number of bins.
Definition: Filter.h:640
virtual void ClearStreams() override
Clears out any existing streams.
Definition: Filter.cpp:1506
static void AdvanceToTimestamp(SparseWaveformBase *wfm, size_t &i, size_t len, int64_t timestamp)
Advance the waveform to a given timestamp.
Definition: Filter.cpp:1297
UniformAnalogWaveform * SetupEmptyUniformAnalogOutputWaveform(WaveformBase *din, size_t stream, bool clear=true)
Sets up an analog output waveform and copies basic metadata from the input.
Definition: Filter.cpp:1055
__attribute__((noinline)) static void SampleOnAnyEdgesWithInterpolation(T *data
Samples an analog waveform on all edges of a clock, interpolating linearly to get sub-sample accuracy...
virtual void ClearSweeps()
Clears any integrated data from past triggers (e.g. eye patterns).
Definition: Filter.cpp:80
static size_t GetNumInstances()
Get all currently existing filters.
Definition: Filter.h:137
__attribute__((noinline)) static void SampleOnRisingEdgesBase(WaveformBase *data
Samples a waveform on rising edges of a clock.
static float GetMinVoltage(SparseAnalogWaveform *s, UniformAnalogWaveform *u)
Gets the lowest voltage of a waveform.
Definition: Filter.h:442
SparseAnalogWaveform * SetupEmptySparseAnalogOutputWaveform(WaveformBase *din, size_t stream, bool clear=true)
Sets up an analog output waveform and copies basic metadata from the input.
Definition: Filter.cpp:1092
Category m_category
Category this filter should be displayed under.
Definition: Filter.h:267
static void PrepareForGpuAccess(SparseWaveform< T > *s, UniformWaveform< T > *u)
Prepares a sparse or uniform analog waveform for GPU access.
Definition: Filter.h:1054
bool VerifyAllInputsOKAndSparseDigital()
Returns true if every input to the filter is non-NULL and has a non-empty, sparsely sampled digital w...
Definition: Filter.cpp:217
virtual void SetDefaultName()
Sets the name of a filter based on its inputs.
Definition: Filter.cpp:1374
std::vector< float > m_ranges
Y axis range of each output stream.
Definition: Filter.h:248
void HideFromList()
Removes this filter from the global list.
Definition: Filter.h:146
static std::set< Filter * > GetAllInstances()
Get all currently existing filters.
Definition: Filter.h:133
unsigned int m_instanceNum
Instance number (for auto naming)
Definition: Filter.h:1153
__attribute__((noinline)) static float GetMinVoltage(T *cap)
Gets the lowest voltage of a waveform.
Definition: Filter.h:424
static int64_t GetNextEventTimestampScaled(SparseWaveformBase *wfm, size_t i, size_t len, int64_t timestamp)
Gets the timestamp of the next event (if any) on a waveform.
Definition: Filter.cpp:1318
UniformDigitalWaveform * SetupEmptyUniformDigitalOutputWaveform(WaveformBase *din, size_t stream)
Sets up an digital output waveform and copies basic metadata from the input.
Definition: Filter.cpp:1128
std::vector< float > m_offsets
Y axis offset of each output stream.
Definition: Filter.h:251
bool m_usingDefault
If true, we're using an auto-generated name.
Definition: Filter.h:270
static int64_t GetNextEventTimestamp(SparseWaveformBase *swfm, UniformWaveformBase *uwfm, size_t i, size_t len, int64_t timestamp)
Gets the timestamp of the next sample in a waveform, which may be sparse or uniform.
Definition: Filter.h:286
static void AdvanceToTimestampScaled(SparseWaveformBase *wfm, size_t &i, size_t len, int64_t timestamp)
Advance the waveform to a given timestamp.
Definition: Filter.cpp:1344
__attribute__((noinline)) static std
Makes a histogram from a waveform with the specified number of bins.
Definition: Filter.h:602
static float GetTopVoltage(SparseAnalogWaveform *swfm, UniformAnalogWaveform *uwfm)
Gets the top voltage of a waveform which may be sparse or uniform.
Definition: Filter.h:556
bool IsUsingDefaultName()
Return true if we're using an autogenerated name, false if customized.
Definition: Filter.h:193
Category GetCategory()
Returns the category for displaying this filter in the browser.
Definition: Filter.h:210
__attribute__((noinline)) static float GetBaseVoltage(T *cap)
Gets the most probable "0" level for a digital waveform.
Definition: Filter.h:484
Category
Category the filter should be displayed under in the GUI.
Definition: Filter.h:108
__attribute__((noinline)) static float GetTopVoltage(T *cap)
Gets the most probable "1" level for a digital waveform.
Definition: Filter.h:526
SparseDigitalWaveform * SetupSparseDigitalOutputWaveform(SparseWaveformBase *din, size_t stream, size_t skipstart, size_t skipend)
Sets up a digital output waveform and copies timebase configuration from the input.
Definition: Filter.cpp:1235
__attribute__((noinline)) static void SampleOnAnyEdgesBase(WaveformBase *data
Samples a waveform on all edges of a clock.
__attribute__((noinline)) static float GetMaxVoltage(T *cap)
Gets the highest voltage of a waveform.
Definition: Filter.h:454
static float InterpolateValue(SparseAnalogWaveform *cap, size_t index, float frac_ticks)
Interpolates the actual value of a point between two samples.
Definition: Filter.cpp:1002
virtual void LoadParameters(const YAML::Node &node, IDTable &table) override
Load configuration from a save file.
Definition: Filter.cpp:876
sigc::signal< void()> m_outputsChangedSignal
Signal emitted when the set of output streams changes.
Definition: Filter.h:1146
__attribute__((noinline)) static void GetMinMaxVoltage(T *cap
Gets the lowest and highest voltage of a waveform.
__attribute__((noinline)) static void SampleOnAnyEdgesBaseWithInterpolation(WaveformBase *data
Samples an analog waveform on all edges of a clock, interpolating linearly to get sub-sample accuracy...
static float GetAvgVoltage(SparseAnalogWaveform *swfm, UniformAnalogWaveform *uwfm)
Gets the average voltage of a waveform which may be sparse or uniform.
Definition: Filter.h:584
SparseAnalogWaveform * SetupSparseOutputWaveform(SparseWaveformBase *din, size_t stream, size_t skipstart, size_t skipend)
Sets up an analog output waveform and copies timebase configuration from the input.
Definition: Filter.cpp:1201
virtual std::string GetProtocolDisplayName()=0
Gets the display name of this protocol (for use in menus, save files, etc). Must be unique.
virtual void AutoscaleVertical(size_t stream)
Adjusts gain and offset such that the active waveform occupies the entire vertical area of the plot.
Definition: Filter.cpp:1523
static float GetBaseVoltage(SparseAnalogWaveform *swfm, UniformAnalogWaveform *uwfm)
Gets the base voltage of a waveform which may be sparse or uniform.
Definition: Filter.h:514
size_t GetRefCount()
Returns the current reference count.
Definition: Filter.h:203
bool VerifyAllInputsOKAndUniformAnalog()
Returns true if every input to the filter is non-NULL and has a non-empty, uniformly sampled analog w...
Definition: Filter.cpp:169
__attribute__((noinline)) static void SampleOnFallingEdges(T *data
Samples a waveform on the falling edges of a clock.
virtual YAML::Node SerializeConfiguration(IDTable &table) override
Serializes this trigger's configuration to a YAML string.
Definition: Filter.cpp:836
__attribute__((noinline)) static float InterpolateTime(T *cap
Interpolates the actual time of a threshold crossing between two samples.
bool VerifyAllInputsOKAndSparseAnalog()
Returns true if every input to the filter is non-NULL and has a non-empty, sparsely sampled analog wa...
Definition: Filter.cpp:193
bool VerifyAllInputsOK(bool allowEmpty=false)
Returns true if every input to the filter is non-NULL (and, optionally has a non-empty waveform prese...
Definition: Filter.cpp:155
void AddProtocolStream(const std::string &name)
Helper method for constructors that adds a new STREAM_TYPE_PROTOCOL output stream.
Definition: Filter.h:164
virtual bool NeedsConfig()
Determines if we need to display the configuration / setup dialog.
Definition: Filter.cpp:1494
Bidirectional table mapping integer IDs in scopesession files to object pointers.
Definition: IDTable.h:49
A single channel on an oscilloscope.
Definition: OscilloscopeChannel.h:49
size_t m_refcount
Number of references (channel is disabled when last ref is released)
Definition: OscilloscopeChannel.h:163
Wrapper around a Vulkan Queue, protected by mutex for thread safety.
Definition: QueueManager.h:53
Base class for waveforms with nonuniform sample rate.
Definition: Waveform.h:266
AcceleratorBuffer< int64_t > m_offsets
Start timestamps of each sample, in multiples of m_timescale.
Definition: Waveform.h:289
virtual void MarkModifiedFromCpu() override
Indicates that this waveform's sample data and timestamps have been modified on the CPU and the GPU-s...
Definition: Waveform.h:319
A waveform sampled at irregular intervals.
Definition: Waveform.h:460
virtual void PrepareForCpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:553
virtual void clear() override
Remove all samples from this waveform.
Definition: Waveform.h:546
AcceleratorBuffer< S > m_samples
Sample data.
Definition: Waveform.h:524
virtual void PrepareForGpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:560
void SetGpuAccessHint(enum AcceleratorBuffer< S >::UsageHint hint)
Passes a hint to the memory allocator about where our sample data is expected to be used.
Definition: Waveform.h:578
StreamType
General data type stored in a stream.
Definition: Stream.h:58
Base class for waveforms with data sampled at uniform intervals.
Definition: Waveform.h:337
A waveform sampled at uniform intervals.
Definition: Waveform.h:365
virtual void PrepareForCpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:427
virtual void PrepareForGpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:430
A unit of measurement, plus conversion to pretty-printed output.
Definition: Unit.h:57
Base class for all Waveform specializations.
Definition: Waveform.h:59
uint64_t m_revision
Revision number.
Definition: Waveform.h:139
virtual void PrepareForCpuAccess()=0
Indicates that this waveform is going to be used by the CPU in the near future.
Describes a particular revision of a waveform.
Definition: Filter.h:52
WaveformCacheKey()
Create an empty cache key for a null waveform.
Definition: Filter.h:56
WaveformBase * m_wfm
Pointer to the waveform object.
Definition: Filter.h:84
WaveformCacheKey(WaveformBase *wfm)
Create a cache key for a waveform.
Definition: Filter.h:66
uint64_t m_rev
Definition: Filter.h:87
int64_t GetOffsetScaled(T *wfm, size_t i)
Returns the offset of a sample from the start of the waveform, in X axis units.
Definition: Waveform.h:702