39 static constexpr double rad2deg = 180. / M_PI;
49 if (u ==
unit)
return *
this;
53 .pitch =
pitch / rad2deg,
54 .roll =
roll / rad2deg,
59 .pitch =
pitch * rad2deg,
60 .roll =
roll * rad2deg,
63 default:
return {.yaw=0,.pitch=0,.roll=0,.unit=NONE};
77 double factor = u==
RADIANS ? 1. : rad2deg;
80 .yaw = factor * atan2(2.0 * (i * j + k * r), (sqi - sqj - sqk + sqr)),
81 .pitch = factor * asin(-2.0 * (i * k - j * r) / (sqi + sqj + sqk + sqr)),
82 .roll = factor * atan2(2.0 * (j * k + i * r), (-sqi - sqj + sqk + sqr)),
96 if (u ==
unit)
return *
this;
110 default:
return {.x=0,.y=0,.z=0,.unit=NONE};
128 int8_t read8(View d) {
return d[0]; }
129 int16_t read16(View d) {
return d[0] | d[1] << 8; }
130 int32_t read32(View d) {
return d[0] | d[1] << 8 | d[2] << 16 | d[3] << 24; }
132 constexpr double Q(uint8_t p) {
133 return 1. / (1 << p);
136 enum Channel : uint8_t {
137 C_SHTP, C_DEV, C_CTRL, C_NORM, C_WAKE, C_GIRV, C_LAST,
140 uint8_t seq[C_LAST] {};
163 uint8_t sq = seq[msg.chan]++;
166 (uint8_t) data.
size, (uint8_t)(data.
size >> 8),
169 for (
auto &d: msg.cargo) data.
append(d);
178 if (q.
full())
return;
179 q.
push(std::move(t));
180 if (q.
front().active ==
false) sendQueued(q);
184 if (q.
empty())
return;
186 if (t.active)
return;
188 if (t.q.empty())
return;
189 auto &oper = t.q.front();
190 if (oper.seq != -1)
return;
194 enum Commands : uint8_t { CMD_GET_ADV, CMD_GET_ERR };
196 void getAdvertisement(
bool all) {
199 .cargo = {CMD_GET_ADV, all},
205 .cargo = {CMD_GET_ERR},
209 enum AdvTag : uint8_t {
210 RESERVED, GUID, MAX_CPH_WRITE, MAX_CPH_READ, MAX_XFER_WRITE, MAX_XFER_READ,
211 NORMAL_CH, WAKE_CH, APP_NAME, CH_NAME
214 void logAdvertisement(View data) {
215 lg->
info(
"advertisement\n");
216 uint8_t last_guid {};
218 assert (data.len > 2);
219 AdvTag tag {data[0]};
220 size_t len {data[1]};
223 case 1: val = read8({data,2});
break;
224 case 2: val = read16({data,2});
break;
225 case 4: val = read32({data,2});
break;
227 switch ((uint8_t)tag) {
228 case GUID: last_guid = val;
229 lg->
print(
"GUID: %d\n", val);
break;
231 lg->
print(
"MaxCargoPlusHeaderWrite: %d\n", val);
break;
233 lg->
print(
"MaxCargoPlusHeaderRead: %d\n", val);
break;
235 lg->
print(
"MaxTransferWrite: %d\n", val);
break;
237 lg->
print(
"MaxTransferRead: %d\n", val);
break;
239 lg->
print(
"NormalChannel: %d\n", val);
break;
241 lg->
print(
"WakeChannel: %d\n", val);
break;
243 lg->
print(
"AppName: %.*s\n", len, &data[2]);
break;
245 lg->
print(
"ChannelName: %.*s\n", len, &data[2]);
break;
247 if (last_guid == 0) {
248 lg->
print(
"SHTP Version: %.*s\n", len, &data[2]);
break;
250 lg->
print(
"Version: %.*s\n", len, &data[2]);
break;
253 if (last_guid == 0) {
254 lg->
print(
"SHTP Timeout: %dms\n", val);
break;
256 lg->
print(
"Report Lengths:\n");
257 auto rpl =
Slice{data,2};
258 for (
size_t i = 0; i < rpl.len; i+=2) {
259 lg->
print(
"\t[ 0x%.2x : %d ]\n", rpl[i], rpl[i+1]);
264 lg->
print(
"Wrong Tag [%d], len [%d]\n", tag, len);
break;
266 data =
Slice{data, len+2};
271 NO_ERR, MAX_READ_EXCEED, HOST_WRITE_SHORT, MAX_WRITE_EXCEED, HOST_WRITE_INVALID,
272 FRAGMENT_UNSUPPORTED_START, FRAGMENT_UNSUPPORTED_CONT, CMD_NOT_RECOGNIZED, PARAM_NOT_RECOGNIZED,
273 CHANNEL_NOT_RECOGNIZED, REQ_WHILE_PENDING, WRITE_WHILE_RESP, ERR_TRUNC_LIST,
276 void logErrorList(View data) {
277 lg->
info(
"error list\n");
278 for (
auto &d: data) {
281 case NO_ERR: lg->
print(
"\tNO_ERR\n", err);
break;
282 case MAX_READ_EXCEED: lg->
print(
"\tMAX_READ_EXCEED\n", err);
break;
283 case HOST_WRITE_SHORT: lg->
print(
"\tHOST_WRITE_SHORT\n", err);
break;
284 case MAX_WRITE_EXCEED: lg->
print(
"\tMAX_WRITE_EXCEED\n", err);
break;
285 case HOST_WRITE_INVALID: lg->
print(
"\tHOST_WRITE_INVALID\n", err);
break;
286 case FRAGMENT_UNSUPPORTED_START: lg->
print(
"\tFRAGMENT_UNSUPPORTED_START\n", err);
break;
287 case FRAGMENT_UNSUPPORTED_CONT: lg->
print(
"\tFRAGMENT_UNSUPPORTED_CONT\n", err);
break;
288 case CMD_NOT_RECOGNIZED: lg->
print(
"\tCMD_NOT_RECOGNIZED\n", err);
break;
289 case PARAM_NOT_RECOGNIZED: lg->
print(
"\tPARAM_NOT_RECOGNIZED\n", err);
break;
290 case CHANNEL_NOT_RECOGNIZED: lg->
print(
"\tCHANNEL_NOT_RECOGNIZED\n", err);
break;
291 case REQ_WHILE_PENDING: lg->
print(
"\tREQ_WHILE_PENDING\n", err);
break;
292 case WRITE_WHILE_RESP: lg->
print(
"\tWRITE_WHILE_RESP\n", err);
break;
293 case ERR_TRUNC_LIST: lg->
print(
"\tERR_TRUNC_LIST\n", err);
break;
298 void handleSHTP(View cargo) {
299 if (lg) lg->
print(
"Response Handler: ");
301 case CMD_GET_ADV:
if(lg) logAdvertisement({cargo, 1});
break;
302 case CMD_GET_ERR:
if(lg) logErrorList({cargo, 1});
break;
303 default:
if(lg) lg->
warn(
"unknown SHTP response: [%d]\n", cargo[0]);
307 void handleWakeNormal(View cargo) {
308 while (cargo.len)
switch (cargo[0]) {
309 case 0x05: cargo = handleRotVecReport(cargo);
break;
310 case 0xFA: cargo = handleRebaseTimeStamp(cargo);
break;
311 case 0xFB: cargo = handleBaseTimeStamp(cargo);
break;
312 case 0xFC: cargo = handleFeatureResponse(cargo);
break;
314 if (lg) lg->
warn(
"unrecognized Report: [0x%.2x]\n", cargo[0]);
315 if (lg) logData({*cargo.buf,0});
320 View handleRotVecReport(View cargo) {
321 assert(cargo[0] == 0x05);
323 .r = read16({cargo, 10}) * Q(14),
324 .i = read16({cargo, 4}) * Q(14),
325 .j = read16({cargo, 6}) * Q(14),
326 .k = read16({cargo, 8}) * Q(14),
328 this->rotvec_accuracy = read16({cargo, 12}) * Q(12);
332 void handleGIRV(View cargo) {
335 .r = read16({cargo, 6}) * Q(14),
336 .i = read16({cargo, 0}) * Q(14),
337 .j = read16({cargo, 2}) * Q(14),
338 .k = read16({cargo, 4}) * Q(14),
341 .
x = read16({cargo, 8}) * Q(10),
342 .y = read16({cargo, 10}) * Q(10),
343 .z = read16({cargo, 12}) * Q(10),
348 View handleBaseTimeStamp(View cargo) {
349 assert(cargo[0] == 0xFB);
350 int32_t tb_delta = read32({cargo, 1});
355 View handleRebaseTimeStamp(View cargo) {
356 assert(cargo[0] == 0xFA);
357 int32_t rb_delta = read32({cargo, 1});
362 void logData(View cargo) {
363 for (uint8_t c: cargo) {
364 lg->
print(
"%#x ", c);
370 uint32_t report_interval, batch_interval, config_word;
371 uint16_t sensitivity;
376 void setFeature(Feature feat) {
380 0xFD, feat.reportID, feat.flags,
381 (uint8_t)feat.sensitivity, (uint8_t)(feat.sensitivity>>8),
382 (uint8_t)(feat.report_interval >> 0),
383 (uint8_t)(feat.report_interval >> 8),
384 (uint8_t)(feat.report_interval >> 16),
385 (uint8_t)(feat.report_interval >> 24),
386 (uint8_t)(feat.batch_interval >> 0),
387 (uint8_t)(feat.batch_interval >> 8),
388 (uint8_t)(feat.batch_interval >> 16),
389 (uint8_t)(feat.batch_interval >> 24),
390 (uint8_t)(feat.config_word >> 0),
391 (uint8_t)(feat.config_word >> 8),
392 (uint8_t)(feat.config_word >> 16),
393 (uint8_t)(feat.config_word >> 24),
398 View handleFeatureResponse(View cargo) {
400 if (lg) lg->
print(
"handling Feature Response.. TODO\n");
408 void handleControl(View cargo) {
410 case 0xF5: handleFRSWriteResponse(cargo);
return;
411 case 0xF3: handleFRSReadResponse(cargo);
return;
413 lg->
warn(
"unhandled message on control channel: [%#x]\n", cargo[0]);
414 logData({*cargo.buf, 0});
419 void handleFRSWriteResponse(View d) {
420 assert(d[0] == 0xF5);
421 if (qs.write.empty())
return;
422 auto &t = qs.write.front();
423 if (t.q.empty())
return;
424 auto &op = t.q.front();
425 if (op.rsp != 0xF5 && lg) lg->
warn(
"response 0xF5, expected [%#x]\n", op.rsp);
426 if (lg) logFRSWriteResponse(d);
427 uint8_t status = d[1];
430 case 0:
if (!t.q.empty()) {
432 sendQueued(qs.write);
438 default:
if (!qs.write.empty()) {
440 sendQueued(qs.write);
446 void logFRSWriteResponse(View d) {
447 uint8_t status = d[1];
448 uint16_t word = read16({d, 2});
449 lg->
info(
"FRS Write Response: offset [%d]\n", word);
451 case 0: lg->
print(
"Word(s) received\n");
break;
452 case 1: lg->
print(
"unrecognized FRS type\n");
break;
453 case 2: lg->
print(
"busy\n");
break;
454 case 3: lg->
print(
"write completed\n");
break;
455 case 4: lg->
print(
"write mode ready\n");
break;
456 case 5: lg->
print(
"write failed\n");
break;
457 case 6: lg->
print(
"data received while not in write mode\n");
break;
458 case 7: lg->
print(
"invalid length\n");
break;
459 case 8: lg->
print(
"record valid\n");
break;
460 case 9: lg->
print(
"record invalid\n");
break;
461 case 10: lg->
print(
"device error\n");
break;
462 case 11: lg->
print(
"record is read only\n");
break;
463 case 12: lg->
print(
"FRS memory is full\n");
break;
467 void handleFRSReadResponse(View d) {
468 assert(d[0] == 0xF3);
469 if (lg) logFRSReadResponse(d);
470 uint8_t status = d[1] & 0xf;
475 qs.read.pop(); sendQueued(qs.read);
break;
479 void logFRSReadResponse(View d) {
480 lg->
info(
"FRS Read Response:\n");
481 uint8_t len = d[1] >> 4;
482 uint8_t status = d[1] & 0xf;
483 uint16_t offset = read16({d, 2});
484 uint32_t w0 = read32({d, 4});
485 uint32_t w1 = read32({d, 8});
487 case 0: lg->
print(
"no error\n");
break;
488 case 1: lg->
warn(
"unrecognized FRS type [0x%.4x]\n", read16({d,12}));
break;
489 case 2: lg->
print(
"busy\n");
break;
490 case 3: lg->
print(
"read record completed\n");
break;
491 case 5: lg->
print(
"record empty\n");
break;
492 case 8: lg->
print(
"device error\n");
break;
494 lg->
print(
"offset: %d\n", offset);
495 lg->
print(
"length of data: %d\n", len);
496 lg->
print(
"data0: %#x\n", w0);
497 lg->
print(
"data1: %#x\n", w1);
498 lg->
print(
"FRS Type: %#x\n", (uint16_t)read16({d,12}));
501 void readFRS(uint16_t type) {
510 (uint8_t)type, (uint8_t)(type>>8),
518 sendTransaction(qs.read, std::move(t));
523 GameRotVec_6Ax = 0x0207,
524 AbsRotVec_9Ax = 0x0204,
525 } ref = GameRotVec_6Ax;
526 uint32_t sync = 10000;
527 uint32_t max_err = 0x10c15238;
528 uint32_t prediction = 0;
534 void configure(GIRVConfig cfg) {
556 (uint8_t)cfg.ref, (uint8_t)(cfg.ref >> 8),
558 (uint8_t)cfg.sync, (uint8_t)(cfg.sync >> 8),
559 (uint8_t)(cfg.sync >> 16), (uint8_t)(cfg.sync >> 24),
570 (uint8_t)cfg.max_err, (uint8_t)(cfg.max_err >> 8),
571 (uint8_t)(cfg.max_err >> 16), (uint8_t)(cfg.max_err >> 24),
572 (uint8_t)cfg.prediction, (uint8_t)(cfg.prediction >> 8),
573 (uint8_t)(cfg.prediction >> 16), (uint8_t)(cfg.prediction >> 24),
584 (uint8_t)cfg.alpha, (uint8_t)(cfg.alpha >> 8),
585 (uint8_t)(cfg.alpha >> 16), (uint8_t)(cfg.alpha >> 24),
586 (uint8_t)cfg.beta, (uint8_t)(cfg.beta >> 8),
587 (uint8_t)(cfg.beta >> 16), (uint8_t)(cfg.beta >> 24),
598 (uint8_t)cfg.gamma, (uint8_t)(cfg.gamma >> 8),
599 (uint8_t)(cfg.gamma >> 16), (uint8_t)(cfg.gamma >> 24),
606 sendTransaction(qs.write, std::move(t));
622 const unsigned int len = (uint16_t)(rq.
data[1] & 0x7f) << 8 | rq.
data[0];
623 if (len == 0xffff)
return;
624 const Channel chan = (Channel)rq.
data[2];
625 const uint8_t seq = rq.
data[3];
626 if (len == 0)
return;
627 if (seq != this->seq[chan]) {
629 if (lg) lg->
warn(
"expected seq: %d, got %d\n", this->seq[chan], seq);
631 this->seq[chan] = seq + 1;
643 View data {rq.
data, 4};
645 case C_SHTP: handleSHTP(data);
return;
647 case C_CTRL: handleControl(data);
return;
648 case C_NORM ... C_WAKE: handleWakeNormal(data);
return;
649 case C_GIRV: handleGIRV(data);
return;
652 lg->
print(
"unrecognized data:\n");
653 logData({rq.
data, 0});
660 double rotvec_accuracy;
671 void poll(uint32_t, uint32_t) {
675 .opts = { .read =
true },
682 .report_interval = 1'000'000 / Hz,
690 .report_interval = 0,
698 .ref = GIRVConfig::AbsRotVec_9Ax,
701 .report_interval = 1'000'000 / Hz,
709 .report_interval = 0,
728 case RADIANS:
return rotvec_accuracy;
729 case DEGREES:
return rotvec_accuracy * rad2deg;