ngscopeclient 0.1-dev+51fbda87c
PeakDetectionFilter.h
Go to the documentation of this file.
1/***********************************************************************************************************************
2* *
3* libscopehal v0.1 *
4* *
5* Copyright (c) 2012-2022 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
35#ifndef PeakDetectionFilter_h
36#define PeakDetectionFilter_h
37
38class Peak
39{
40public:
41 Peak(int64_t x, float y, float fwhm)
42 : m_x(x)
43 , m_y(y)
44 , m_fwhm(fwhm)
45 {}
46
47 bool operator<(const Peak& rhs) const
48 { return (m_y < rhs.m_y); }
49
50 int64_t m_x;
51 float m_y;
52 float m_fwhm;
53};
54
56{
57public:
59 virtual ~PeakDetector();
60
61 const std::vector<Peak>& GetPeaks()
62 { return m_peaks; }
63
64 template<class T>
65 __attribute__((noinline))
66 void FindPeaks(T* cap, int64_t max_peaks, float search_hz)
67 {
68 //input must be analog
69 AssertTypeIsAnalogWaveform(cap);
70
71 size_t nouts = cap->size();
72 if( (max_peaks == 0) || (nouts < 2) )
73 m_peaks.clear();
74 else
75 {
76 //We're looking for peaks on the CPU
77 cap->PrepareForCpuAccess();
78
79 //Get peak search width in bins
80 //(assume bins are equal size, this should get us close)
81 int64_t binsize = GetOffsetScaled(cap, 1) - GetOffsetScaled(cap, 0);
82 int64_t search_bins = ceil(search_hz / binsize);
83 int64_t search_rad = search_bins/2;
84 search_rad = std::max(search_rad, (int64_t)1);
85
86 float baseline = Filter::GetMinVoltage(cap);
87
88 //Find peaks (TODO: can we vectorize/multithread this?)
89 std::vector<Peak> peaks;
90 ssize_t nend = nouts-1;
91 size_t minpeak = 10; //Skip this many bins at left to avoid false positives on the DC peak
92 //(TODO: this only makes sense for FFT)
93 for(ssize_t i=minpeak; i<(ssize_t)nouts; i++)
94 {
95 //Locate the peak
96 ssize_t left = std::max((ssize_t)minpeak, (ssize_t)(i - search_rad));
97 ssize_t right = std::min((ssize_t)(i + search_rad), (ssize_t)nend);
98
99 float target = cap->m_samples[i];
100 bool is_peak = true;
101 for(ssize_t j=left; j<=right; j++)
102 {
103 if(i == j)
104 continue;
105 if(cap->m_samples[j] >= target)
106 {
107 //Something higher is to our right.
108 //It's higher than anything from left to j. This makes it a candidate peak.
109 //Restart our search from there.
110 if(j > i)
111 i = j-1;
112
113 is_peak = false;
114 break;
115 }
116 }
117 if(!is_peak)
118 continue;
119
120 //Do a weighted average of our immediate neighbors to fine tune our position
121 ssize_t fine_rad = 10;
122 left = std::max((ssize_t)1, i - fine_rad);
123 right = std::min(i + fine_rad, (ssize_t)nouts-1);
124 //LogDebug("peak range: %zu, %zu\n", left, right);
125 double total = 0;
126 double count = 0;
127 for(ssize_t j=left; j<=right; j++)
128 {
129 total += GetSampleTimesIndex(cap, j);
130 count += cap->m_samples[j];
131 }
132 ssize_t peak_location = round(total / count);
133 //LogDebug("Moved peak from %zu to %zd\n", (size_t)cap->m_offsets[i], peak_location);
134
135 //Move left and right from the peak until we get half magnitude
136 float hmtarget = (target - baseline)/2 + baseline;
137 ssize_t hmleft = target;
138 ssize_t hmright = target;
139 for(ssize_t j=i; j >= 0; j--)
140 {
141 //TODO: interpolate
142 if(cap->m_samples[j] <= hmtarget)
143 {
144 hmleft = j;
145 break;
146 }
147 }
148 for(ssize_t j=i; j < (ssize_t)nouts; j++)
149 {
150 //TODO: interpolate
151 if(cap->m_samples[j] <= hmtarget)
152 {
153 hmright = j;
154 break;
155 }
156 }
157 float fwhm = GetOffsetScaled(cap, hmright) - GetOffsetScaled(cap, hmleft);
158
159 peaks.push_back(Peak(peak_location, target, fwhm));
160
161 //We know we're the highest point until at least i+search_rad.
162 //Don't bother searching those points.
163 i += (search_rad-1);
164 }
165
166 //Sort the peak table and pluck out the requested count
167 std::sort(peaks.rbegin(), peaks.rend(), std::less<Peak>());
168 m_peaks.clear();
169 for(size_t i=0; i<(size_t)max_peaks && i<peaks.size(); i++)
170 m_peaks.push_back(peaks[i]);
171 }
172 }
173
174protected:
175 std::vector<Peak> m_peaks;
176};
177
182 : public Filter
183 , public PeakDetector
184{
185public:
186 PeakDetectionFilter(const std::string& color, Category cat);
187 virtual ~PeakDetectionFilter();
188
189protected:
190
191 template<class T>
192 void FindPeaks(T* cap)
193 {
194 PeakDetector::FindPeaks(
195 cap,
196 m_parameters[m_numpeaksname].GetIntVal(),
197 m_parameters[m_peakwindowname].GetFloatVal());
198 }
199
200 std::string m_numpeaksname;
201 std::string m_peakwindowname;
202};
203
204#endif
205
Abstract base class for all filter graph blocks which are not physical instrument channels.
Definition: Filter.h:95
static float GetMinVoltage(SparseAnalogWaveform *s, UniformAnalogWaveform *u)
Gets the lowest voltage of a waveform.
Definition: Filter.h:442
Category
Category the filter should be displayed under in the GUI.
Definition: Filter.h:108
A filter that does peak detection.
Definition: PeakDetectionFilter.h:184
Definition: PeakDetectionFilter.h:56
Definition: PeakDetectionFilter.h:39
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