2 * This file is part of the PulseView project.
4 * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "storesession.hpp"
24 #include <pv/data/analog.hpp>
25 #include <pv/data/analogsegment.hpp>
26 #include <pv/data/logic.hpp>
27 #include <pv/data/logicsegment.hpp>
28 #include <pv/data/signalbase.hpp>
29 #include <pv/devicemanager.hpp>
30 #include <pv/devices/device.hpp>
31 #include <pv/session.hpp>
33 #include <libsigrokcxx/libsigrokcxx.hpp>
37 using std::lock_guard
;
43 using std::shared_ptr
;
45 using std::unordered_set
;
48 using Glib::VariantBase
;
50 using sigrok::ConfigKey
;
52 using sigrok::OutputFormat
;
53 using sigrok::OutputFlag
;
57 const size_t StoreSession::BlockSize
= 10 * 1024 * 1024;
59 StoreSession::StoreSession(const string
&file_name
,
60 const shared_ptr
<OutputFormat
> &output_format
,
61 const map
<string
, VariantBase
> &options
,
62 const pair
<uint64_t, uint64_t> sample_range
,
63 const Session
&session
) :
64 file_name_(file_name
),
65 output_format_(output_format
),
67 sample_range_(sample_range
),
75 StoreSession::~StoreSession()
80 pair
<int, int> StoreSession::progress() const
82 return make_pair(units_stored_
.load(), unit_count_
.load());
85 const QString
& StoreSession::error() const
87 lock_guard
<mutex
> lock(mutex_
);
91 bool StoreSession::start()
93 const unordered_set
< shared_ptr
<data::SignalBase
> > sigs(session_
.signalbases());
95 shared_ptr
<data::Segment
> any_segment
;
96 shared_ptr
<data::LogicSegment
> lsegment
;
97 vector
< shared_ptr
<data::SignalBase
> > achannel_list
;
98 vector
< shared_ptr
<data::AnalogSegment
> > asegment_list
;
100 for (shared_ptr
<data::SignalBase
> signal
: sigs
) {
101 if (!signal
->enabled())
104 if (signal
->type() == data::SignalBase::LogicChannel
) {
105 // All logic channels share the same data segments
106 shared_ptr
<data::Logic
> ldata
= signal
->logic_data();
108 const deque
< shared_ptr
<data::LogicSegment
> > &lsegments
=
109 ldata
->logic_segments();
111 if (lsegments
.empty()) {
112 error_
= tr("Can't save logic channel without data.");
116 lsegment
= lsegments
.front();
117 any_segment
= lsegment
;
120 if (signal
->type() == data::SignalBase::AnalogChannel
) {
121 // Each analog channel has its own segments
122 shared_ptr
<data::Analog
> adata
= signal
->analog_data();
124 const deque
< shared_ptr
<data::AnalogSegment
> > &asegments
=
125 adata
->analog_segments();
127 if (asegments
.empty()) {
128 error_
= tr("Can't save analog channel without data.");
132 asegment_list
.push_back(asegments
.front());
133 any_segment
= asegments
.front();
135 achannel_list
.push_back(signal
);
140 error_
= tr("No channels enabled.");
144 // Check whether the user wants to export a certain sample range
147 if (sample_range_
.first
== sample_range_
.second
) {
149 sample_count_
= any_segment
->get_sample_count();
151 if (sample_range_
.first
> sample_range_
.second
) {
152 start_sample_
= sample_range_
.second
;
153 end_sample
= min(sample_range_
.first
, any_segment
->get_sample_count());
154 sample_count_
= end_sample
- start_sample_
;
156 start_sample_
= sample_range_
.first
;
157 end_sample
= min(sample_range_
.second
, any_segment
->get_sample_count());
158 sample_count_
= end_sample
- start_sample_
;
164 const auto context
= session_
.device_manager().context();
165 auto device
= session_
.device()->device();
167 map
<string
, Glib::VariantBase
> options
= options_
;
169 if (!output_format_
->test_flag(OutputFlag::INTERNAL_IO_HANDLING
))
170 output_stream_
.open(file_name_
, ios_base::binary
|
171 ios_base::trunc
| ios_base::out
);
173 output_
= output_format_
->create_output(file_name_
, device
, options
);
174 auto meta
= context
->create_meta_packet(
175 {{ConfigKey::SAMPLERATE
, Glib::Variant
<guint64
>::create(
176 any_segment
->samplerate())}});
177 output_
->receive(meta
);
178 } catch (Error error
) {
179 error_
= tr("Error while saving: ") + error
.what();
183 thread_
= std::thread(&StoreSession::store_proc
, this,
184 achannel_list
, asegment_list
, lsegment
);
188 void StoreSession::wait()
190 if (thread_
.joinable())
194 void StoreSession::cancel()
199 void StoreSession::store_proc(vector
< shared_ptr
<data::SignalBase
> > achannel_list
,
200 vector
< shared_ptr
<data::AnalogSegment
> > asegment_list
,
201 shared_ptr
<data::LogicSegment
> lsegment
)
203 unsigned progress_scale
= 0;
207 unsigned int lsamples_per_block
= INT_MAX
;
208 unsigned int asamples_per_block
= INT_MAX
;
210 if (!asegment_list
.empty()) {
211 // We assume all analog channels use the sample unit size
212 aunit_size
= asegment_list
.front()->unit_size();
213 asamples_per_block
= BlockSize
/ aunit_size
;
216 lunit_size
= lsegment
->unit_size();
217 lsamples_per_block
= BlockSize
/ lunit_size
;
220 // Qt needs the progress values to fit inside an int. If they would
221 // not, scale the current and max values down until they do.
222 while ((sample_count_
>> progress_scale
) > INT_MAX
)
225 unit_count_
= sample_count_
>> progress_scale
;
227 const unsigned int samples_per_block
=
228 min(asamples_per_block
, lsamples_per_block
);
230 while (!interrupt_
&& sample_count_
) {
233 const uint64_t packet_len
=
234 min((uint64_t)samples_per_block
, sample_count_
);
237 const auto context
= session_
.device_manager().context();
239 for (unsigned int i
= 0; i
< achannel_list
.size(); i
++) {
240 shared_ptr
<sigrok::Channel
> achannel
= (achannel_list
.at(i
))->channel();
241 shared_ptr
<data::AnalogSegment
> asegment
= asegment_list
.at(i
);
244 asegment
->get_samples(start_sample_
, start_sample_
+ packet_len
);
246 auto analog
= context
->create_analog_packet(
247 vector
<shared_ptr
<sigrok::Channel
> >{achannel
},
248 (float *)adata
, packet_len
,
249 sigrok::Quantity::VOLTAGE
, sigrok::Unit::VOLT
,
250 vector
<const sigrok::QuantityFlag
*>());
251 const string adata_str
= output_
->receive(analog
);
253 if (output_stream_
.is_open())
254 output_stream_
<< adata_str
;
260 const uint8_t* ldata
=
261 lsegment
->get_samples(start_sample_
, start_sample_
+ packet_len
);
263 const size_t length
= packet_len
* lunit_size
;
264 auto logic
= context
->create_logic_packet((void*)ldata
, length
, lunit_size
);
265 const string ldata_str
= output_
->receive(logic
);
267 if (output_stream_
.is_open())
268 output_stream_
<< ldata_str
;
272 } catch (Error error
) {
273 error_
= tr("Error while saving: ") + error
.what();
277 sample_count_
-= packet_len
;
278 start_sample_
+= packet_len
;
279 units_stored_
= unit_count_
- (sample_count_
>> progress_scale
);
282 // Zeroing the progress variables indicates completion
283 units_stored_
= unit_count_
= 0;
289 output_stream_
.close();