v2.0.0
Loading...
Searching...
No Matches
fastrak_parser.cpp
Go to the documentation of this file.
1//=============================================================================================================
22
23#include "fastrak_parser.h"
24
25using namespace UTILSLIB;
26
27namespace
28{
29
30constexpr float kInchToMetre = 0.0254f;
31constexpr float kCentimetreToMetre = 0.01f;
32
33float linearScale(FastrakParser::Units units)
34{
35 return (units == FastrakParser::Units::Inches) ? kInchToMetre
36 : kCentimetreToMetre;
37}
38
39} // namespace
40
41//=============================================================================================================
42
43bool FastrakParser::parseRecord(const QByteArray& record, Units units, FastrakSample& out)
44{
45 // Fastrak uses fixed-width ASCII fields. When a negative value
46 // immediately follows a positive one the minus sign eats the
47 // separating space, e.g. "0.8829-0.3055". Insert a space before
48 // every '-' that is preceded by a digit so tokenisation works.
49 QByteArray fixed = record.simplified();
50 for (int i = fixed.size() - 1; i > 0; --i) {
51 const char c = fixed.at(i);
52 const char pc = fixed.at(i - 1);
53 if (c == '-' && pc >= '0' && pc <= '9') {
54 fixed.insert(i, ' ');
55 }
56 }
57
58 const QList<QByteArray> tokens = fixed.split(' ');
59 if (tokens.size() < 4) {
60 return false;
61 }
62
63 bool ok = false;
64 const int station = tokens[0].toInt(&ok);
65 if (!ok) {
66 return false;
67 }
68
69 auto parseFloat = [&ok](const QByteArray& t) -> float {
70 bool good = false;
71 const float v = t.toFloat(&good);
72 ok = ok && good;
73 return v;
74 };
75
76 ok = true;
77 const float x = parseFloat(tokens[1]);
78 const float y = parseFloat(tokens[2]);
79 const float z = parseFloat(tokens[3]);
80 if (!ok) {
81 return false;
82 }
83
84 const float scale = linearScale(units);
85 out.station = station;
86 out.position = QVector3D(x * scale, y * scale, z * scale);
87 out.hasOrientation = false;
88 out.orientation = QQuaternion();
89
90 if (tokens.size() >= 8) {
91 // Quaternion output (O-item 11): q0 q1 q2 q3 (scalar-first).
92 // No gimbal lock — preferred over Euler angles.
93 const float q0 = parseFloat(tokens[4]);
94 const float q1 = parseFloat(tokens[5]);
95 const float q2 = parseFloat(tokens[6]);
96 const float q3 = parseFloat(tokens[7]);
97 if (ok) {
98 out.orientation = QQuaternion(q0, q1, q2, q3).normalized();
99 out.hasOrientation = true;
100 }
101 } else if (tokens.size() >= 7) {
102 // Euler angle output (O-item 4): azimuth, elevation, roll.
103 // Kept for backward compatibility; suffers from gimbal lock at elevation ±90°.
104 const float az = parseFloat(tokens[4]);
105 const float el = parseFloat(tokens[5]);
106 const float ro = parseFloat(tokens[6]);
107 if (ok) {
108 const QQuaternion qZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, az);
109 const QQuaternion qY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, el);
110 const QQuaternion qX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, ro);
111 out.orientation = qZ * qY * qX;
112 out.hasOrientation = true;
113 }
114 }
115
116 return true;
117}
118
119//=============================================================================================================
120
122{
123 while (true) {
124 const int eol = m_buffer.indexOf('\n');
125 if (eol < 0) {
126 return false;
127 }
128
129 QByteArray line = m_buffer.left(eol);
130 m_buffer.remove(0, eol + 1);
131 if (line.endsWith('\r')) {
132 line.chop(1);
133 }
134 if (line.trimmed().isEmpty()) {
135 continue; // skip blank line, keep scanning
136 }
137 if (parseRecord(line, m_units, out)) {
138 return true;
139 }
140 // malformed line → drop and keep scanning
141 }
142}
Polhemus Fastrak / FastSCAN ASCII record parser.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
One decoded sample from a Fastrak ASCII stream.
QVector3D position
Position in metres (always normalised to SI).
QQuaternion orientation
Sensor orientation; identity if not provided.
int station
1-based station id reported by the device.
bool nextSample(FastrakSample &out)
@ Inches
Fastrak factory default.
static bool parseRecord(const QByteArray &record, Units units, FastrakSample &out)