]>
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; |
27a3f09b | 36 | using std::unique_lock; |
bf0edd2b SA |
37 | |
38 | namespace pv { | |
39 | namespace data { | |
40 | ||
c063290a | 41 | const int SignalBase::ColourBGAlpha = 8 * 256 / 100; |
bcaf0334 | 42 | const uint64_t SignalBase::ConversionBlockSize = 4096; |
bf0edd2b | 43 | |
472a80c5 SA |
44 | SignalBase::SignalBase(shared_ptr<sigrok::Channel> channel, ChannelType channel_type) : |
45 | channel_(channel), | |
12ea3616 SA |
46 | channel_type_(channel_type), |
47 | conversion_type_(NoConversion) | |
bf0edd2b | 48 | { |
050b5a6c SA |
49 | if (channel_) |
50 | internal_name_ = QString::fromStdString(channel_->name()); | |
bf0edd2b SA |
51 | } |
52 | ||
12ea3616 SA |
53 | SignalBase::~SignalBase() |
54 | { | |
27a3f09b | 55 | stop_conversion(); |
12ea3616 SA |
56 | } |
57 | ||
bf0edd2b SA |
58 | shared_ptr<sigrok::Channel> SignalBase::channel() const |
59 | { | |
60 | return channel_; | |
61 | } | |
62 | ||
63 | QString SignalBase::name() const | |
64 | { | |
65 | return (channel_) ? QString::fromStdString(channel_->name()) : name_; | |
66 | } | |
67 | ||
050b5a6c SA |
68 | QString SignalBase::internal_name() const |
69 | { | |
70 | return internal_name_; | |
71 | } | |
72 | ||
bf0edd2b SA |
73 | void SignalBase::set_name(QString name) |
74 | { | |
75 | if (channel_) | |
76 | channel_->set_name(name.toUtf8().constData()); | |
77 | ||
78 | name_ = name; | |
79 | ||
80 | name_changed(name); | |
81 | } | |
82 | ||
83 | bool SignalBase::enabled() const | |
84 | { | |
85 | return (channel_) ? channel_->enabled() : true; | |
86 | } | |
87 | ||
88 | void SignalBase::set_enabled(bool value) | |
89 | { | |
90 | if (channel_) { | |
91 | channel_->set_enabled(value); | |
92 | enabled_changed(value); | |
93 | } | |
94 | } | |
95 | ||
472a80c5 | 96 | SignalBase::ChannelType SignalBase::type() const |
bf0edd2b | 97 | { |
472a80c5 | 98 | return channel_type_; |
bf0edd2b SA |
99 | } |
100 | ||
101 | unsigned int SignalBase::index() const | |
102 | { | |
27a3f09b SA |
103 | return (channel_) ? channel_->index() : 0; |
104 | } | |
105 | ||
106 | unsigned int SignalBase::logic_bit_index() const | |
107 | { | |
108 | if (channel_type_ == LogicChannel) | |
109 | return channel_->index(); | |
110 | else | |
111 | return 0; | |
bf0edd2b SA |
112 | } |
113 | ||
114 | QColor SignalBase::colour() const | |
115 | { | |
116 | return colour_; | |
117 | } | |
118 | ||
119 | void SignalBase::set_colour(QColor colour) | |
120 | { | |
121 | colour_ = colour; | |
122 | ||
123 | bgcolour_ = colour; | |
124 | bgcolour_.setAlpha(ColourBGAlpha); | |
125 | ||
126 | colour_changed(colour); | |
127 | } | |
128 | ||
129 | QColor SignalBase::bgcolour() const | |
130 | { | |
131 | return bgcolour_; | |
132 | } | |
133 | ||
cbd2a2de SA |
134 | void SignalBase::set_data(shared_ptr<pv::data::SignalData> data) |
135 | { | |
8ce8ebb9 SA |
136 | if (data_) { |
137 | disconnect(data.get(), SIGNAL(samples_cleared()), | |
12ea3616 | 138 | this, SLOT(on_samples_cleared())); |
8ce8ebb9 | 139 | disconnect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), |
12ea3616 SA |
140 | this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t))); |
141 | } | |
142 | ||
cbd2a2de | 143 | data_ = data; |
12ea3616 | 144 | |
8ce8ebb9 SA |
145 | if (data_) { |
146 | connect(data.get(), SIGNAL(samples_cleared()), | |
12ea3616 | 147 | this, SLOT(on_samples_cleared())); |
8ce8ebb9 | 148 | connect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), |
12ea3616 SA |
149 | this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t))); |
150 | } | |
cbd2a2de SA |
151 | } |
152 | ||
153 | shared_ptr<data::Analog> SignalBase::analog_data() const | |
154 | { | |
12ea3616 SA |
155 | shared_ptr<Analog> result = nullptr; |
156 | ||
472a80c5 | 157 | if (channel_type_ == AnalogChannel) |
12ea3616 SA |
158 | result = dynamic_pointer_cast<Analog>(data_); |
159 | ||
160 | return result; | |
cbd2a2de SA |
161 | } |
162 | ||
163 | shared_ptr<data::Logic> SignalBase::logic_data() const | |
164 | { | |
12ea3616 SA |
165 | shared_ptr<Logic> result = nullptr; |
166 | ||
472a80c5 | 167 | if (channel_type_ == LogicChannel) |
12ea3616 SA |
168 | result = dynamic_pointer_cast<Logic>(data_); |
169 | ||
170 | if (((conversion_type_ == A2LConversionByTreshold) || | |
171 | (conversion_type_ == A2LConversionBySchmittTrigger))) | |
172 | result = dynamic_pointer_cast<Logic>(converted_data_); | |
173 | ||
174 | return result; | |
175 | } | |
176 | ||
06b6ce26 SA |
177 | SignalBase::ConversionType SignalBase::get_conversion_type() const |
178 | { | |
179 | return conversion_type_; | |
180 | } | |
181 | ||
12ea3616 SA |
182 | void SignalBase::set_conversion_type(ConversionType t) |
183 | { | |
184 | if (conversion_type_ != NoConversion) { | |
27a3f09b | 185 | stop_conversion(); |
12ea3616 SA |
186 | |
187 | // Discard converted data | |
188 | converted_data_.reset(); | |
1b56c646 | 189 | samples_cleared(); |
12ea3616 SA |
190 | } |
191 | ||
192 | conversion_type_ = t; | |
193 | ||
ccccb914 SA |
194 | // Re-create an empty container |
195 | // so that the signal is recognized as providing logic data | |
196 | // and thus can be assigned to a decoder | |
197 | if (conversion_is_a2l()) | |
198 | if (!converted_data_) | |
199 | converted_data_ = make_shared<Logic>(1); // Contains only one channel | |
200 | ||
27a3f09b | 201 | start_conversion(); |
12ea3616 SA |
202 | |
203 | conversion_type_changed(t); | |
cbd2a2de SA |
204 | } |
205 | ||
bb7dd726 SA |
206 | #ifdef ENABLE_DECODE |
207 | bool SignalBase::is_decode_signal() const | |
208 | { | |
47747218 | 209 | return (channel_type_ == DecodeChannel); |
bb7dd726 SA |
210 | } |
211 | #endif | |
cbd2a2de | 212 | |
6de38b17 SA |
213 | void SignalBase::save_settings(QSettings &settings) const |
214 | { | |
215 | settings.setValue("name", name()); | |
216 | settings.setValue("enabled", enabled()); | |
217 | settings.setValue("colour", colour()); | |
12ea3616 | 218 | settings.setValue("conversion_type", (int)conversion_type_); |
6de38b17 SA |
219 | } |
220 | ||
221 | void SignalBase::restore_settings(QSettings &settings) | |
222 | { | |
223 | set_name(settings.value("name").toString()); | |
224 | set_enabled(settings.value("enabled").toBool()); | |
225 | set_colour(settings.value("colour").value<QColor>()); | |
12ea3616 SA |
226 | set_conversion_type((ConversionType)settings.value("conversion_type").toInt()); |
227 | } | |
228 | ||
ccccb914 SA |
229 | bool SignalBase::conversion_is_a2l() const |
230 | { | |
231 | return ((channel_type_ == AnalogChannel) && | |
232 | ((conversion_type_ == A2LConversionByTreshold) || | |
233 | (conversion_type_ == A2LConversionBySchmittTrigger))); | |
234 | } | |
235 | ||
27a3f09b | 236 | void SignalBase::conversion_thread_proc(QObject* segment) |
12ea3616 | 237 | { |
12ea3616 SA |
238 | // TODO Support for multiple segments is missing |
239 | ||
27a3f09b SA |
240 | uint64_t start_sample, end_sample; |
241 | start_sample = end_sample = 0; | |
12ea3616 | 242 | |
27a3f09b | 243 | do { |
ccccb914 | 244 | if (conversion_is_a2l()) { |
27a3f09b SA |
245 | |
246 | AnalogSegment *asegment = qobject_cast<AnalogSegment*>(segment); | |
247 | ||
ccccb914 | 248 | const shared_ptr<Logic> logic_data = dynamic_pointer_cast<Logic>(converted_data_); |
27a3f09b SA |
249 | |
250 | // Create the initial logic data segment if needed | |
251 | if (logic_data->segments().size() == 0) { | |
252 | shared_ptr<LogicSegment> lsegment = | |
253 | make_shared<LogicSegment>(*logic_data.get(), 1, asegment->samplerate()); | |
254 | logic_data->push_segment(lsegment); | |
255 | } | |
12ea3616 | 256 | |
27a3f09b | 257 | LogicSegment *lsegment = dynamic_cast<LogicSegment*>(logic_data->segments().front().get()); |
12ea3616 | 258 | |
12ea3616 SA |
259 | start_sample = lsegment->get_sample_count(); |
260 | end_sample = asegment->get_sample_count(); | |
12ea3616 | 261 | |
27a3f09b SA |
262 | if (end_sample > start_sample) { |
263 | float min_v, max_v; | |
264 | tie(min_v, max_v) = asegment->get_min_max(); | |
265 | ||
419ec4e1 SA |
266 | // Create sigrok::Analog instance |
267 | float *asamples = new float[ConversionBlockSize]; | |
268 | uint8_t *lsamples = new uint8_t[ConversionBlockSize]; | |
269 | ||
270 | vector<shared_ptr<sigrok::Channel> > channels; | |
271 | channels.push_back(channel_); | |
272 | ||
273 | vector<const sigrok::QuantityFlag*> mq_flags; | |
274 | const sigrok::Quantity * const mq = sigrok::Quantity::VOLTAGE; | |
275 | const sigrok::Unit * const unit = sigrok::Unit::VOLT; | |
276 | ||
277 | shared_ptr<sigrok::Packet> packet = | |
278 | Session::sr_context->create_analog_packet(channels, | |
279 | asamples, ConversionBlockSize, mq, unit, mq_flags); | |
280 | ||
281 | shared_ptr<sigrok::Analog> analog = | |
282 | dynamic_pointer_cast<sigrok::Analog>(packet->payload()); | |
27a3f09b | 283 | |
419ec4e1 | 284 | // Convert |
27a3f09b SA |
285 | uint64_t i = start_sample; |
286 | ||
287 | if (conversion_type_ == A2LConversionByTreshold) { | |
288 | const float threshold = (min_v + max_v) * 0.5; // middle between min and max | |
289 | ||
290 | // Convert as many sample blocks as we can | |
291 | while ((end_sample - i) > ConversionBlockSize) { | |
b82243f7 | 292 | asegment->get_samples(i, i + ConversionBlockSize, asamples); |
419ec4e1 SA |
293 | |
294 | shared_ptr<sigrok::Logic> logic = | |
295 | analog->get_logic_via_threshold(threshold, lsamples); | |
296 | ||
297 | lsegment->append_payload(logic->data_pointer(), logic->data_length()); | |
298 | ||
27a3f09b SA |
299 | samples_added(lsegment, i, i + ConversionBlockSize); |
300 | i += ConversionBlockSize; | |
27a3f09b SA |
301 | } |
302 | ||
419ec4e1 SA |
303 | // Re-create sigrok::Analog and convert remaining samples |
304 | packet = Session::sr_context->create_analog_packet(channels, | |
305 | asamples, end_sample - i, mq, unit, mq_flags); | |
306 | ||
307 | analog = dynamic_pointer_cast<sigrok::Analog>(packet->payload()); | |
308 | ||
b82243f7 | 309 | asegment->get_samples(i, end_sample, asamples); |
419ec4e1 SA |
310 | shared_ptr<sigrok::Logic> logic = |
311 | analog->get_logic_via_threshold(threshold, lsamples); | |
312 | lsegment->append_payload(logic->data_pointer(), logic->data_length()); | |
27a3f09b | 313 | samples_added(lsegment, i, end_sample); |
27a3f09b SA |
314 | } |
315 | ||
316 | if (conversion_type_ == A2LConversionBySchmittTrigger) { | |
317 | const float amplitude = max_v - min_v; | |
5e95d3d9 SA |
318 | const float center = min_v + (amplitude / 2); |
319 | const float lo_thr = center - (amplitude * 0.15); // 15% margin | |
320 | const float hi_thr = center + (amplitude * 0.15); // 15% margin | |
27a3f09b SA |
321 | uint8_t state = 0; // TODO Use value of logic sample n-1 instead of 0 |
322 | ||
323 | // Convert as many sample blocks as we can | |
324 | while ((end_sample - i) > ConversionBlockSize) { | |
b82243f7 | 325 | asegment->get_samples(i, i + ConversionBlockSize, asamples); |
419ec4e1 SA |
326 | |
327 | shared_ptr<sigrok::Logic> logic = | |
328 | analog->get_logic_via_schmitt_trigger(lo_thr, hi_thr, | |
329 | &state, lsamples); | |
330 | ||
331 | lsegment->append_payload(logic->data_pointer(), logic->data_length()); | |
332 | ||
27a3f09b SA |
333 | samples_added(lsegment, i, i + ConversionBlockSize); |
334 | i += ConversionBlockSize; | |
27a3f09b SA |
335 | } |
336 | ||
419ec4e1 SA |
337 | // Re-create sigrok::Analog and convert remaining samples |
338 | packet = Session::sr_context->create_analog_packet(channels, | |
339 | asamples, end_sample - i, mq, unit, mq_flags); | |
340 | ||
341 | analog = dynamic_pointer_cast<sigrok::Analog>(packet->payload()); | |
342 | ||
b82243f7 | 343 | asegment->get_samples(i, end_sample, asamples); |
419ec4e1 SA |
344 | shared_ptr<sigrok::Logic> logic = |
345 | analog->get_logic_via_schmitt_trigger(lo_thr, hi_thr, | |
346 | &state, lsamples); | |
347 | lsegment->append_payload(logic->data_pointer(), logic->data_length()); | |
27a3f09b | 348 | samples_added(lsegment, i, end_sample); |
27a3f09b SA |
349 | } |
350 | ||
351 | // If acquisition is ongoing, start-/endsample may have changed | |
352 | end_sample = asegment->get_sample_count(); | |
b82243f7 | 353 | |
419ec4e1 | 354 | delete[] lsamples; |
b82243f7 | 355 | delete[] asamples; |
12ea3616 | 356 | } |
27a3f09b | 357 | } |
12ea3616 | 358 | |
27a3f09b SA |
359 | if (!conversion_interrupt_ && (start_sample == end_sample)) { |
360 | unique_lock<mutex> input_lock(conversion_input_mutex_); | |
361 | conversion_input_cond_.wait(input_lock); | |
12ea3616 | 362 | } |
27a3f09b SA |
363 | } while (!conversion_interrupt_); |
364 | } | |
12ea3616 | 365 | |
27a3f09b SA |
366 | void SignalBase::start_conversion() |
367 | { | |
368 | stop_conversion(); | |
369 | ||
ccccb914 | 370 | if (conversion_is_a2l()) { |
27a3f09b | 371 | shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_); |
12ea3616 | 372 | |
27a3f09b SA |
373 | if (analog_data->analog_segments().size() > 0) { |
374 | // TODO Support for multiple segments is missing | |
375 | AnalogSegment *asegment = analog_data->analog_segments().front().get(); | |
eae3bbbb | 376 | |
27a3f09b SA |
377 | conversion_interrupt_ = false; |
378 | conversion_thread_ = std::thread( | |
379 | &SignalBase::conversion_thread_proc, this, asegment); | |
12ea3616 SA |
380 | } |
381 | } | |
382 | } | |
383 | ||
27a3f09b SA |
384 | void SignalBase::stop_conversion() |
385 | { | |
386 | // Stop conversion so we can restart it from the beginning | |
387 | conversion_interrupt_ = true; | |
388 | conversion_input_cond_.notify_one(); | |
389 | if (conversion_thread_.joinable()) | |
390 | conversion_thread_.join(); | |
391 | } | |
392 | ||
12ea3616 SA |
393 | void SignalBase::on_samples_cleared() |
394 | { | |
395 | if (converted_data_) | |
396 | converted_data_->clear(); | |
eae3bbbb SA |
397 | |
398 | samples_cleared(); | |
12ea3616 SA |
399 | } |
400 | ||
401 | void SignalBase::on_samples_added(QObject* segment, uint64_t start_sample, | |
402 | uint64_t end_sample) | |
403 | { | |
12ea3616 | 404 | if (conversion_type_ != NoConversion) { |
27a3f09b SA |
405 | if (conversion_thread_.joinable()) { |
406 | // Notify the conversion thread since it's running | |
407 | conversion_input_cond_.notify_one(); | |
408 | } else { | |
409 | // Start the conversion thread | |
410 | start_conversion(); | |
411 | } | |
12ea3616 | 412 | } |
eae3bbbb SA |
413 | |
414 | samples_added(segment, start_sample, end_sample); | |
12ea3616 SA |
415 | } |
416 | ||
417 | void SignalBase::on_capture_state_changed(int state) | |
418 | { | |
27a3f09b SA |
419 | if (state == Session::Running) { |
420 | if (conversion_type_ != NoConversion) { | |
421 | // Restart conversion | |
422 | stop_conversion(); | |
423 | start_conversion(); | |
12ea3616 SA |
424 | } |
425 | } | |
6de38b17 SA |
426 | } |
427 | ||
bf0edd2b SA |
428 | } // namespace data |
429 | } // namespace pv |