ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvWaveform.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ecvWaveform.h"
9 
10 // Qt
11 #include <QtCompat.h>
12 
13 #include <QDataStream>
14 #include <QFile>
15 
17  : numberOfSamples(0),
18  samplingRate_ps(0),
19  digitizerGain(0),
20  digitizerOffset(0),
21  bitsPerSample(0) {}
22 
24  return d.bitsPerSample != bitsPerSample ||
29 }
30 
31 bool WaveformDescriptor::toFile(QFile& out, short dataVersion) const {
32  // Version validation
33  if (dataVersion < 44) {
34  assert(false);
35  return false;
36  }
37 
38  QDataStream outStream(&out);
39 
40  // dataVersion >= 44
41  outStream << numberOfSamples;
42  outStream << samplingRate_ps;
43  outStream << digitizerGain;
44  outStream << digitizerOffset;
45  outStream << bitsPerSample;
46 
47  return true;
48 }
49 
50 short WaveformDescriptor::minimumFileVersion() const { return 44; }
51 
53  short dataVersion,
54  int flags,
55  LoadedIDMap& oldToNewIDMap) {
56  QDataStream inStream(&in);
57 
58  if (dataVersion < 44) return false;
59 
60  // dataVersion >= 44
61  inStream >> numberOfSamples;
62  inStream >> samplingRate_ps;
63  inStream >> digitizerGain;
64  inStream >> digitizerOffset;
65  inStream >> bitsPerSample;
66 
67  return true;
68 }
69 
70 ccWaveform::ccWaveform(uint8_t descriptorID /*=0*/)
71  : m_byteCount(0),
72  m_dataOffset(0),
73  m_beamDir(0, 0, 0),
74  m_echoTime_ps(0),
75  m_descriptorID(descriptorID),
76  m_returnIndex(1) {}
77 
78 void ccWaveform::setDataDescription(uint64_t dataOffset, uint32_t byteCount) {
81 }
82 
83 uint32_t ccWaveform::getRawSample(uint32_t i,
84  const WaveformDescriptor& descriptor,
85  const uint8_t* dataStorage) const {
86  if (!dataStorage) {
87  assert(false);
88  return 0;
89  }
90 
91  const uint8_t* _data = data(dataStorage);
92  switch (descriptor.bitsPerSample) {
93  case 8:
94  return _data[i];
95 
96  case 16:
97  return reinterpret_cast<const uint16_t*>(_data)[i];
98 
99  case 24: {
100  uint32_t v = *reinterpret_cast<const uint32_t*>(_data + 3 * i);
101  //'hide' the 4th byte
102  static const uint32_t Byte4Mask = 0x0FFF;
103  v &= Byte4Mask;
104  return v;
105  }
106 
107  case 32:
108  return reinterpret_cast<const uint32_t*>(_data)[i];
109 
110  default: // other 'strange' bps values ;)
111  {
112  uint32_t firstBitIndex = descriptor.bitsPerSample * i;
113  uint32_t firstByteIndex = (firstBitIndex >> 3); // = divide by 8
114 
115  uint32_t lastBitIndex =
116  firstBitIndex + descriptor.bitsPerSample - 1;
117  uint32_t lastByteIndex = (lastBitIndex >> 3); // = divide by 8
118  if (lastByteIndex >= m_byteCount) {
119  assert(false);
120  return 0;
121  }
122 
123  // last byte (may be the first one!)
124  uint32_t value = _data[lastByteIndex];
125  {
126  // number of bits used in the current byte
127  uint32_t r = ((lastByteIndex + 1) % 8);
128  if (r != 0) {
129  // we keep only the used bits
130  value &= ((1 << r) - 1);
131  }
132  }
133 
134  // other bytes (if any)
135  while (lastByteIndex != firstByteIndex) {
136  --lastByteIndex;
137  value <<= 8;
138  value |= _data[lastByteIndex];
139  }
140 
141  // remove the first unused bits (if any)
142  {
143  uint32_t r = firstBitIndex - 8 * firstByteIndex;
144  if (r != 0) {
145  value >>= r;
146  }
147  }
148 
149  return value;
150  }
151  }
152 
153  // we should never arrive here
154  assert(false);
155  return 0;
156 }
157 
158 double ccWaveform::getSample(uint32_t i,
159  const WaveformDescriptor& descriptor,
160  const uint8_t* dataStorage) const {
161  uint32_t raw = getRawSample(i, descriptor, dataStorage);
162 
163  return descriptor.digitizerGain * raw + descriptor.digitizerOffset;
164 }
165 
166 bool ccWaveform::decodeSamples(std::vector<double>& values,
167  const WaveformDescriptor& descriptor,
168  const uint8_t* dataStorage) const {
169  try {
170  values.resize(descriptor.numberOfSamples);
171  for (uint32_t i = 0; i < descriptor.numberOfSamples; ++i) {
172  values[i] = getSample(i, descriptor, dataStorage);
173  }
174  } catch (const std::bad_alloc&) {
175  // not enough memory
176  return false;
177  }
178 
179  return true;
180 }
181 
182 bool ccWaveform::toASCII(const QString& filename,
183  const WaveformDescriptor& descriptor,
184  const uint8_t* dataStorage) const {
185  if (descriptor.numberOfSamples == 0) {
186  assert(false);
187  return false;
188  }
189 
190  std::vector<double> values;
191  if (!decodeSamples(values, descriptor, dataStorage)) {
192  CVLog::Warning(QString("[ccWaveform::toASCII] Not enough memory"));
193  return false;
194  }
195 
196  return ToASCII(filename, values, descriptor.samplingRate_ps);
197 }
198 
199 bool ccWaveform::ToASCII(const QString& filename,
200  std::vector<double>& values,
201  uint32_t samplingRate_ps) {
202  QFile file(filename);
203  if (!file.open(QFile::Text | QFile::WriteOnly)) {
204  CVLog::Warning(QString("[ccWaveform::toASCII] Failed to open file '%1' "
205  "for writing")
206  .arg(filename));
207  return false;
208  }
209 
210  QTextStream stream(&file);
211  stream.setRealNumberPrecision(6);
212  stream.setRealNumberNotation(QTextStream::FixedNotation);
213  stream << "//time(ps);intensity" << QtCompat::endl;
214 
215  for (uint32_t i = 0; i < values.size(); ++i) {
216  stream << i * samplingRate_ps << ";" << values[i] << QtCompat::endl;
217  }
218 
219  file.close();
220  CVLog::Print(QString("[ccWaveform::toASCII] File '%1' has been saved "
221  "successfully")
222  .arg(filename));
223 
224  return true;
225 }
226 
227 double ccWaveform::getRange(double& minVal,
228  double& maxVal,
229  const WaveformDescriptor& descriptor,
230  const uint8_t* dataStorage) const {
231  if (descriptor.numberOfSamples == 0) {
232  assert(false);
233  minVal = maxVal = std::numeric_limits<double>::quiet_NaN();
234  return 0.0;
235  } else {
236  minVal = maxVal = getSample(0, descriptor, dataStorage);
237  }
238 
239  for (uint32_t i = 1; i < descriptor.numberOfSamples; ++i) {
240  double c = getSample(i, descriptor, dataStorage);
241  maxVal = std::max(maxVal, c);
242  minVal = std::min(minVal, c);
243  }
244 
245  return maxVal - minVal;
246 }
247 
249  const CCVector3& P0,
250  const WaveformDescriptor& descriptor) const {
251  float delta_ps = m_echoTime_ps - index * descriptor.samplingRate_ps;
252  return P0 + CCVector3::fromArray(m_beamDir.u) * delta_ps;
253 }
254 
256  // we apply only the rotation
258  trans.applyRotation(u);
260 }
261 
262 bool ccWaveform::toFile(QFile& out, short dataVersion) const {
263  // Version validation
264  if (dataVersion < 46) {
265  assert(false);
266  return false;
267  }
268 
269  QDataStream outStream(&out);
270 
271  // dataVersion >= 46
272  outStream << m_descriptorID;
273  if (m_descriptorID != 0) // no need to save invalid waveforms
274  {
275  outStream << m_byteCount;
276  outStream << static_cast<quint64>(
277  m_dataOffset); // see comment below (in 'fromFile')
278  outStream << m_beamDir.x;
279  outStream << m_beamDir.y;
280  outStream << m_beamDir.z;
281  outStream << m_echoTime_ps;
282  // dataVersion >= 47
283  if (dataVersion >= 47) {
284  outStream << m_returnIndex;
285  }
286  }
287 
288  return true;
289 }
290 
292  // Return index requires version 47, otherwise 46 is sufficient
293  return (m_returnIndex != 1) ? 47 : 46;
294 }
295 
296 bool ccWaveform::fromFile(QFile& in,
297  short dataVersion,
298  int flags,
299  LoadedIDMap& oldToNewIDMap) {
300  QDataStream inStream(&in);
301 
302  if (dataVersion < 46) return false;
303 
304  // dataVersion >= 46
305  inStream >> m_descriptorID;
306  if (m_descriptorID != 0) {
307  inStream >> m_byteCount;
308 
309  // for compilation on gcc/clang, we need to be 'more explicit'...
310  //(apparently uint64_t is not 'evidently' casted to quint64?!)
311  quint64 dataOffset;
312  inStream >> dataOffset;
313  m_dataOffset = static_cast<uint64_t>(dataOffset);
314 
315  inStream >> m_beamDir.x;
316  inStream >> m_beamDir.y;
317  inStream >> m_beamDir.z;
318  inStream >> m_echoTime_ps;
319 
320  if (dataVersion > 46) {
321  // dataVersion >= 47
322  inStream >> m_returnIndex;
323  } else {
324  m_returnIndex = 1;
325  }
326  }
327 
328  return true;
329 }
std::string filename
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
Waveform descriptor.
Definition: ecvWaveform.h:23
bool operator!=(const WaveformDescriptor &d) const
Difference operator.
Definition: ecvWaveform.cpp:23
uint32_t samplingRate_ps
Sampling rate in pico seconds.
Definition: ecvWaveform.h:44
uint8_t bitsPerSample
Number of bits per sample.
Definition: ecvWaveform.h:49
bool toFile(QFile &out, short dataVersion) const override
Saves data to binary stream.
Definition: ecvWaveform.cpp:31
WaveformDescriptor()
Default constructor.
Definition: ecvWaveform.cpp:16
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
Definition: ecvWaveform.cpp:52
short minimumFileVersion() const override
Returns the minimum file version required to save this instance.
Definition: ecvWaveform.cpp:50
uint32_t numberOfSamples
Number of samples.
Definition: ecvWaveform.h:43
double digitizerOffset
Definition: ecvWaveform.h:47
void applyRotation(Vector3Tpl< float > &vec) const
Applies rotation only to a 3D vector (in place) - float version.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
QMultiMap< unsigned, unsigned > LoadedIDMap
Map of loaded unique IDs (old ID --> new ID)
uint32_t byteCount() const
Returns the number of allocated bytes.
Definition: ecvWaveform.h:112
uint8_t m_returnIndex
Return index.
Definition: ecvWaveform.h:183
bool decodeSamples(std::vector< double > &values, const WaveformDescriptor &descriptor, const uint8_t *dataStorage) const
Decodes the samples and store them in a vector.
uint32_t getRawSample(uint32_t i, const WaveformDescriptor &descriptor, const uint8_t *dataStorage) const
Returns the (raw) value of a given sample.
Definition: ecvWaveform.cpp:83
uint32_t m_byteCount
Waveform packet size in bytes.
Definition: ecvWaveform.h:158
bool toASCII(const QString &filename, const WaveformDescriptor &descriptor, const uint8_t *dataStorage) const
Exports (real) samples to an ASCII file.
float m_echoTime_ps
Return Point location (in picoseconds)
Definition: ecvWaveform.h:174
double getSample(uint32_t i, const WaveformDescriptor &descriptor, const uint8_t *dataStorage) const
Returns the (real) value of a given sample (in volts)
CCVector3f m_beamDir
Laser beam direction.
Definition: ecvWaveform.h:167
short minimumFileVersion() const override
Returns the minimum file version required to save this instance.
uint64_t m_dataOffset
Byte offset to waveform data.
Definition: ecvWaveform.h:161
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
static bool ToASCII(const QString &filename, std::vector< double > &values, uint32_t samplingRate_ps)
Helper: exports a series of values as an ASCII file.
void applyRigidTransformation(const ccGLMatrix &trans)
Applies a rigid transformation (on the beam direction)
const uint8_t * data(const uint8_t *dataStorage) const
Gives access to the internal data.
Definition: ecvWaveform.h:121
ccWaveform(uint8_t descriptorID=0)
Default constructor.
Definition: ecvWaveform.cpp:70
uint8_t m_descriptorID
Wave Packet descriptor index.
Definition: ecvWaveform.h:180
bool toFile(QFile &out, short dataVersion) const override
Saves data to binary stream.
void setDataDescription(uint64_t dataOffset, uint32_t byteCount)
Describes the waveform data.
Definition: ecvWaveform.cpp:78
CCVector3 getSamplePos(float i, const CCVector3 &P0, const WaveformDescriptor &descriptor) const
Returns the sample position in 3D.
uint64_t dataOffset() const
Returns the byte offset to waveform data.
Definition: ecvWaveform.h:115
double getRange(double &minVal, double &maxVal, const WaveformDescriptor &descriptor, const uint8_t *dataStorage) const
Returns the range of (real) samples.
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718