]>
Commit | Line | Data |
---|---|---|
bf0edd2b SA |
1 | /* |
2 | * This file is part of the PulseView project. | |
3 | * | |
4 | * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk> | |
5 | * Copyright (C) 2016 Soeren Apel <soeren@apelpie.net> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
efdec55a | 18 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
bf0edd2b SA |
19 | */ |
20 | ||
cbd2a2de | 21 | #include "analog.hpp" |
12ea3616 | 22 | #include "analogsegment.hpp" |
aca9aa83 | 23 | #include "decode/row.hpp" |
cbd2a2de | 24 | #include "logic.hpp" |
12ea3616 | 25 | #include "logicsegment.hpp" |
bf0edd2b | 26 | #include "signalbase.hpp" |
cbd2a2de | 27 | #include "signaldata.hpp" |
bb7dd726 SA |
28 | |
29 | #include <pv/binding/decoder.hpp> | |
aca9aa83 | 30 | #include <pv/session.hpp> |
bf0edd2b | 31 | |
cbd2a2de | 32 | using std::dynamic_pointer_cast; |
12ea3616 | 33 | using std::make_shared; |
bf0edd2b | 34 | using std::shared_ptr; |
12ea3616 | 35 | using std::tie; |
bf0edd2b SA |
36 | |
37 | namespace pv { | |
38 | namespace data { | |
39 | ||
c063290a | 40 | const int SignalBase::ColourBGAlpha = 8 * 256 / 100; |
bcaf0334 | 41 | const uint64_t SignalBase::ConversionBlockSize = 4096; |
bf0edd2b | 42 | |
472a80c5 SA |
43 | SignalBase::SignalBase(shared_ptr<sigrok::Channel> channel, ChannelType channel_type) : |
44 | channel_(channel), | |
12ea3616 SA |
45 | channel_type_(channel_type), |
46 | conversion_type_(NoConversion) | |
bf0edd2b | 47 | { |
050b5a6c SA |
48 | if (channel_) |
49 | internal_name_ = QString::fromStdString(channel_->name()); | |
bf0edd2b SA |
50 | } |
51 | ||
12ea3616 SA |
52 | SignalBase::~SignalBase() |
53 | { | |
54 | // Wait for the currently ongoing conversion to finish | |
55 | if (conversion_thread_.joinable()) | |
56 | conversion_thread_.join(); | |
57 | } | |
58 | ||
bf0edd2b SA |
59 | shared_ptr<sigrok::Channel> SignalBase::channel() const |
60 | { | |
61 | return channel_; | |
62 | } | |
63 | ||
64 | QString SignalBase::name() const | |
65 | { | |
66 | return (channel_) ? QString::fromStdString(channel_->name()) : name_; | |
67 | } | |
68 | ||
050b5a6c SA |
69 | QString SignalBase::internal_name() const |
70 | { | |
71 | return internal_name_; | |
72 | } | |
73 | ||
bf0edd2b SA |
74 | void SignalBase::set_name(QString name) |
75 | { | |
76 | if (channel_) | |
77 | channel_->set_name(name.toUtf8().constData()); | |
78 | ||
79 | name_ = name; | |
80 | ||
81 | name_changed(name); | |
82 | } | |
83 | ||
84 | bool SignalBase::enabled() const | |
85 | { | |
86 | return (channel_) ? channel_->enabled() : true; | |
87 | } | |
88 | ||
89 | void SignalBase::set_enabled(bool value) | |
90 | { | |
91 | if (channel_) { | |
92 | channel_->set_enabled(value); | |
93 | enabled_changed(value); | |
94 | } | |
95 | } | |
96 | ||
472a80c5 | 97 | SignalBase::ChannelType SignalBase::type() const |
bf0edd2b | 98 | { |
472a80c5 | 99 | return channel_type_; |
bf0edd2b SA |
100 | } |
101 | ||
102 | unsigned int SignalBase::index() const | |
103 | { | |
104 | return (channel_) ? channel_->index() : (unsigned int)-1; | |
105 | } | |
106 | ||
107 | QColor SignalBase::colour() const | |
108 | { | |
109 | return colour_; | |
110 | } | |
111 | ||
112 | void SignalBase::set_colour(QColor colour) | |
113 | { | |
114 | colour_ = colour; | |
115 | ||
116 | bgcolour_ = colour; | |
117 | bgcolour_.setAlpha(ColourBGAlpha); | |
118 | ||
119 | colour_changed(colour); | |
120 | } | |
121 | ||
122 | QColor SignalBase::bgcolour() const | |
123 | { | |
124 | return bgcolour_; | |
125 | } | |
126 | ||
cbd2a2de SA |
127 | void SignalBase::set_data(shared_ptr<pv::data::SignalData> data) |
128 | { | |
8ce8ebb9 SA |
129 | if (data_) { |
130 | disconnect(data.get(), SIGNAL(samples_cleared()), | |
12ea3616 | 131 | this, SLOT(on_samples_cleared())); |
8ce8ebb9 | 132 | disconnect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), |
12ea3616 SA |
133 | this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t))); |
134 | } | |
135 | ||
cbd2a2de | 136 | data_ = data; |
12ea3616 | 137 | |
8ce8ebb9 SA |
138 | if (data_) { |
139 | connect(data.get(), SIGNAL(samples_cleared()), | |
12ea3616 | 140 | this, SLOT(on_samples_cleared())); |
8ce8ebb9 | 141 | connect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), |
12ea3616 SA |
142 | this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t))); |
143 | } | |
cbd2a2de SA |
144 | } |
145 | ||
146 | shared_ptr<data::Analog> SignalBase::analog_data() const | |
147 | { | |
12ea3616 SA |
148 | shared_ptr<Analog> result = nullptr; |
149 | ||
472a80c5 | 150 | if (channel_type_ == AnalogChannel) |
12ea3616 SA |
151 | result = dynamic_pointer_cast<Analog>(data_); |
152 | ||
153 | return result; | |
cbd2a2de SA |
154 | } |
155 | ||
156 | shared_ptr<data::Logic> SignalBase::logic_data() const | |
157 | { | |
12ea3616 SA |
158 | shared_ptr<Logic> result = nullptr; |
159 | ||
472a80c5 | 160 | if (channel_type_ == LogicChannel) |
12ea3616 SA |
161 | result = dynamic_pointer_cast<Logic>(data_); |
162 | ||
163 | if (((conversion_type_ == A2LConversionByTreshold) || | |
164 | (conversion_type_ == A2LConversionBySchmittTrigger))) | |
165 | result = dynamic_pointer_cast<Logic>(converted_data_); | |
166 | ||
167 | return result; | |
168 | } | |
169 | ||
170 | void SignalBase::set_conversion_type(ConversionType t) | |
171 | { | |
172 | if (conversion_type_ != NoConversion) { | |
173 | // Wait for the currently ongoing conversion to finish | |
174 | if (conversion_thread_.joinable()) | |
175 | conversion_thread_.join(); | |
176 | ||
177 | // Discard converted data | |
178 | converted_data_.reset(); | |
179 | } | |
180 | ||
181 | conversion_type_ = t; | |
182 | ||
183 | if ((channel_type_ == AnalogChannel) && | |
184 | ((conversion_type_ == A2LConversionByTreshold) || | |
185 | (conversion_type_ == A2LConversionBySchmittTrigger))) { | |
186 | ||
187 | shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_); | |
188 | ||
189 | if (analog_data->analog_segments().size() > 0) { | |
190 | AnalogSegment *asegment = analog_data->analog_segments().front().get(); | |
191 | ||
192 | // Begin conversion of existing sample data | |
193 | // TODO Support for multiple segments is missing | |
194 | on_samples_added(asegment, 0, 0); | |
195 | } | |
196 | } | |
197 | ||
198 | conversion_type_changed(t); | |
cbd2a2de SA |
199 | } |
200 | ||
bb7dd726 SA |
201 | #ifdef ENABLE_DECODE |
202 | bool SignalBase::is_decode_signal() const | |
203 | { | |
47747218 | 204 | return (channel_type_ == DecodeChannel); |
bb7dd726 SA |
205 | } |
206 | #endif | |
cbd2a2de | 207 | |
6de38b17 SA |
208 | void SignalBase::save_settings(QSettings &settings) const |
209 | { | |
210 | settings.setValue("name", name()); | |
211 | settings.setValue("enabled", enabled()); | |
212 | settings.setValue("colour", colour()); | |
12ea3616 | 213 | settings.setValue("conversion_type", (int)conversion_type_); |
6de38b17 SA |
214 | } |
215 | ||
216 | void SignalBase::restore_settings(QSettings &settings) | |
217 | { | |
218 | set_name(settings.value("name").toString()); | |
219 | set_enabled(settings.value("enabled").toBool()); | |
220 | set_colour(settings.value("colour").value<QColor>()); | |
12ea3616 SA |
221 | set_conversion_type((ConversionType)settings.value("conversion_type").toInt()); |
222 | } | |
223 | ||
224 | uint8_t SignalBase::convert_a2l_threshold(float threshold, float value) | |
225 | { | |
226 | return (value >= threshold) ? 1 : 0; | |
227 | } | |
228 | ||
5d9fe823 SA |
229 | uint8_t SignalBase::convert_a2l_schmitt_trigger(float lo_thr, float hi_thr, |
230 | float value, uint8_t &state) | |
12ea3616 | 231 | { |
12ea3616 SA |
232 | if (value < lo_thr) |
233 | state = 0; | |
234 | else if (value > hi_thr) | |
235 | state = 1; | |
236 | ||
237 | return state; | |
238 | } | |
239 | ||
240 | void SignalBase::conversion_thread_proc(QObject* segment, uint64_t start_sample, | |
241 | uint64_t end_sample) | |
242 | { | |
12ea3616 SA |
243 | // TODO Support for multiple segments is missing |
244 | ||
245 | if ((channel_type_ == AnalogChannel) && | |
246 | ((conversion_type_ == A2LConversionByTreshold) || | |
247 | (conversion_type_ == A2LConversionBySchmittTrigger))) { | |
248 | ||
249 | AnalogSegment *asegment = qobject_cast<AnalogSegment*>(segment); | |
250 | ||
251 | // Create the logic data container if needed | |
252 | shared_ptr<Logic> logic_data; | |
253 | if (!converted_data_) { | |
254 | logic_data = make_shared<Logic>(1); // Contains only one channel | |
255 | converted_data_ = logic_data; | |
256 | } else | |
257 | logic_data = dynamic_pointer_cast<Logic>(converted_data_); | |
258 | ||
259 | // Create the initial logic data segment if needed | |
260 | if (logic_data->segments().size() == 0) { | |
261 | shared_ptr<LogicSegment> lsegment = | |
262 | make_shared<LogicSegment>(*logic_data.get(), 1, asegment->samplerate()); | |
263 | logic_data->push_segment(lsegment); | |
264 | } | |
265 | ||
266 | LogicSegment *lsegment = dynamic_cast<LogicSegment*>(logic_data->segments().front().get()); | |
267 | ||
268 | // start_sample=end_sample=0 means we need to figure out the unprocessed range | |
269 | if ((start_sample == 0) && (end_sample == 0)) { | |
270 | start_sample = lsegment->get_sample_count(); | |
271 | end_sample = asegment->get_sample_count(); | |
272 | } | |
273 | ||
274 | if (start_sample == end_sample) | |
275 | return; // Nothing to do | |
276 | ||
277 | float min_v, max_v; | |
278 | tie(min_v, max_v) = asegment->get_min_max(); | |
279 | ||
280 | vector<uint8_t> lsamples; | |
bcaf0334 | 281 | lsamples.reserve(ConversionBlockSize); |
12ea3616 SA |
282 | |
283 | uint64_t i = start_sample; | |
284 | ||
285 | if (conversion_type_ == A2LConversionByTreshold) { | |
286 | const float threshold = (min_v + max_v) * 0.5; // middle between min and max | |
287 | ||
288 | // Convert as many sample blocks as we can | |
bcaf0334 SA |
289 | while ((end_sample - i) > ConversionBlockSize) { |
290 | const float* asamples = asegment->get_samples(i, i + ConversionBlockSize); | |
291 | for (uint32_t j = 0; j < ConversionBlockSize; j++) | |
12ea3616 SA |
292 | lsamples.push_back(convert_a2l_threshold(threshold, asamples[j])); |
293 | lsegment->append_payload(lsamples.data(), lsamples.size()); | |
bcaf0334 | 294 | i += ConversionBlockSize; |
12ea3616 SA |
295 | lsamples.clear(); |
296 | delete[] asamples; | |
297 | } | |
298 | ||
299 | // Convert remaining samples | |
300 | const float* asamples = asegment->get_samples(i, end_sample); | |
301 | for (uint32_t j = 0; j < (end_sample - i); j++) | |
302 | lsamples.push_back(convert_a2l_threshold(threshold, asamples[j])); | |
303 | lsegment->append_payload(lsamples.data(), lsamples.size()); | |
304 | delete[] asamples; | |
eae3bbbb SA |
305 | |
306 | samples_added(lsegment, start_sample, end_sample); | |
12ea3616 SA |
307 | } |
308 | ||
309 | if (conversion_type_ == A2LConversionBySchmittTrigger) { | |
310 | const float amplitude = max_v - min_v; | |
311 | const float lo_thr = min_v + (amplitude * 0.1); // 10% above min | |
312 | const float hi_thr = max_v - (amplitude * 0.1); // 10% below max | |
5d9fe823 | 313 | uint8_t state = 0; // TODO Use value of logic sample n-1 instead of 0 |
12ea3616 SA |
314 | |
315 | // Convert as many sample blocks as we can | |
bcaf0334 SA |
316 | while ((end_sample - i) > ConversionBlockSize) { |
317 | const float* asamples = asegment->get_samples(i, i + ConversionBlockSize); | |
318 | for (uint32_t j = 0; j < ConversionBlockSize; j++) | |
5d9fe823 | 319 | lsamples.push_back(convert_a2l_schmitt_trigger(lo_thr, hi_thr, asamples[j], state)); |
12ea3616 | 320 | lsegment->append_payload(lsamples.data(), lsamples.size()); |
bcaf0334 | 321 | i += ConversionBlockSize; |
12ea3616 SA |
322 | lsamples.clear(); |
323 | delete[] asamples; | |
324 | } | |
325 | ||
326 | // Convert remaining samples | |
327 | const float* asamples = asegment->get_samples(i, end_sample); | |
328 | for (uint32_t j = 0; j < (end_sample - i); j++) | |
5d9fe823 | 329 | lsamples.push_back(convert_a2l_schmitt_trigger(lo_thr, hi_thr, asamples[j], state)); |
12ea3616 SA |
330 | lsegment->append_payload(lsamples.data(), lsamples.size()); |
331 | delete[] asamples; | |
eae3bbbb SA |
332 | |
333 | samples_added(lsegment, start_sample, end_sample); | |
12ea3616 SA |
334 | } |
335 | } | |
336 | } | |
337 | ||
338 | void SignalBase::on_samples_cleared() | |
339 | { | |
340 | if (converted_data_) | |
341 | converted_data_->clear(); | |
eae3bbbb SA |
342 | |
343 | samples_cleared(); | |
12ea3616 SA |
344 | } |
345 | ||
346 | void SignalBase::on_samples_added(QObject* segment, uint64_t start_sample, | |
347 | uint64_t end_sample) | |
348 | { | |
12ea3616 SA |
349 | if (conversion_type_ != NoConversion) { |
350 | ||
351 | // Wait for the currently ongoing conversion to finish | |
352 | if (conversion_thread_.joinable()) | |
353 | conversion_thread_.join(); | |
354 | ||
355 | conversion_thread_ = std::thread( | |
356 | &SignalBase::conversion_thread_proc, this, | |
357 | segment, start_sample, end_sample); | |
358 | } | |
eae3bbbb SA |
359 | |
360 | samples_added(segment, start_sample, end_sample); | |
12ea3616 SA |
361 | } |
362 | ||
363 | void SignalBase::on_capture_state_changed(int state) | |
364 | { | |
365 | return; | |
366 | if (state == Session::Stopped) { | |
367 | // Make sure that all data is converted | |
368 | ||
369 | if ((channel_type_ == AnalogChannel) && | |
370 | ((conversion_type_ == A2LConversionByTreshold) || | |
371 | (conversion_type_ == A2LConversionBySchmittTrigger))) { | |
372 | ||
373 | shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_); | |
374 | ||
375 | if (analog_data->analog_segments().size() > 0) { | |
376 | // TODO Support for multiple segments is missing | |
377 | AnalogSegment *asegment = analog_data->analog_segments().front().get(); | |
378 | ||
379 | if (conversion_thread_.joinable()) | |
380 | conversion_thread_.join(); | |
381 | ||
382 | conversion_thread_ = std::thread( | |
383 | &SignalBase::conversion_thread_proc, this, asegment, 0, 0); | |
384 | } | |
385 | } | |
386 | } | |
6de38b17 SA |
387 | } |
388 | ||
bf0edd2b SA |
389 | } // namespace data |
390 | } // namespace pv |