antplus_racquet.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. This software is subject to the license described in the License.txt file
  3. included with this software distribution. You may not use this file except in compliance
  4. with this license.
  5. Copyright (c) Dynastream Innovations Inc. 2012
  6. All rights reserved.
  7. */
  8. #pragma once
  9. #define RACQUET_REV 1.0 // Device Profile Revision Number
  10. public ref class AntPlusRacquet
  11. {
  12. public:
  13. // Channel Parameters
  14. static const UCHAR DEVICE_TYPE = 0x1A;
  15. static const UCHAR TX_TYPE = 5;
  16. static const USHORT MSG_PERIOD = 8192; // 4 Hz
  17. // Data Pages
  18. static const UCHAR PAGE_STROKE_EVENT = 0x01;
  19. static const UCHAR PAGE_STROKE_COUNT = 0x02;
  20. static const UCHAR PAGE_PREVIOUS_STROKE_EVENT = 0x03;
  21. static const UCHAR PAGE_ZONE_COUNT = 0x04;
  22. static const UCHAR PAGE_EVENT_DATA = 0x20;
  23. static const UCHAR PAGE_SESSION_DATA = 0x21;
  24. static const UCHAR PAGE_LAP_DATA = 0x22;
  25. static const UCHAR PAGE_LENGTH_DATA = 0x23;
  26. static const UCHAR PAGE_METABOLIC_DATA = 0x24;
  27. static const UCHAR PAGE_SPEED_DATA = 0x25;
  28. static const UCHAR PAGE_DISTANCE_DATA = 0x26;
  29. // Reserved/Invalid/Special Values
  30. static const UCHAR RESERVED = 0xFF;
  31. static const USHORT UNKNOWN = 0xFFFF;
  32. static const ULONG UNKNOWN_ULONG = 0xFFFFFFFF;
  33. static const UCHAR NUMBER_OF_ZONES = 12;
  34. static const UCHAR NUMBER_OF_STROKE_TYPES = 7;
  35. // Bit Masks
  36. static const UCHAR BYTE_MASK = 0xFF;
  37. // Bit Shifts
  38. static const UCHAR NIBBLE_SHIFT = 4;
  39. static const UCHAR BYTE_SHIFT = 8;
  40. // Acknowledged Messages
  41. static const UCHAR ACK_FAIL = 0;
  42. static const UCHAR ACK_SUCCESS = 1;
  43. static const UCHAR ACK_RETRY = 2;
  44. static const UCHAR MAX_RETRIES = 5; // defines the max number of times to retry before failing
  45. // Enumerations
  46. enum class StrokeTypes : UCHAR // Stroke type definitions
  47. {
  48. NO_EVENT = 0,
  49. ALL_STROKES = 0,
  50. OTHER = 1,
  51. SERVE = 2,
  52. FOREHAND = 3,
  53. BACKHAND = 4,
  54. SMASH = 5,
  55. UNKNOWN = 255,
  56. };
  57. enum class RacquetZones : UCHAR // Racquet Zone definitions
  58. {
  59. MISS = 0,
  60. TOP_RIGHT = 1,
  61. MIDDLE_RIGHT = 2,
  62. BOTTOM_RIGHT = 3,
  63. BOTTOM_CENTRE = 4,
  64. BOTTOM_LEFT = 5,
  65. MIDDLE_CENTRE = 6,
  66. MIDDLE_LEFT = 7,
  67. TOP_LEFT = 8,
  68. TOP_CENTRE = 9,
  69. RIM_OTHER = 10,
  70. UNKNOWN = 63,
  71. };
  72. enum class ANTFS : UCHAR // Racquet Zone definitions
  73. {
  74. NOT_SUPPORTED = 0,
  75. SUPPORTED = 1,
  76. };
  77. enum class FITEvents : UCHAR
  78. {
  79. TIMER = 0x00,
  80. SESSION = 0x08,
  81. USER_MARKER = 0x20,
  82. SPORT_POINT = 0x21,
  83. };
  84. enum class FITEventTypes : UCHAR
  85. {
  86. START = 0x00,
  87. STOP = 0x01,
  88. MARKER = 0x03,
  89. };
  90. enum class FITSports : UCHAR
  91. {
  92. TENNIS = 0x8,
  93. };
  94. enum class FITSubSports : UCHAR
  95. {
  96. WARM_UP = 21,
  97. MATCH = 22,
  98. EXERCISE = 23,
  99. CHALLENGE = 24,
  100. };
  101. // Error handling
  102. // Flags indicate errors causing the exception
  103. ref class Error : public System::Exception{
  104. public:
  105. BOOL bUndefPage; // Undefined page
  106. BOOL bBadReserved; // Invalid values on reserved fields
  107. BOOL bUndefBatStatus; // Undefined battery status value
  108. enum class Code : UCHAR // Error code definitions
  109. {
  110. INVALID_RESERVED
  111. };
  112. Error()
  113. {
  114. ClearFlags();
  115. }
  116. Error(Code eCode1_)
  117. {
  118. ClearFlags();
  119. SetFlags(eCode1_);
  120. }
  121. Error(Code eCode1_, Code eCode2_)
  122. {
  123. ClearFlags();
  124. SetFlags(eCode1_);
  125. SetFlags(eCode2_);
  126. }
  127. private:
  128. void ClearFlags()
  129. {
  130. bUndefPage = FALSE;
  131. bBadReserved = FALSE;
  132. bUndefBatStatus = FALSE;
  133. }
  134. void SetFlags(Code eCode_)
  135. {
  136. switch(eCode_)
  137. {
  138. case Code::INVALID_RESERVED:
  139. bBadReserved = TRUE;
  140. break;
  141. default:
  142. break;
  143. }
  144. }
  145. };
  146. public:
  147. // Current Stroke Event Data (Page 1)
  148. UCHAR ucEventCount; // Incremented with every stroke detected
  149. USHORT usEventTime; // Time that the last stroke event occurred
  150. UCHAR ucEventType; // Event/Stroke Type (in StrokeTypes enum)
  151. UCHAR ucRacquetZone; // Zone of the racquet event occurred on (RacquetZone enum)
  152. UCHAR ucANTFS; // Indicates whether ANT-FS is supported or not
  153. USHORT usSpeed; // Speed of the ball
  154. // Stroke Count Summary Data (Page 2)
  155. UCHAR ucStrokeType1; // Stroke Type of first subpaged item
  156. UCHAR ucStrokeType2; // Stroke Type of second subpaged item
  157. USHORT usStrokeCount1; // Stroke Count for Stroke Type 1
  158. USHORT usStrokeCount2; // Stroke Count for Stroke Type 2
  159. // Previous Stroke Event Data (Page 3)
  160. UCHAR ucPreviousEventCount; // Event count of the data included in this page
  161. USHORT usPreviousEventTime; // Accumulated time at the time of this event
  162. UCHAR ucPreviousEventType; // Event/Stroke Type of this event
  163. UCHAR ucPreviousRacquetZone; // Zone of the racquet this event occurred on
  164. USHORT usPreviousSpeed; // Speed of the ball at this event
  165. // Racquet Zone Count Summary Data (Page 4)
  166. UCHAR ucType; // Stroke Type
  167. UCHAR ucZone; // Racquet Zone
  168. USHORT usCount; // Stroke count for stroke type in zone
  169. USHORT usCountPlus1; // Stroke count for stroke type in (zone + 1)
  170. // Event Data (Page 32)
  171. FITEvents eEvent;
  172. FITEventTypes eEventType;
  173. ULONG ulEventData;
  174. // Session Data (Page 33)
  175. FITSports eSport;
  176. FITSubSports eGameMode;
  177. USHORT usMySessionsWon;
  178. USHORT usOpponentSessionsWon;
  179. // Lap Data (Page 34)
  180. USHORT usMySetCount;
  181. USHORT usOpponentSetCount;
  182. // Length Data (Page 35)
  183. USHORT usMyGameCount;
  184. USHORT usOpponentGameCount;
  185. // Metabolic Data (Page 36)
  186. UCHAR ucMaxHeartRate;
  187. UCHAR ucAvgHeartRate;
  188. USHORT usTotalCalories;
  189. // Speed Data (Page 37)
  190. USHORT usMaxRunningSpeed;
  191. USHORT usAvgRunningSpeed;
  192. // Distance Data (Page 38)
  193. UINT32 uiTotalDistance;
  194. public:
  195. AntPlusRacquet()
  196. {
  197. }
  198. ~AntPlusRacquet()
  199. {
  200. }
  201. public:
  202. /**************************************************************************
  203. * AntPlusRacquet::Encode
  204. *
  205. * Encodes common data
  206. * Exceptions are thrown when dealing with invalid data
  207. *
  208. * ucPageNum_: number of page to encode
  209. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  210. *
  211. * returns: N/A
  212. *
  213. **************************************************************************/
  214. void Encode(UCHAR ucPageNum_, UCHAR* pucTxBuffer_)
  215. {
  216. switch(ucPageNum_)
  217. {
  218. case PAGE_STROKE_EVENT:
  219. pucTxBuffer_[0] = ucPageNum_; // Page # (1)
  220. pucTxBuffer_[1] = ucEventCount; // Event Count of current stroke event
  221. pucTxBuffer_[2] = usEventTime & BYTE_MASK; // Lower byte of current event time
  222. pucTxBuffer_[3] = (usEventTime >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of current event time
  223. pucTxBuffer_[4] = ucEventType; // Stroke type of this event
  224. pucTxBuffer_[5] = ucRacquetZone | (ucANTFS << 6); // Racquet zone that the event occurred on, ANT-FS capabilities of the device
  225. pucTxBuffer_[6] = usSpeed & BYTE_MASK; // Lower byte of ball speed
  226. pucTxBuffer_[7] = (usSpeed >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of ball speed
  227. break;
  228. case PAGE_STROKE_COUNT:
  229. pucTxBuffer_[0] = ucPageNum_; // Page # (2)
  230. pucTxBuffer_[1] = RESERVED;
  231. pucTxBuffer_[2] = ucStrokeType1; // First stroke type subpaged in this page
  232. pucTxBuffer_[3] = ucStrokeType2; // Second stroke type subpaged in this page
  233. pucTxBuffer_[4] = usStrokeCount1 & BYTE_MASK; // Lower byte of stroke type 1 count
  234. pucTxBuffer_[5] = (usStrokeCount1 >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of stroke type 1 count
  235. pucTxBuffer_[6] = usStrokeCount2 & BYTE_MASK; // Lower byte of stroke type 2 count
  236. pucTxBuffer_[7] = (usStrokeCount2 >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of stroke type 2 count
  237. break;
  238. case PAGE_PREVIOUS_STROKE_EVENT:
  239. pucTxBuffer_[0] = ucPageNum_; // Page # (3)
  240. pucTxBuffer_[1] = ucPreviousEventCount; // Event Count of requested stroke
  241. pucTxBuffer_[2] = usPreviousEventTime & BYTE_MASK; // Lower byte of requested stroke time
  242. pucTxBuffer_[3] = (usPreviousEventTime >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of requested stroke time
  243. pucTxBuffer_[4] = ucPreviousEventType; // Stroke type of requested event
  244. pucTxBuffer_[5] = ucPreviousRacquetZone; // Racquet zone that the requeste event occurred on
  245. pucTxBuffer_[6] = usPreviousSpeed & BYTE_MASK; // Lower byte of requested stroke ball speed
  246. pucTxBuffer_[7] = (usPreviousSpeed >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of requested stroke ball speed
  247. break;
  248. case PAGE_ZONE_COUNT:
  249. pucTxBuffer_[0] = ucPageNum_; // Page # (4)
  250. pucTxBuffer_[1] = RESERVED;
  251. pucTxBuffer_[2] = ucType; // Stroke type subpaged in this page
  252. pucTxBuffer_[3] = ucZone; // Racquet zone subpaged in this page
  253. pucTxBuffer_[4] = usCount & BYTE_MASK; // Lower byte of stroke count in zone
  254. pucTxBuffer_[5] = (usCount >> BYTE_SHIFT) & BYTE_MASK; // Upper byte of stroke count in zone
  255. pucTxBuffer_[6] = usCountPlus1 & BYTE_MASK; // Lower byte of stroke count in (zone + 1)
  256. pucTxBuffer_[7] = (usCountPlus1 >> BYTE_SHIFT)& BYTE_MASK; // Upper byte of stroke count in (zone + 1)
  257. break;
  258. case PAGE_EVENT_DATA:
  259. pucTxBuffer_[0] = ucPageNum_;
  260. pucTxBuffer_[1] = RESERVED;
  261. pucTxBuffer_[2] = (UCHAR)eEvent;
  262. pucTxBuffer_[3] = (UCHAR)eEventType;
  263. pucTxBuffer_[4] = ulEventData & BYTE_MASK;
  264. pucTxBuffer_[5] = (ulEventData >> BYTE_SHIFT) & BYTE_MASK;
  265. pucTxBuffer_[6] = (ulEventData >> (2*BYTE_SHIFT)) & BYTE_MASK;
  266. pucTxBuffer_[7] = (ulEventData >> (3*BYTE_SHIFT)) & BYTE_MASK;
  267. break;
  268. case PAGE_SESSION_DATA:
  269. pucTxBuffer_[0] = ucPageNum_;
  270. pucTxBuffer_[1] = RESERVED;
  271. pucTxBuffer_[2] = (UCHAR)FITSports::TENNIS;
  272. pucTxBuffer_[3] = (UCHAR)eGameMode;
  273. pucTxBuffer_[4] = usMySessionsWon & BYTE_MASK;
  274. pucTxBuffer_[5] = (usMySessionsWon >> BYTE_SHIFT) & BYTE_MASK;
  275. pucTxBuffer_[6] = usOpponentSessionsWon & BYTE_MASK;
  276. pucTxBuffer_[7] = (usOpponentSessionsWon >> BYTE_SHIFT) & BYTE_MASK;
  277. break;
  278. case PAGE_LAP_DATA:
  279. pucTxBuffer_[0] = ucPageNum_;
  280. pucTxBuffer_[1] = RESERVED;
  281. pucTxBuffer_[2] = RESERVED;
  282. pucTxBuffer_[3] = RESERVED;
  283. pucTxBuffer_[4] = usMySetCount & BYTE_MASK;
  284. pucTxBuffer_[5] = (usMySetCount >> BYTE_SHIFT) & BYTE_MASK;
  285. pucTxBuffer_[6] = usOpponentSetCount & BYTE_MASK;
  286. pucTxBuffer_[7] = (usOpponentSetCount >> BYTE_SHIFT) & BYTE_MASK;
  287. break;
  288. case PAGE_LENGTH_DATA:
  289. pucTxBuffer_[0] = ucPageNum_;
  290. pucTxBuffer_[1] = RESERVED;
  291. pucTxBuffer_[2] = RESERVED;
  292. pucTxBuffer_[3] = RESERVED;
  293. pucTxBuffer_[4] = usMyGameCount & BYTE_MASK;
  294. pucTxBuffer_[5] = (usMyGameCount >> BYTE_SHIFT) & BYTE_MASK;
  295. pucTxBuffer_[6] = usOpponentGameCount & BYTE_MASK;
  296. pucTxBuffer_[7] = (usOpponentGameCount >> BYTE_SHIFT) & BYTE_MASK;
  297. break;
  298. case PAGE_METABOLIC_DATA:
  299. pucTxBuffer_[0] = ucPageNum_;
  300. pucTxBuffer_[1] = RESERVED;
  301. pucTxBuffer_[2] = RESERVED;
  302. pucTxBuffer_[3] = RESERVED;
  303. pucTxBuffer_[4] = ucMaxHeartRate;
  304. pucTxBuffer_[5] = ucAvgHeartRate;
  305. pucTxBuffer_[6] = usTotalCalories & BYTE_MASK;
  306. pucTxBuffer_[7] = (usTotalCalories >> BYTE_SHIFT) & BYTE_MASK;
  307. break;
  308. case PAGE_SPEED_DATA:
  309. pucTxBuffer_[0] = ucPageNum_;
  310. pucTxBuffer_[1] = RESERVED;
  311. pucTxBuffer_[2] = RESERVED;
  312. pucTxBuffer_[3] = RESERVED;
  313. pucTxBuffer_[4] = usMaxRunningSpeed & BYTE_MASK;
  314. pucTxBuffer_[5] = (usMaxRunningSpeed >> BYTE_SHIFT) & BYTE_MASK;
  315. pucTxBuffer_[6] = usAvgRunningSpeed & BYTE_MASK;
  316. pucTxBuffer_[7] = (usAvgRunningSpeed >> BYTE_SHIFT) & BYTE_MASK;
  317. break;
  318. case PAGE_DISTANCE_DATA:
  319. pucTxBuffer_[0] = ucPageNum_;
  320. pucTxBuffer_[1] = RESERVED;
  321. pucTxBuffer_[2] = RESERVED;
  322. pucTxBuffer_[3] = RESERVED;
  323. pucTxBuffer_[4] = uiTotalDistance & BYTE_MASK;
  324. pucTxBuffer_[5] = (uiTotalDistance >> BYTE_SHIFT) & BYTE_MASK;
  325. pucTxBuffer_[6] = (uiTotalDistance >> (BYTE_SHIFT * 2)) & BYTE_MASK;
  326. pucTxBuffer_[7] = (uiTotalDistance >> (BYTE_SHIFT * 3)) & BYTE_MASK;
  327. break;
  328. default:
  329. break;
  330. }
  331. }
  332. /**************************************************************************
  333. * AntPlusRacquet::Decode
  334. *
  335. * Decodes received Racquet Data Pages
  336. * Exceptions are thrown when dealing with invalid data
  337. *
  338. * pucRxBuffer_: pointer to the buffer containing the received data
  339. *
  340. * returns: N/A
  341. *
  342. **************************************************************************/
  343. void Decode(UCHAR* pucRxBuffer_)
  344. {
  345. switch(pucRxBuffer_[0])
  346. {
  347. case PAGE_STROKE_EVENT:
  348. ucEventCount = pucRxBuffer_[1];
  349. usEventTime = pucRxBuffer_[2] | (pucRxBuffer_[3] << BYTE_SHIFT);
  350. ucEventType = pucRxBuffer_[4];
  351. ucRacquetZone = pucRxBuffer_[5] & 0x3F;
  352. ucANTFS = ((pucRxBuffer_[5] & 0xC0) >> 6);
  353. usSpeed = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  354. break;
  355. case PAGE_STROKE_COUNT:
  356. ucStrokeType1 = pucRxBuffer_[2];
  357. ucStrokeType2 = pucRxBuffer_[3];
  358. usStrokeCount1 = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT);
  359. usStrokeCount2 = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  360. break;
  361. case PAGE_PREVIOUS_STROKE_EVENT:
  362. ucPreviousEventCount = pucRxBuffer_[1];
  363. usPreviousEventTime = pucRxBuffer_[2] | (pucRxBuffer_[3] << BYTE_SHIFT);
  364. ucPreviousEventType = pucRxBuffer_[4];
  365. ucPreviousRacquetZone = pucRxBuffer_[5];
  366. usPreviousSpeed = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  367. break;
  368. case PAGE_ZONE_COUNT:
  369. ucType = pucRxBuffer_[2];
  370. ucZone = pucRxBuffer_[3];
  371. usCount = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT);
  372. usCountPlus1 = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  373. break;
  374. case PAGE_EVENT_DATA:
  375. eEvent = (FITEvents)(pucRxBuffer_[2]);
  376. eEventType = (FITEventTypes)(pucRxBuffer_[3]);
  377. ulEventData = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT)
  378. | (pucRxBuffer_[6] << (2*BYTE_SHIFT)) | (pucRxBuffer_[7] << (3*BYTE_SHIFT));
  379. break;
  380. case PAGE_SESSION_DATA:
  381. eSport = (FITSports)(pucRxBuffer_[2]);
  382. eGameMode = (FITSubSports)(pucRxBuffer_[3]);
  383. usMySessionsWon = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT);
  384. usOpponentSessionsWon = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  385. break;
  386. case PAGE_LAP_DATA:
  387. usMySetCount = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT);
  388. usOpponentSetCount = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  389. break;
  390. case PAGE_LENGTH_DATA:
  391. usMyGameCount = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT);
  392. usOpponentGameCount = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  393. break;
  394. case PAGE_METABOLIC_DATA:
  395. ucMaxHeartRate = pucRxBuffer_[4];
  396. ucAvgHeartRate = pucRxBuffer_[5];
  397. usTotalCalories = pucRxBuffer_[6] | (pucRxBuffer_[7] << BYTE_SHIFT);
  398. break;
  399. case PAGE_SPEED_DATA:
  400. usMaxRunningSpeed = pucRxBuffer_[4] | (pucRxBuffer_[5] << BYTE_SHIFT);
  401. usAvgRunningSpeed = pucRxBuffer_[6] | (pucRxBuffer_[5] << BYTE_SHIFT);
  402. break;
  403. case PAGE_DISTANCE_DATA:
  404. uiTotalDistance = pucRxBuffer_[4]
  405. | (pucRxBuffer_[5] << BYTE_SHIFT)
  406. | (pucRxBuffer_[6] << (BYTE_SHIFT * 2))
  407. | (pucRxBuffer_[7] << (BYTE_SHIFT * 3));
  408. break;
  409. default:
  410. break;
  411. }
  412. }
  413. };