antplus_bikepower.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  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 BP_REV 2.0 // Device Profile Revision Number
  10. public ref class BikePower
  11. {
  12. public:
  13. // Channel Parameters
  14. static const UCHAR DEVICE_TYPE = 11;
  15. static const UCHAR TX_TYPE = 5;
  16. static const USHORT MSG_PERIOD = 8182; // ~4.049 Hz
  17. // Data Pages
  18. static const UCHAR PAGE_CALIBRATION = 0x01;
  19. static const UCHAR PAGE_GET_SET_PARAMETERS = 0x02;
  20. static const UCHAR PAGE_POWER = 0x10;
  21. static const UCHAR PAGE_WHEEL_TORQUE = 0x11;
  22. static const UCHAR PAGE_CRANK_TORQUE = 0x12;
  23. static const UCHAR PAGE_TEPS = 0x13;
  24. static const UCHAR PAGE_CTF = 0x20;
  25. // Subpages
  26. static const UCHAR SUBPAGE_CRANK_PARAMETERS = 0x01;
  27. // Calibration Messages
  28. static const UCHAR CAL_REQUEST = 0xAA;
  29. static const UCHAR CAL_AUTOZERO_CONFIG = 0xAB;
  30. static const UCHAR CAL_SUCCESS = 0xAC;
  31. static const UCHAR CAL_FAIL = 0xAF;
  32. static const UCHAR CAL_CTF = 0x10;
  33. static const UCHAR CAL_TORQUE_METER_CAPABILITIES = 0x12;
  34. static const UCHAR CAL_RAW_TORQUE = 0x13;
  35. static const UCHAR CTF_OFFSET = 0x01;
  36. static const UCHAR CTF_SLOPE = 0x02;
  37. static const UCHAR CTF_SERIAL = 0x03;
  38. static const UCHAR CTF_ACK = 0xAC;
  39. // Reserved/invalid values
  40. static const UCHAR RESERVED = 0xFF;
  41. static const UCHAR RESERVED_DESCR = 0x00;
  42. static const SHORT INVALID_TORQUE = -0x7FFF; // Signed 0xFFFF
  43. static const UCHAR INVALID_CADENCE = 0xFF;
  44. static const UCHAR INVALID_CRANK_LENGTH = 0xFF;
  45. static const UCHAR INVALID_TEPS = 0xFF;
  46. static const UCHAR COMBINED_PEDAL_SMOOTHNESS = 0xFE;
  47. static const UCHAR AUTO_CRANK = 0xFE;
  48. static const UCHAR READ_ONLY = 0x00;
  49. static const USHORT MIN_SLOPE = 100;
  50. static const USHORT MAX_SLOPE = 500;
  51. static const USHORT MIN_OFFSET = 0;
  52. static const USHORT MAX_OFFSET = 65535;
  53. static const SHORT MAX_CALDATA = 32767;
  54. static const SHORT MIN_CALDATA = -32768;
  55. static const UCHAR MAX_NOEVENT = 12;
  56. // Interleaving requirements
  57. static const UCHAR INTERVAL_COMMON_AND_AZ = 121; // Pages 80, 81 & Auto Zero Support interleaved every 121 messages (~30.25s)
  58. static const UCHAR INTERVAL_BATTERY = 61; // Page 82 interleaved every 61 messages (~15.25s)
  59. static const UCHAR INTERVAL_MIN_POWER = 9; // Minimum: Interleave basic power every 9th message
  60. static const UCHAR INTERVAL_POWER = 5; // Preferred: Interleave basic power every 5th message
  61. static const UCHAR INTERVAL_TEPS = 5; // Preferred: Interleave once every 5 messages
  62. static const UCHAR INTERVAL_MAX_POWER = 2; // Maximum: Interleave basic power every other page
  63. static const UCHAR MAX_RETRY_CTF = 40; // CTF Offset message must be sent for 10 seconds (40 messages at 4Hz)
  64. enum class AutoZeroStatus : UCHAR
  65. {
  66. OFF = 0x00,
  67. ON = 0x01,
  68. UNSUPPORTED = 0xFF
  69. };
  70. enum class SensorType : UCHAR
  71. {
  72. UNKNOWN,
  73. POWER_ONLY,
  74. TORQUE_WHEEL,
  75. TORQUE_CRANK,
  76. CRANK_TORQUE_FREQ
  77. };
  78. enum class UpdateType : UCHAR
  79. {
  80. EVENT_SYNCH,
  81. TIME_SYNCH
  82. };
  83. enum class CrankLengthStatus : UCHAR
  84. {
  85. LENGTH_INVALID = 0x00,
  86. DEFAULT_LENGTH_USED = 0x01,
  87. LENGTH_MANUALLY_SET = 0x02,
  88. LENGTH_AUTOMATICALLY_SET = 0x03,
  89. };
  90. enum class SWMistmatchStatus : UCHAR
  91. {
  92. UNDEFINED = 0x00,
  93. RIGHT_SENSOR_OLDER = 0x01,
  94. LEFT_SENSOR_OLDER = 0x02,
  95. SW_SAME = 0x03,
  96. };
  97. enum class SensorStatus : UCHAR
  98. {
  99. UNDEFINED = 0x00,
  100. LEFT_PRESENT = 0x01,
  101. RIGHT_PRESENT = 0x02,
  102. LEFT_RIGHT_PRESENT = 0x03,
  103. };
  104. // Error handling
  105. // Flags indicate errors causing the exception
  106. ref class Error : public System::Exception{
  107. public:
  108. BOOL bBadReserved; // Invalid values on reserved fields
  109. BOOL bUndefPage; // Undefined page
  110. BOOL bUndefCalID; // Undefined calibration message ID
  111. BOOL bUndefCTFID; // Undefined CTF calibration message ID
  112. BOOL bUndefCTFAck; // Undefined CTF Acked message
  113. BOOL bUndefAutoZero; // Undefined auto zero status
  114. enum class Code : UCHAR // Error code definitions
  115. {
  116. INVALID_RESERVED, // Invalid value in reserved field
  117. UNDEF_PAGE, // Undefined data page
  118. UNDEF_CAL_ID, // Undefined calibration message ID
  119. UNDEF_CTF_ID, // Undefined CTF calibration message ID
  120. UNDEF_CTF_ACKED, // Undefined CTF acked message
  121. UNDEF_AUTOZERO, // Invalid auto zero status
  122. };
  123. Error()
  124. {
  125. ClearFlags();
  126. }
  127. Error(Code eCode1_)
  128. {
  129. ClearFlags();
  130. SetFlags(eCode1_);
  131. }
  132. Error(Code eCode1_, Code eCode2_)
  133. {
  134. ClearFlags();
  135. SetFlags(eCode1_);
  136. SetFlags(eCode2_);
  137. }
  138. private:
  139. void ClearFlags()
  140. {
  141. bBadReserved = FALSE;
  142. bUndefPage = FALSE;
  143. bUndefCalID = FALSE;
  144. bUndefCTFID = FALSE;
  145. bUndefCTFAck = FALSE;
  146. bUndefAutoZero = FALSE;
  147. }
  148. void SetFlags(Code eCode_)
  149. {
  150. switch(eCode_)
  151. {
  152. case Code::INVALID_RESERVED:
  153. bBadReserved = TRUE;
  154. break;
  155. case Code::UNDEF_PAGE:
  156. bUndefPage = TRUE;
  157. break;
  158. case Code::UNDEF_CAL_ID:
  159. bUndefCalID = TRUE;
  160. break;
  161. case Code::UNDEF_CTF_ID:
  162. bUndefCTFID = TRUE;
  163. break;
  164. case Code::UNDEF_CTF_ACKED:
  165. bUndefCTFAck = TRUE;
  166. break;
  167. case Code::UNDEF_AUTOZERO:
  168. bUndefAutoZero = TRUE;
  169. break;
  170. default:
  171. break;
  172. }
  173. }
  174. };
  175. public:
  176. SensorType eType; // Type of sensor
  177. // Get/Set Crank Parameters Data Page
  178. UCHAR ucSubpageNumber;
  179. UCHAR ucCrankLength;
  180. UCHAR ucSensorStatus;
  181. UCHAR ucSensorCapabilities;
  182. // Standard Power-Only Main Data Page
  183. UCHAR ucPowEventCount; // Power event count
  184. UCHAR ucCadence; // Instantaneous crank cadence (rpm)
  185. UCHAR ucPedalPower; // Pedal power (%)
  186. USHORT usAcumPower; // Cumulative power (W)
  187. USHORT usPower; // Instantaneous power (W)
  188. // Standard Wheel Torque Main Data Page
  189. UCHAR ucWTEventCount; // Wheel torque event count
  190. UCHAR ucWheelTicks; // Wheel revolutions
  191. USHORT usAcumWheelPeriod2048; // Cumulative wheel period (1/2048 s)
  192. USHORT usAcumTorque32; // Cumulative torque (1/32 Nm)
  193. // Standard Crank Torque Main Data Page
  194. UCHAR ucCTEventCount; // Crank torque event count
  195. UCHAR ucCrankTicks; // Crank revolutions
  196. USHORT usAcumCrankPeriod2048; // Cumulative crank period (1/2048 s)
  197. // Crank Torque Frequency Main Data Page
  198. UCHAR ucCTFEventCount; // Rotation event count (pedal revolutions)
  199. USHORT usSlope10; // Variation of output frequency (1/10 Hz/Nm)
  200. USHORT usTime2000; // Time of most recent rotation event (1/2000 s)
  201. USHORT usTorqueTicks; // Count of most recent torque event
  202. // Torque Effectiveness and Pedal Smoothness Main Data Page
  203. UCHAR ucLeftTorqueEffectiveness; // Left torque effectiveness (0.5%)
  204. UCHAR ucRightTorqueEffectiveness; // Right torque effectiveness (0.5%)
  205. UCHAR ucLeftPedalSmoothness; // Left pedal smoothness (0.5%)
  206. UCHAR ucRightPedalSmoothness; // Right pedal smoothness (0.5%)
  207. // General Calibration Response Main Data Page
  208. SHORT sCalibrationData; // Calibration data (signed, no units)
  209. // Auto Zero Configuration Main Data Page
  210. // Torque Sensor Capabilities Main Data Page
  211. BOOL bAutoZeroEnable; // Sensor description
  212. BOOL bAutoZeroOn; // Auto zero status
  213. SHORT sRawTorque; // Raw torque counts (signed, no units)
  214. SHORT sOffsetTorque; // Offset torque (signed, no units)
  215. // Torque-Frequency Defined Calibration Main Data Pages
  216. USHORT usCalOffset; // Offset (100-1000)
  217. USHORT usCalSlope; // Slope (100-500)
  218. USHORT usCalSerialNum; // Serial number
  219. UCHAR ucAckedCTFMsg; // CTF message acknowledged
  220. // General Calibration
  221. UCHAR ucRxCalibrationID; // Calibration Message ID received
  222. UCHAR ucRxCTFMsgID; // CTF Calibration Message ID received
  223. // Error handling
  224. BOOL bValidation; // Turns validation on/off
  225. private:
  226. UCHAR ucPowMsgCount; // Power message count (to determine sensor type)
  227. UCHAR ucTorqueMsgCount; // Torque message count (to determine sensor type)
  228. UCHAR ucPrevTorqueMsgCount; // Previous torque message count
  229. static const UCHAR NUM_MSGS = 4; // Maximum number of messages before deciding type of sensor
  230. public:
  231. BikePower()
  232. {
  233. bValidation = FALSE;
  234. eType = SensorType::UNKNOWN;
  235. ucPowMsgCount = 0;
  236. ucTorqueMsgCount = 0;
  237. ucPrevTorqueMsgCount = 0;
  238. // Set cumulative values to 0
  239. usAcumTorque32 = 0;
  240. usAcumWheelPeriod2048 = 0;
  241. usAcumCrankPeriod2048 = 0;
  242. usAcumPower = 0;
  243. // Set event counters to 0
  244. ucPowEventCount = 0;
  245. ucWTEventCount = 0;
  246. ucWheelTicks = 0;
  247. ucCTEventCount = 0;
  248. ucCrankTicks = 0;
  249. ucCTFEventCount = 0;
  250. usTorqueTicks = 0;
  251. }
  252. ~BikePower()
  253. {
  254. }
  255. public:
  256. /**************************************************************************
  257. * BikePower::Decode
  258. *
  259. * Decodes all main data pages and calibration pages
  260. * Exceptions are thrown when dealing with non compliant pages
  261. *
  262. * pucRxBuffer_: pointer to the buffer containing the received data
  263. *
  264. * returns: N/A
  265. *
  266. **************************************************************************/
  267. void Decode(UCHAR* pucRxBuffer_)
  268. {
  269. switch(pucRxBuffer_[0])
  270. {
  271. case PAGE_GET_SET_PARAMETERS:
  272. ucSubpageNumber = pucRxBuffer_[1];
  273. ucCrankLength = pucRxBuffer_[4];
  274. ucSensorStatus = pucRxBuffer_[5];
  275. ucSensorCapabilities = pucRxBuffer_[6];
  276. break;
  277. case PAGE_POWER:
  278. if(eType == SensorType::UNKNOWN || eType == SensorType::CRANK_TORQUE_FREQ)
  279. {
  280. if(++ucPowMsgCount >= NUM_MSGS)
  281. {
  282. ucPowMsgCount = 0;
  283. eType = SensorType::POWER_ONLY; // After receiving NUM_MSGS successive power messages, determine it is a basic power sensor
  284. }
  285. }
  286. if(eType == SensorType::TORQUE_CRANK || eType == SensorType::TORQUE_WHEEL)
  287. {
  288. // If it is a torque sensor, and we stop receiving torque messages, set type to unknown
  289. if(ucPrevTorqueMsgCount == ucTorqueMsgCount)
  290. {
  291. eType = SensorType::UNKNOWN;
  292. ucPowMsgCount = 0;
  293. ucTorqueMsgCount = 0;
  294. ucPrevTorqueMsgCount = 0;
  295. }
  296. ucPrevTorqueMsgCount = ucTorqueMsgCount;
  297. }
  298. ucPowEventCount = pucRxBuffer_[1]; // Power event count
  299. ucPedalPower = pucRxBuffer_[2]; // Pedal Power
  300. ucCadence = pucRxBuffer_[3]; // Cadence
  301. usAcumPower = pucRxBuffer_[4] | pucRxBuffer_[5] << 8; // Cumulative power (W)
  302. usPower = pucRxBuffer_[6] | pucRxBuffer_[7] << 8; // Instantaneous power (W)
  303. //if(pucRxBuffer_[2] != RESERVED && bValidation)
  304. //throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: Invalid value in reserved field
  305. break;
  306. case PAGE_WHEEL_TORQUE:
  307. if(eType != SensorType::TORQUE_WHEEL)
  308. {
  309. eType = SensorType::TORQUE_WHEEL; // Dealing with wheel torque sensor
  310. ucTorqueMsgCount = 0;
  311. ucPrevTorqueMsgCount = 0;
  312. }
  313. ucTorqueMsgCount++; // Update torque message count
  314. ucWTEventCount = pucRxBuffer_[1]; // Wheel torque event count
  315. ucWheelTicks = pucRxBuffer_[2]; // Wheel revolutions
  316. ucCadence = pucRxBuffer_[3]; // Cadence
  317. usAcumWheelPeriod2048 = pucRxBuffer_[4] | pucRxBuffer_[5] << 8; // Cumulative wheel period (1/2048 s)
  318. usAcumTorque32 = pucRxBuffer_[6] | pucRxBuffer_[7] << 8; // Cumulative torque (1/32 Nm)
  319. break;
  320. case PAGE_CRANK_TORQUE:
  321. if(eType != SensorType::TORQUE_CRANK)
  322. {
  323. eType = SensorType::TORQUE_CRANK; // Dealing with crank torque sensor
  324. ucTorqueMsgCount = 0;
  325. ucPrevTorqueMsgCount = 0;
  326. }
  327. ucTorqueMsgCount++; // Update torque message count
  328. ucCTEventCount = pucRxBuffer_[1]; // Crank torque event count
  329. ucCrankTicks = pucRxBuffer_[2]; // Crank revolutions
  330. ucCadence = pucRxBuffer_[3]; // Cadence
  331. usAcumCrankPeriod2048 = pucRxBuffer_[4] | pucRxBuffer_[5] << 8; // Cumulative crank period (1/2048 s)
  332. usAcumTorque32 = pucRxBuffer_[6] | pucRxBuffer_[7] << 8; // Cumulative torque (1/32 Nm)
  333. break;
  334. case PAGE_TEPS:
  335. ucPowEventCount = pucRxBuffer_[1]; // Power event count
  336. ucLeftTorqueEffectiveness = pucRxBuffer_[2];
  337. ucRightTorqueEffectiveness = pucRxBuffer_[3];
  338. ucLeftPedalSmoothness = pucRxBuffer_[4];
  339. ucRightPedalSmoothness = pucRxBuffer_[5];
  340. break;
  341. case PAGE_CTF:
  342. eType = SensorType::CRANK_TORQUE_FREQ; // Dealing with crank torque frequency sensor
  343. ucCTFEventCount = pucRxBuffer_[1]; // Crank torque frequency event count (pedal revolutions)
  344. usSlope10 = pucRxBuffer_[3] | pucRxBuffer_[2] << 8; // Variation of output frequency (10 Hz/Nm)
  345. usTime2000 = pucRxBuffer_[5] | pucRxBuffer_[4] << 8; // Time of most recent rotation event (1/2000 s)
  346. usTorqueTicks = pucRxBuffer_[7] | pucRxBuffer_[6] << 8; // Count of most recent torque event
  347. break;
  348. case PAGE_CALIBRATION:
  349. ucRxCalibrationID = pucRxBuffer_[1]; // Current calibration page received
  350. switch(ucRxCalibrationID)
  351. {
  352. case CAL_TORQUE_METER_CAPABILITIES:
  353. bAutoZeroEnable = (BOOL) (pucRxBuffer_[2] & 0x01); // Auto Zero Enabled
  354. bAutoZeroOn = (BOOL) ((pucRxBuffer_[2] & 0x02) >> 1); // Auto Zero Status
  355. sRawTorque = pucRxBuffer_[3] | pucRxBuffer_[4] << 8; // Raw torque (signed)
  356. sOffsetTorque = pucRxBuffer_[5] | pucRxBuffer_[6] << 8; // Offset torque (signed)
  357. if(bValidation && ((pucRxBuffer_[2] & 0xFC) != RESERVED_DESCR || pucRxBuffer_[7] != RESERVED))
  358. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: Invalid value on reserved field
  359. break;
  360. case CAL_REQUEST:
  361. if(bValidation && (pucRxBuffer_[2] != RESERVED || pucRxBuffer_[3] != RESERVED || pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED || pucRxBuffer_[6] != RESERVED || pucRxBuffer_[7] != RESERVED))
  362. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: Invalid value on reserved field
  363. break;
  364. case CAL_AUTOZERO_CONFIG:
  365. {
  366. BOOL bUndefAZ = FALSE;
  367. switch((AutoZeroStatus) pucRxBuffer_[2])
  368. {
  369. case AutoZeroStatus::OFF:
  370. bAutoZeroEnable = TRUE;
  371. bAutoZeroOn = FALSE;
  372. break;
  373. case AutoZeroStatus::ON:
  374. bAutoZeroEnable = TRUE;
  375. bAutoZeroOn = TRUE;
  376. break;
  377. case AutoZeroStatus::UNSUPPORTED:
  378. bAutoZeroEnable = FALSE;
  379. bAutoZeroOn = FALSE;
  380. break;
  381. default:
  382. bUndefAZ = TRUE;
  383. break;
  384. }
  385. if(bValidation)
  386. {
  387. if(pucRxBuffer_[3] != RESERVED || pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED || pucRxBuffer_[6] != RESERVED || pucRxBuffer_[7] != RESERVED)
  388. {
  389. if(bUndefAZ)
  390. throw gcnew Error(Error::Code::INVALID_RESERVED, Error::Code::UNDEF_AUTOZERO); // Error: Bad auto zero status and reserved field
  391. else
  392. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: bad reserved field
  393. }
  394. else if(bUndefAZ)
  395. throw gcnew Error(Error::Code::UNDEF_AUTOZERO); // Error: undefined auto zero status
  396. }
  397. break;
  398. }
  399. case CAL_SUCCESS:
  400. case CAL_FAIL: // intentional fall thru
  401. {
  402. sCalibrationData = pucRxBuffer_[6] | pucRxBuffer_[7] << 8; // Calibration data (signed)
  403. BOOL bUndefAZ = FALSE;
  404. switch((AutoZeroStatus) pucRxBuffer_[2])
  405. {
  406. case AutoZeroStatus::OFF:
  407. bAutoZeroEnable = TRUE;
  408. bAutoZeroOn = FALSE;
  409. break;
  410. case AutoZeroStatus::ON:
  411. bAutoZeroEnable = TRUE;
  412. bAutoZeroOn = TRUE;
  413. break;
  414. case AutoZeroStatus::UNSUPPORTED:
  415. bAutoZeroEnable = FALSE;
  416. bAutoZeroOn = FALSE;
  417. break;
  418. default:
  419. bUndefAZ = TRUE;
  420. break;
  421. }
  422. if(bValidation)
  423. {
  424. if(pucRxBuffer_[3] != RESERVED || pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED)
  425. {
  426. if(bUndefAZ)
  427. throw gcnew Error(Error::Code::INVALID_RESERVED, Error::Code::UNDEF_AUTOZERO); // Error: Bad auto zero status and reserved field
  428. else
  429. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: bad reserved field
  430. }
  431. else if(bUndefAZ)
  432. throw gcnew Error(Error::Code::UNDEF_AUTOZERO); // Error: invalid auto zero status
  433. }
  434. break;
  435. }
  436. case CAL_CTF:
  437. ucRxCTFMsgID = pucRxBuffer_[2];
  438. switch(ucRxCTFMsgID)
  439. {
  440. case CTF_OFFSET:
  441. usCalOffset = pucRxBuffer_[7] | pucRxBuffer_[6] << 8;
  442. if(bValidation && (pucRxBuffer_[3] != RESERVED || pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED))
  443. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: bad reserved fields
  444. break;
  445. case CTF_SLOPE:
  446. usCalSlope = pucRxBuffer_[7] | pucRxBuffer_[6] << 8;
  447. if(bValidation && (pucRxBuffer_[3] != RESERVED || pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED))
  448. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: bad reserved field
  449. break;
  450. case CTF_SERIAL:
  451. usCalSerialNum = pucRxBuffer_[7] | pucRxBuffer_[6] << 8;
  452. if(bValidation && (pucRxBuffer_[3] != RESERVED || pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED))
  453. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: bad reserved field
  454. break;
  455. case CTF_ACK:
  456. ucAckedCTFMsg = pucRxBuffer_[3];
  457. if(bValidation)
  458. {
  459. if(pucRxBuffer_[4] != RESERVED || pucRxBuffer_[5] != RESERVED || pucRxBuffer_[6] != RESERVED || pucRxBuffer_[7] != RESERVED)
  460. {
  461. if(ucAckedCTFMsg != CTF_SLOPE && ucAckedCTFMsg != CTF_SERIAL)
  462. throw gcnew Error(Error::Code::INVALID_RESERVED, Error::Code::UNDEF_CTF_ACKED); // Error: bad reserved field and unsupported acked message
  463. else
  464. throw gcnew Error(Error::Code::INVALID_RESERVED); // Error: bad reserved field
  465. }
  466. else if(ucAckedCTFMsg != CTF_SLOPE && ucAckedCTFMsg != CTF_SERIAL)
  467. throw gcnew Error(Error::Code::UNDEF_CTF_ACKED); // Error: unsupported acked message
  468. }
  469. break;
  470. default:
  471. if(bValidation)
  472. throw gcnew Error(Error::Code::UNDEF_CTF_ID); // Error: Undefined CTF message
  473. break;
  474. }
  475. break;
  476. default:
  477. if(bValidation)
  478. throw gcnew Error(Error::Code::UNDEF_CAL_ID); // Error: Undefined calibration message
  479. break;
  480. }
  481. break;
  482. default:
  483. if(bValidation)
  484. throw gcnew Error(Error::Code::UNDEF_PAGE); // Error: Undefined page
  485. break;
  486. }
  487. }
  488. /**************************************************************************
  489. * BikePower::EncodeMainData
  490. *
  491. * Encodes main bike power data pages:
  492. * - Basic Power
  493. * - Wheel Torque
  494. * - Crank Torque
  495. * - Crank Torque Frequency
  496. *
  497. * Exceptions are thrown when dealing with invalid data
  498. *
  499. * ucPageNum_: number of page to encode
  500. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  501. *
  502. * returns: N/A
  503. *
  504. **************************************************************************/
  505. void EncodeMainData(UCHAR ucPageNum_, UCHAR* pucTxBuffer_)
  506. {
  507. switch(ucPageNum_)
  508. {
  509. case PAGE_GET_SET_PARAMETERS:
  510. pucTxBuffer_[0] = ucPageNum_;
  511. pucTxBuffer_[1] = ucSubpageNumber;
  512. pucTxBuffer_[2] = RESERVED;
  513. pucTxBuffer_[3] = RESERVED;
  514. pucTxBuffer_[4] = ucCrankLength;
  515. pucTxBuffer_[5] = ucSensorStatus;
  516. pucTxBuffer_[6] = ucSensorCapabilities;
  517. pucTxBuffer_[7] = RESERVED;
  518. break;
  519. case PAGE_POWER:
  520. pucTxBuffer_[0] = ucPageNum_;
  521. pucTxBuffer_[1] = ucPowEventCount; // Power event count
  522. pucTxBuffer_[2] = ucPedalPower; // Pedal Power
  523. pucTxBuffer_[3] = ucCadence; // Cadence
  524. pucTxBuffer_[4] = usAcumPower & 0xFF; // Cumulative power (W)
  525. pucTxBuffer_[5] = (usAcumPower >> 8) & 0xFF;
  526. pucTxBuffer_[6] = usPower & 0xFF; // Instantaneous power (W)
  527. pucTxBuffer_[7] = (usPower >> 8) & 0xFF;
  528. break;
  529. case PAGE_WHEEL_TORQUE:
  530. pucTxBuffer_[0] = ucPageNum_;
  531. pucTxBuffer_[1] = ucWTEventCount; // Wheel torque event count
  532. pucTxBuffer_[2] = ucWheelTicks; // Wheel revolutions
  533. pucTxBuffer_[3] = ucCadence; // Cadence
  534. pucTxBuffer_[4] = usAcumWheelPeriod2048 & 0xFF; // Cumulative wheel period (1/2048 s)
  535. pucTxBuffer_[5] = (usAcumWheelPeriod2048 >> 8) & 0xFF;
  536. pucTxBuffer_[6] = usAcumTorque32 & 0xFF; // Cumulative torque (1/32 Nm)
  537. pucTxBuffer_[7] = (usAcumTorque32 >> 8) & 0xFF;
  538. break;
  539. case PAGE_CRANK_TORQUE:
  540. pucTxBuffer_[0] = ucPageNum_;
  541. pucTxBuffer_[1] = ucCTEventCount; // Crank torque event count
  542. pucTxBuffer_[2] = ucCrankTicks; // Crank revolutions
  543. pucTxBuffer_[3] = ucCadence; // Cadence
  544. pucTxBuffer_[4] = usAcumCrankPeriod2048 & 0xFF; // Cumulative crank period (1/2048 s)
  545. pucTxBuffer_[5] = (usAcumCrankPeriod2048 >> 8) & 0xFF;
  546. pucTxBuffer_[6] = usAcumTorque32 & 0xFF; // Cumulative torque (1/32 Nm)
  547. pucTxBuffer_[7] = (usAcumTorque32 >> 8) & 0xFF;
  548. break;
  549. case PAGE_TEPS:
  550. pucTxBuffer_[0] = ucPageNum_;
  551. pucTxBuffer_[1] = ucPowEventCount; // tied to event counter in power only page
  552. pucTxBuffer_[2] = ucLeftTorqueEffectiveness;
  553. pucTxBuffer_[3] = ucRightTorqueEffectiveness;
  554. pucTxBuffer_[4] = ucLeftPedalSmoothness;
  555. pucTxBuffer_[5] = ucRightPedalSmoothness;
  556. pucTxBuffer_[6] = RESERVED;
  557. pucTxBuffer_[7] = RESERVED;
  558. break;
  559. case PAGE_CTF:
  560. pucTxBuffer_[0] = ucPageNum_;
  561. pucTxBuffer_[1] = ucCTFEventCount; // Crank torque frequency event count (pedal revolutions)
  562. pucTxBuffer_[2] = (usSlope10 >> 8) & 0xFF; // Variation of output frequency (10 Hz/Nm)
  563. pucTxBuffer_[3] = usSlope10 & 0xFF;
  564. pucTxBuffer_[4] = (usTime2000 >> 8) & 0xFF; // Time of most recent rotation event (1/2000 s)
  565. pucTxBuffer_[5] = usTime2000 & 0xFF;
  566. pucTxBuffer_[6] = (usTorqueTicks >> 8) & 0xFF; // Count of most recent torque event
  567. pucTxBuffer_[7] = usTorqueTicks & 0xFF;
  568. break;
  569. default:
  570. if(bValidation)
  571. throw gcnew Error(Error::Code::UNDEF_PAGE); // Error: Undefined page
  572. break;
  573. }
  574. }
  575. /**************************************************************************
  576. * BikePower::EncodeAutoZeroSupport
  577. *
  578. * Encodes the auto zero support (torque meter capabilities) page (0x12)
  579. *
  580. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  581. *
  582. * returns: N/A
  583. *
  584. **************************************************************************/
  585. void EncodeAutoZeroSupport(UCHAR* pucTxBuffer_)
  586. {
  587. pucTxBuffer_[0] = PAGE_CALIBRATION;
  588. pucTxBuffer_[1] = CAL_TORQUE_METER_CAPABILITIES;
  589. pucTxBuffer_[2] = (UCHAR) bAutoZeroEnable; // Auto Zero Enabled (bit 0)
  590. pucTxBuffer_[2] |= (UCHAR) bAutoZeroOn << 1; // Auto Zero Status (bit 1)
  591. pucTxBuffer_[3] = sRawTorque & 0xFF; // Raw torque (LSB)
  592. pucTxBuffer_[4] = (sRawTorque >> 8) & 0xFF; // Raw torque (MSB)
  593. pucTxBuffer_[5] = sOffsetTorque & 0xFF; // Offset torque (LSB)
  594. pucTxBuffer_[6] = (sOffsetTorque >> 8) & 0xFF; // Offset torque (MSB)
  595. pucTxBuffer_[7] = RESERVED;
  596. }
  597. /**************************************************************************
  598. * BikePower::EncodeManualCalibrationRequest
  599. *
  600. * Encodes a manual calibration request (0xAA)
  601. *
  602. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  603. *
  604. * returns: N/A
  605. *
  606. **************************************************************************/
  607. void EncodeManualCalibrationRequest(UCHAR* pucTxBuffer_)
  608. {
  609. pucTxBuffer_[0] = PAGE_CALIBRATION;
  610. pucTxBuffer_[1] = CAL_REQUEST;
  611. pucTxBuffer_[2] = RESERVED;
  612. pucTxBuffer_[3] = RESERVED;
  613. pucTxBuffer_[4] = RESERVED;
  614. pucTxBuffer_[5] = RESERVED;
  615. pucTxBuffer_[6] = RESERVED;
  616. pucTxBuffer_[7] = RESERVED;
  617. }
  618. /**************************************************************************
  619. * BikePower::EncodeAZCalibrationRequest
  620. *
  621. * Encodes an auto zero calibration request (0xAB)
  622. *
  623. * bAutoZeroEnabled_ = TRUE if AutoZero is supported, FALSE otherwise
  624. * bAutoZeroOn_ = TRUE if AutoZero is ON, FALSE otherwise
  625. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  626. *
  627. * returns: N/A
  628. *
  629. **************************************************************************/
  630. void EncodeAZCalibrationRequest(BOOL bAutoZeroEnabled_, BOOL bAutoZeroOn_, UCHAR* pucTxBuffer_)
  631. {
  632. pucTxBuffer_[0] = PAGE_CALIBRATION;
  633. pucTxBuffer_[1] = CAL_AUTOZERO_CONFIG;
  634. if(!bAutoZeroEnabled_)
  635. pucTxBuffer_[2] = (UCHAR) AutoZeroStatus::UNSUPPORTED; // Encode auto zero status
  636. else if(bAutoZeroOn_)
  637. pucTxBuffer_[2] = (UCHAR) AutoZeroStatus::ON;
  638. else
  639. pucTxBuffer_[2] = (UCHAR) AutoZeroStatus::OFF;
  640. pucTxBuffer_[3] = RESERVED;
  641. pucTxBuffer_[4] = RESERVED;
  642. pucTxBuffer_[5] = RESERVED;
  643. pucTxBuffer_[6] = RESERVED;
  644. pucTxBuffer_[7] = RESERVED;
  645. }
  646. /**************************************************************************
  647. * BikePower::EncodeCalibrationResponse
  648. *
  649. * Encodes a calibration response (0xAC or 0xAF)
  650. *
  651. * ucCalResponseID_: response to send (either CAL_FAIL or CAL_SUCCESS)
  652. * bAutoZeroEnabled_ = TRUE if AutoZero is supported, FALSE otherwise
  653. * bAutoZeroOn_ = TRUE if AutoZero is ON, FALSE otherwise
  654. * sCalData_ = calibration data to send in response
  655. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  656. *
  657. * returns: N/A
  658. *
  659. **************************************************************************/
  660. void EncodeCalibrationResponse(UCHAR ucCalResponseID_, BOOL bAutoZeroEnabled_, BOOL bAutoZeroOn_, SHORT sCalData_, UCHAR* pucTxBuffer_)
  661. {
  662. pucTxBuffer_[0] = PAGE_CALIBRATION;
  663. pucTxBuffer_[1] = ucCalResponseID_;
  664. if(!bAutoZeroEnabled_)
  665. pucTxBuffer_[2] = (UCHAR) AutoZeroStatus::UNSUPPORTED; // Encode auto zero status
  666. else if(bAutoZeroOn_)
  667. pucTxBuffer_[2] = (UCHAR) AutoZeroStatus::ON;
  668. else
  669. pucTxBuffer_[2] = (UCHAR) AutoZeroStatus::OFF;
  670. pucTxBuffer_[3] = RESERVED;
  671. pucTxBuffer_[4] = RESERVED;
  672. pucTxBuffer_[5] = RESERVED;
  673. pucTxBuffer_[6] = sCalData_ & 0xFF; // Calibration data, low byte
  674. pucTxBuffer_[7] = (sCalData_ >> 8) & 0x0FF; // Calibration data, high byte
  675. }
  676. /**************************************************************************
  677. * BikePower::EncodeCTFCalibrationPage
  678. *
  679. * Encodes a CTF calibration page
  680. *
  681. * ucCTFMsgID_: ID of CTF defined message; either CTF_OFFSET, CTF_SLOPE or CTF_SERIAL
  682. * usParam_: parameter to include in page (offset, slope or serial number)
  683. * pucTxBuffer_: pointer to the buffer that will store the encoded data
  684. *
  685. * returns: N/A
  686. *
  687. **************************************************************************/
  688. void EncodeCTFCalibrationPage(UCHAR ucCTFMsgID_, USHORT usParam_, UCHAR* pucTxBuffer_)
  689. {
  690. switch(ucCTFMsgID_)
  691. {
  692. case CTF_OFFSET:
  693. case CTF_SLOPE:
  694. case CTF_SERIAL: // Intentional fall thru
  695. pucTxBuffer_[0] = PAGE_CALIBRATION;
  696. pucTxBuffer_[1] = CAL_CTF;
  697. pucTxBuffer_[2] = ucCTFMsgID_;
  698. pucTxBuffer_[3] = RESERVED;
  699. pucTxBuffer_[4] = RESERVED;
  700. pucTxBuffer_[5] = RESERVED;
  701. pucTxBuffer_[6] = (usParam_ >> 8) & 0xFF; // CTF parameter (offset, slope, serial number), high byte
  702. pucTxBuffer_[7] = usParam_ & 0xFF; // CTF parameter (offset, slope, serial number), low byte
  703. break;
  704. case CTF_ACK:
  705. pucTxBuffer_[0] = PAGE_CALIBRATION;
  706. pucTxBuffer_[1] = CAL_CTF;
  707. pucTxBuffer_[2] = ucCTFMsgID_;
  708. pucTxBuffer_[3] = (UCHAR) usParam_; // parameter in this case is message code to acknowledge
  709. pucTxBuffer_[4] = RESERVED;
  710. pucTxBuffer_[5] = RESERVED;
  711. pucTxBuffer_[6] = RESERVED;
  712. pucTxBuffer_[7] = RESERVED;
  713. break;
  714. default:
  715. break;
  716. }
  717. }
  718. /**************************************************************************
  719. * BikePower::IsCadenceInvalid
  720. *
  721. * Checks if the cadence value is invalid
  722. *
  723. * ucCadence_: cadence (rpm)
  724. *
  725. * returns: N/A
  726. *
  727. **************************************************************************/
  728. BOOL IsCadenceInvalid(UCHAR ucCadence_)
  729. {
  730. if(ucCadence_ == INVALID_CADENCE)
  731. return TRUE;
  732. else
  733. return FALSE;
  734. }
  735. /**************************************************************************
  736. * BikePower::IsSlopeInvalid
  737. *
  738. * Checks if the slope is within the boundaries defined in the device profile
  739. *
  740. * usSlope10_: slope (1/10 Nm/Hz)
  741. *
  742. * returns: N/A
  743. *
  744. **************************************************************************/
  745. BOOL IsSlopeInvalid(USHORT usSlope10_)
  746. {
  747. if(usSlope10_ < MIN_SLOPE || usSlope10_ > MAX_SLOPE)
  748. return TRUE;
  749. else
  750. return FALSE;
  751. }
  752. /**************************************************************************
  753. * BikePower::IsRawTorqueInvalid
  754. *
  755. * Checks if the raw torque is invalid
  756. *
  757. * sRawTorque: raw torque
  758. *
  759. * returns: N/A
  760. *
  761. **************************************************************************/
  762. BOOL IsRawTorqueInvalid(SHORT sRawTorque_)
  763. {
  764. if(sRawTorque_ == INVALID_TORQUE)
  765. return TRUE;
  766. else
  767. return FALSE;
  768. }
  769. /**************************************************************************
  770. * BikePower::IsOffsetTorqueInvalid
  771. *
  772. * Checks if the offset torque is invalid
  773. *
  774. * sOffsetTorque_: offset torque
  775. *
  776. * returns: N/A
  777. *
  778. **************************************************************************/
  779. BOOL IsOffsetTorqueInvalid(SHORT sOffsetTorque_)
  780. {
  781. if(sOffsetTorque_ == INVALID_TORQUE)
  782. return TRUE;
  783. else
  784. return FALSE;
  785. }
  786. /**************************************************************************
  787. * BikePower::IsOffsetInvalid
  788. *
  789. * Checks if the offset is within the boundaries defined in the device profile
  790. *
  791. * usCalOffset_: offset
  792. *
  793. * returns: N/A
  794. *
  795. **************************************************************************/
  796. BOOL IsOffsetInvalid(USHORT usCalOffset_)
  797. {
  798. if(usCalOffset_ < MIN_OFFSET || usCalOffset_ > MAX_OFFSET)
  799. return TRUE;
  800. else
  801. return FALSE;
  802. }
  803. };