ngscopeclient v0.1-rc1
Filter.h
Go to the documentation of this file.
1/***********************************************************************************************************************
2* *
3* libscopehal *
4* *
5* Copyright (c) 2012-2025 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
710 //If the clock is sparse, assume it probably has edges on every sample and allocate that much buffer to start
711 //(we might overallocate here but it'll be a lot faster)
712 if(dynamic_cast<SparseDigitalWaveform*>(clock) != nullptr)
713 samples.Reserve(clock->size());
714 else
715 {
716 samples.Reserve(1 * 1024 * 1024); //preallocate 1 MB sample buffer to avoid lots of reallocation when small
717 //if it's smaller than this, we won't waste a lot of memory
718 }
719 samples.PrepareForCpuAccess();
720
721 //TODO: split up into blocks and multithread?
722 //TODO: AVX vcompress?
723
724 size_t len = clock->size();
725 size_t dlen = data->size();
726
727 size_t ndata = 0;
728 for(size_t i=1; i<len; i++)
729 {
730 //Throw away clock samples until we find an edge
731 if(clock->m_samples[i] == clock->m_samples[i-1])
732 continue;
733
734 //Throw away data samples until the data is synced with us
735 int64_t clkstart = GetOffsetScaled(clock, i);
736 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
737 ndata ++;
738 if(ndata >= dlen)
739 break;
740
741 //Add the new sample
742 samples.m_offsets.push_back(clkstart);
743 samples.m_samples.push_back(data->m_samples[ndata]);
744 }
745
746 //Compute sample durations
747 #ifdef __x86_64__
748 if(g_hasAvx2)
749 FillDurationsAVX2(samples);
750 else
751 #endif
752 FillDurationsGeneric(samples);
753
754 samples.MarkModifiedFromCpu();
755 }
756
769 template<class T>
770 __attribute__((noinline))
771 static void SampleOnAnyEdgesBase(WaveformBase* data, WaveformBase* clock, SparseWaveform<T>& samples)
772 {
773 data->PrepareForCpuAccess();
774 clock->PrepareForCpuAccess();
775 samples.PrepareForCpuAccess();
776
777 auto udata = dynamic_cast<UniformWaveform<T>*>(data);
778 auto sdata = dynamic_cast<SparseWaveform<T>*>(data);
779
780 auto uclock = dynamic_cast<UniformDigitalWaveform*>(clock);
781 auto sclock = dynamic_cast<SparseDigitalWaveform*>(clock);
782
783 if(udata && uclock)
784 SampleOnAnyEdges(udata, uclock, samples);
785 else if(udata && sclock)
786 SampleOnAnyEdges(udata, sclock, samples);
787 else if(sdata && sclock)
788 SampleOnAnyEdges(sdata, sclock, samples);
789 else if(sdata && uclock)
790 SampleOnAnyEdges(sdata, uclock, samples);
791 }
792
805 template<class T, class R, class S>
806 __attribute__((noinline))
807 static void SampleOnRisingEdges(T* data, R* clock, SparseWaveform<S>& samples)
808 {
809 //Compile-time check to make sure inputs are correct types
810 AssertTypeIsDigitalWaveform(clock);
811 AssertTypeIsSparseWaveform(&samples);
812 AssertSampleTypesAreSame(data, &samples);
813
814 samples.clear();
815 samples.SetGpuAccessHint(AcceleratorBuffer<S>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
816 samples.Reserve(1 * 1024 * 1024); //preallocate 1 MB sample buffer to avoid lots of reallocation when small
817 //if it's smaller than this, we won't waste a lot of memory
818
819 //TODO: split up into blocks and multithread?
820 //TODO: AVX vcompress?
821
822 size_t len = clock->size();
823 size_t dlen = data->size();
824
825 size_t ndata = 0;
826 for(size_t i=1; i<len; i++)
827 {
828 //Throw away clock samples until we find a rising edge
829 if(!(clock->m_samples[i] && !clock->m_samples[i-1]))
830 continue;
831
832 //Throw away data samples until the data is synced with us
833 int64_t clkstart = GetOffsetScaled(clock, i);
834 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
835 ndata ++;
836 if(ndata >= dlen)
837 break;
838
839 //Add the new sample
840 samples.m_offsets.push_back(clkstart);
841 samples.m_samples.push_back(data->m_samples[ndata]);
842 }
843
844 //Compute sample durations
845 #ifdef __x86_64__
846 if(g_hasAvx2)
847 FillDurationsAVX2(samples);
848 else
849 #endif
850 FillDurationsGeneric(samples);
851
852 samples.MarkModifiedFromCpu();
853 }
854
867 template<class T>
868 __attribute__((noinline))
869 static void SampleOnRisingEdgesBase(WaveformBase* data, WaveformBase* clock, SparseWaveform<T>& samples)
870 {
871 data->PrepareForCpuAccess();
872 clock->PrepareForCpuAccess();
873 samples.PrepareForCpuAccess();
874
875 auto udata = dynamic_cast<UniformWaveform<T>*>(data);
876 auto sdata = dynamic_cast<SparseWaveform<T>*>(data);
877
878 auto uclock = dynamic_cast<UniformDigitalWaveform*>(clock);
879 auto sclock = dynamic_cast<SparseDigitalWaveform*>(clock);
880
881 if(udata && uclock)
882 SampleOnRisingEdges(udata, uclock, samples);
883 else if(udata && sclock)
884 SampleOnRisingEdges(udata, sclock, samples);
885 else if(sdata && sclock)
886 SampleOnRisingEdges(sdata, sclock, samples);
887 else if(sdata && uclock)
888 SampleOnRisingEdges(sdata, uclock, samples);
889 }
890
903 template<class T, class R, class S>
904 __attribute__((noinline))
905 static void SampleOnFallingEdges(T* data, R* clock, SparseWaveform<S>& samples)
906 {
907 //Compile-time check to make sure inputs are correct types
908 AssertTypeIsDigitalWaveform(clock);
909 AssertTypeIsSparseWaveform(&samples);
910 AssertSampleTypesAreSame(data, &samples);
911
912 samples.clear();
913 samples.SetGpuAccessHint(AcceleratorBuffer<S>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
914 samples.Reserve(1 * 1024 * 1024); //preallocate 1 MB sample buffer to avoid lots of reallocation when small
915 //if it's smaller than this, we won't waste a lot of memory
916
917 //TODO: split up into blocks and multithread?
918 //TODO: AVX vcompress?
919
920 size_t len = clock->size();
921 size_t dlen = data->size();
922
923 size_t ndata = 0;
924 for(size_t i=1; i<len; i++)
925 {
926 //Throw away clock samples until we find a falling edge
927 if(!(!clock->m_samples[i] && clock->m_samples[i-1]))
928 continue;
929
930 //Throw away data samples until the data is synced with us
931 int64_t clkstart = GetOffsetScaled(clock, i);
932 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
933 ndata ++;
934 if(ndata >= dlen)
935 break;
936
937 //Add the new sample
938 samples.m_offsets.push_back(clkstart);
939 samples.m_samples.push_back(data->m_samples[ndata]);
940 }
941
942 //Compute sample durations
943 #ifdef __x86_64__
944 if(g_hasAvx2)
945 FillDurationsAVX2(samples);
946 else
947 #endif
948 FillDurationsGeneric(samples);
949
950 samples.MarkModifiedFromCpu();
951 }
952
964 template<class T, class R>
965 __attribute__((noinline))
966 static void SampleOnAnyEdgesWithInterpolation(T* data, R* clock, SparseAnalogWaveform& samples)
967 {
968 //Compile-time check to make sure inputs are correct types
969 AssertTypeIsAnalogWaveform(data);
970 AssertTypeIsDigitalWaveform(clock);
971
972 samples.clear();
973 samples.SetGpuAccessHint(AcceleratorBuffer<float>::HINT_NEVER); //assume we're being used as part of a CPU-side filter
974 samples.Reserve(1 * 1024 * 1024); //preallocate 1 MB sample buffer to avoid lots of reallocation when small
975 //if it's smaller than this, we won't waste a lot of memory
976
977 //TODO: split up into blocks and multithread?
978 //TODO: AVX vcompress
979
980 size_t len = clock->size();
981 size_t dlen = data->size();
982
983 size_t ndata = 0;
984 for(size_t i=1; i<len; i++)
985 {
986 //Throw away clock samples until we find an edge
987 if(clock->m_samples[i] == clock->m_samples[i-1])
988 continue;
989
990 //Throw away data samples until the data is synced with us
991 int64_t clkstart = GetOffsetScaled(clock, i);
992 while( (ndata+1 < dlen) && (GetOffsetScaled(data, ndata+1) < clkstart) )
993 ndata ++;
994 if(ndata >= dlen)
995 break;
996
997 //Find the fractional position of the clock edge
998 int64_t tsample = GetOffsetScaled(data, ndata);
999 int64_t delta = clkstart - tsample;
1000 float frac = delta * 1.0 / data->m_timescale;
1001
1002 //Add the new sample
1003 samples.m_offsets.push_back(clkstart);
1004 samples.m_samples.push_back(InterpolateValue(data, ndata, frac));
1005 }
1006
1007 //Compute sample durations
1008 #ifdef __x86_64__
1009 if(g_hasAvx2)
1010 FillDurationsAVX2(samples);
1011 else
1012 #endif
1013 FillDurationsGeneric(samples);
1014
1015 samples.MarkModifiedFromCpu();
1016 }
1017
1030 template<class T>
1031 __attribute__((noinline))
1032 static void SampleOnAnyEdgesBaseWithInterpolation(WaveformBase* data, WaveformBase* clock, SparseWaveform<T>& samples)
1033 {
1034 data->PrepareForCpuAccess();
1035 clock->PrepareForCpuAccess();
1036 samples.PrepareForCpuAccess();
1037
1038 auto udata = dynamic_cast<UniformWaveform<T>*>(data);
1039 auto sdata = dynamic_cast<SparseWaveform<T>*>(data);
1040
1041 auto uclock = dynamic_cast<UniformDigitalWaveform*>(clock);
1042 auto sclock = dynamic_cast<SparseDigitalWaveform*>(clock);
1043
1044 if(udata && uclock)
1045 SampleOnAnyEdgesWithInterpolation(udata, uclock, samples);
1046 else if(udata && sclock)
1047 SampleOnAnyEdgesWithInterpolation(udata, sclock, samples);
1048 else if(sdata && sclock)
1049 SampleOnAnyEdgesWithInterpolation(sdata, sclock, samples);
1050 else if(sdata && uclock)
1051 SampleOnAnyEdgesWithInterpolation(sdata, uclock, samples);
1052 }
1053
1057 template<class T>
1059 {
1060 if(s)
1062 else
1064 }
1065
1069 template<class T>
1071 {
1072 if(s)
1074 else
1076 }
1077
1078 static void FindRisingEdges(UniformAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1079 static void FindRisingEdges(SparseAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1080 static void FindZeroCrossings(SparseAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1081 static void FindZeroCrossings(UniformAnalogWaveform* data, float threshold, std::vector<int64_t>& edges);
1082 static void FindZeroCrossings(UniformDigitalWaveform* data, std::vector<int64_t>& edges);
1083 static void FindZeroCrossings(SparseDigitalWaveform* data, std::vector<int64_t>& edges);
1084 static void FindRisingEdges(UniformDigitalWaveform* data, std::vector<int64_t>& edges);
1085 static void FindRisingEdges(SparseDigitalWaveform* data, std::vector<int64_t>& edges);
1086 static void FindFallingEdges(UniformDigitalWaveform* data, std::vector<int64_t>& edges);
1087 static void FindFallingEdges(SparseDigitalWaveform* data, std::vector<int64_t>& edges);
1088 static void FindPeaks(UniformAnalogWaveform* data, float peak_threshold, std::vector<int64_t>& peak_indices);
1089 static void FindPeaks(SparseAnalogWaveform* data, float peak_threshold, std::vector<int64_t>& peak_indices);
1090
1091 static void FindZeroCrossingsBase(WaveformBase* data, float threshold, std::vector<int64_t>& edges)
1092 {
1093 auto udata = dynamic_cast<UniformAnalogWaveform*>(data);
1094 auto sdata = dynamic_cast<SparseAnalogWaveform*>(data);
1095
1096 if(udata)
1097 FindZeroCrossings(udata, threshold, edges);
1098 else
1099 FindZeroCrossings(sdata, threshold, edges);
1100 }
1101
1102 static void FindRisingEdges(
1103 SparseDigitalWaveform* sdata, UniformDigitalWaveform* udata, std::vector<int64_t>& edges)
1104 {
1105 if(sdata)
1106 FindRisingEdges(sdata, edges);
1107 else
1108 FindRisingEdges(udata, edges);
1109 }
1110
1111 static void FindFallingEdges(
1112 SparseDigitalWaveform* sdata, UniformDigitalWaveform* udata, std::vector<int64_t>& edges)
1113 {
1114 if(sdata)
1115 FindFallingEdges(sdata, edges);
1116 else
1117 FindFallingEdges(udata, edges);
1118 }
1119
1120 static void FindPeaks(
1121 SparseAnalogWaveform* sdata, UniformAnalogWaveform* udata, float peak_threshold, std::vector<int64_t>& peak_indices)
1122 {
1123 if(sdata)
1124 FindPeaks(sdata, peak_threshold, peak_indices);
1125 else
1126 FindPeaks(udata, peak_threshold, peak_indices);
1127 }
1128
1129 static void FindZeroCrossings(
1130 SparseAnalogWaveform* sdata, UniformAnalogWaveform* udata, float threshold, std::vector<int64_t>& edges)
1131 {
1132 if(sdata)
1133 FindZeroCrossings(sdata, threshold, edges);
1134 else
1135 FindZeroCrossings(udata, threshold, edges);
1136 }
1137
1138 static void FindZeroCrossings(
1139 SparseDigitalWaveform* sdata, UniformDigitalWaveform* udata, std::vector<int64_t>& edges)
1140 {
1141 if(sdata)
1142 FindZeroCrossings(sdata, edges);
1143 else
1144 FindZeroCrossings(udata, edges);
1145 }
1146
1147 static void ClearAnalysisCache();
1148
1149 enum FIRFilterType
1150 {
1151 FILTER_TYPE_LOWPASS,
1152 FILTER_TYPE_HIGHPASS,
1153 FILTER_TYPE_BANDPASS,
1154 FILTER_TYPE_NOTCH
1155 };
1156
1157 static void CalculateFIRCoefficients(
1158 float fa,
1159 float fb,
1160 float stopbandAtten,
1161 FIRFilterType type,
1162 AcceleratorBuffer<float>& coefficients);
1163 static float Bessel(float x);
1164
1165protected:
1166 //Helpers for sparse waveforms
1167 static void FillDurationsGeneric(SparseWaveformBase& wfm);
1168#ifdef __x86_64__
1169 static void FillDurationsAVX2(SparseWaveformBase& wfm);
1170#endif
1171
1172public:
1173 sigc::signal<void()> signal_outputsChanged()
1174 { return m_outputsChangedSignal; }
1175
1176protected:
1178 sigc::signal<void()> m_outputsChangedSignal;
1179
1185 unsigned int m_instanceNum;
1186
1187public:
1188 typedef Filter* (*CreateProcType)(const std::string&);
1189 static void DoAddDecoderClass(const std::string& name, CreateProcType proc);
1190
1191 static void EnumProtocols(std::vector<std::string>& names);
1192 static Filter* CreateFilter(const std::string& protocol, const std::string& color = "#ffffff");
1193
1194protected:
1195 //Class enumeration
1196 typedef std::map< std::string, CreateProcType > CreateMapType;
1197 static CreateMapType m_createprocs;
1198
1199 //Object enumeration
1200 static std::set<Filter*> m_filters;
1201
1202 //Instance naming
1203 static std::map<std::string, unsigned int> m_instanceCount;
1204
1205 //Caching
1206 static std::mutex m_cacheMutex;
1207 static std::map<std::pair<WaveformBase*, float>, std::vector<int64_t> > m_zeroCrossingCache;
1208};
1209
1210#define PROTOCOL_DECODER_INITPROC(T) \
1211 static Filter* CreateInstance(const std::string& color) \
1212 { \
1213 return new T(color); \
1214 } \
1215 virtual std::string GetProtocolDisplayName() override \
1216 { return GetProtocolName(); }
1217
1218#define AddDecoderClass(T) Filter::DoAddDecoderClass(T::GetProtocolName(), T::CreateInstance)
1219
1220#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:752
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:823
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:1598
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:1170
__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:944
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:1278
static void PrepareForCpuAccess(SparseWaveform< T > *s, UniformWaveform< T > *u)
Prepares a sparse or uniform analog waveform for CPU access.
Definition: Filter.h:1058
__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:1591
static void AdvanceToTimestamp(SparseWaveformBase *wfm, size_t &i, size_t len, int64_t timestamp)
Advance the waveform to a given timestamp.
Definition: Filter.cpp:1304
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:1062
__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:1099
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:1070
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:1459
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:1185
__attribute__((noinline)) static float GetMinVoltage(T *cap)
Gets the lowest voltage of a waveform.
Definition: Filter.h:424
static float Bessel(float x)
0th order Bessel function
Definition: Filter.cpp:1436
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:1325
UniformDigitalWaveform * SetupEmptyUniformDigitalOutputWaveform(WaveformBase *din, size_t stream)
Sets up an digital output waveform and copies basic metadata from the input.
Definition: Filter.cpp:1135
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:1351
__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:1242
__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:1009
virtual void LoadParameters(const YAML::Node &node, IDTable &table) override
Load configuration from a save file.
Definition: Filter.cpp:883
sigc::signal< void()> m_outputsChangedSignal
Signal emitted when the set of output streams changes.
Definition: Filter.h:1178
__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:1208
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:1608
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:843
__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
static void CalculateFIRCoefficients(float fa, float fb, float stopbandAtten, FIRFilterType type, AcceleratorBuffer< float > &coefficients)
Calculates FIR coefficients.
Definition: Filter.cpp:1388
virtual bool NeedsConfig()
Determines if we need to display the configuration / setup dialog.
Definition: Filter.cpp:1579
Bidirectional table mapping integer IDs in scopesession files to object pointers.
Definition: IDTable.h:51
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:165
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:272
AcceleratorBuffer< int64_t > m_offsets
Start timestamps of each sample, in multiples of m_timescale.
Definition: Waveform.h:295
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:325
A waveform sampled at irregular intervals.
Definition: Waveform.h:469
virtual void Reserve(size_t size) override
Preallocates buffers without changing the usable size of the waveform.
Definition: Waveform.h:552
virtual void PrepareForCpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:569
virtual void clear() override
Remove all samples from this waveform.
Definition: Waveform.h:562
AcceleratorBuffer< S > m_samples
Sample data.
Definition: Waveform.h:533
virtual void PrepareForGpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:576
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:594
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:343
A waveform sampled at uniform intervals.
Definition: Waveform.h:371
virtual void PrepareForCpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:436
virtual void PrepareForGpuAccess() override
Indicates that this waveform is going to be used by the CPU in the near future.
Definition: Waveform.h:439
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:718