DecodeCrankTorque.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. 2014
  6. All rights reserved.
  7. */
  8. #include "string.h"
  9. #include "stdlib.h"
  10. #define _USE_MATH_DEFINES
  11. #include "math.h"
  12. #include "RecordOutput.h"
  13. #include "PowerDecoder.h"
  14. #include "DecodeCrankTorque.h"
  15. static BPSAMPLER stState;
  16. static PowerRecordReceiver prrPtr;
  17. static double dRecordInterval;
  18. static double dReSyncInterval;
  19. #define UPDATE_EVENT_BYTE 1
  20. #define CRANK_TICKS_BYTE 2
  21. #define INST_CADENCE_BYTE 3
  22. #define ACCUM_PERIOD_LSB 4
  23. #define ACCUM_PERIOD_MSB 5
  24. #define ACCUM_TORQUE_LSB 6
  25. #define ACCUM_TORQUE_MSB 7
  26. ///////////////////////////////////////////////////////////////////////////////
  27. // void DecodeCrankTorque_Init(double dRecordInterval_, double dTimeBase_)
  28. ///////////////////////////////////////////////////////////////////////////////
  29. //
  30. // Call this to initialize the decoder.
  31. // dTimeBase_ is set to zero to initialize event based decoding; otherwise
  32. // the timebase value is assumed to be the sensor message update rate.
  33. //
  34. ///////////////////////////////////////////////////////////////////////////////
  35. void DecodeCrankTorque_Init(double dRecordInterval_, double dTimeBase_, double dReSyncInterval_, PowerRecordReceiver powerRecordReceiverPtr_)
  36. {
  37. ResamplerOutput_Init(&stState, (int)(dRecordInterval_ * CT_TIME_QUANTIZATION), dRecordInterval_, (int)(dTimeBase_ * CT_TIME_QUANTIZATION));
  38. dRecordInterval = dRecordInterval_;
  39. prrPtr = powerRecordReceiverPtr_;
  40. dReSyncInterval = dReSyncInterval_;
  41. }
  42. ///////////////////////////////////////////////////////////////////////////////
  43. // void DecodeCrankTorque_Message(double dTime_, unsigned char aucByte_[])
  44. ///////////////////////////////////////////////////////////////////////////////
  45. //
  46. // Message event handler interface.
  47. // This is intended to abstract away the top-level messiness of having to
  48. // detect data gaps or duplicates, etc.
  49. //
  50. ///////////////////////////////////////////////////////////////////////////////
  51. void DecodeCrankTorque_Message(double dTime_, unsigned char aucByte_[])
  52. {
  53. // see if the message is new.
  54. if (stState.ucLastEventCount != aucByte_[UPDATE_EVENT_BYTE])
  55. {
  56. if ((dTime_ - stState.dLastMessageTime) > dReSyncInterval)
  57. {
  58. DecodeCrankTorque_Resync(dTime_, aucByte_);
  59. }
  60. else
  61. {
  62. DecodeCrankTorque(dTime_, aucByte_);
  63. }
  64. stState.dLastMessageTime = dTime_;
  65. }
  66. }
  67. ///////////////////////////////////////////////////////////////////////////////
  68. // void DecodeCrankTorque_Resync(double dCurrentTime_, unsigned char aucByte_[])
  69. ///////////////////////////////////////////////////////////////////////////////
  70. //
  71. // Re-establish data baseline.
  72. ///////////////////////////////////////////////////////////////////////////////
  73. void DecodeCrankTorque_Resync(double dCurrentTime_, unsigned char aucByte_[])
  74. {
  75. unsigned short usCurrentAccumTorque;
  76. unsigned short usCurrentAccumPeriod;
  77. // CurrentRecordEpoch is the last time that we should have had a data record.
  78. double dCurrentRecordEpoch = (floor(dCurrentTime_ / dRecordInterval)) * dRecordInterval;
  79. if ((stState.dLastRecordTime != 0) && (dCurrentRecordEpoch - stState.dLastRecordTime < MAXIMUM_TIME_GAP))
  80. {
  81. // Figure out how many records we missed based on the receive timestamps.
  82. stState.ucRecordGapCount = (unsigned char)((dCurrentRecordEpoch - stState.dLastRecordTime + 0.5 * dRecordInterval)
  83. / (dRecordInterval)); // We need to fill in the gap with records.
  84. // Transfer the accumulated data to the gap.
  85. stState.fGapEnergy = stState.fAccumEnergy;
  86. stState.fGapRotation = stState.fAccumRotation;
  87. RecordOutput_FillGap(prrPtr, &stState);
  88. }
  89. usCurrentAccumPeriod = aucByte_[ACCUM_PERIOD_LSB];
  90. usCurrentAccumPeriod += ((unsigned short)aucByte_[ACCUM_PERIOD_MSB]) << 8;
  91. usCurrentAccumTorque = aucByte_[ACCUM_TORQUE_LSB];
  92. usCurrentAccumTorque += ((unsigned short)aucByte_[ACCUM_TORQUE_MSB]) << 8;
  93. stState.ucCadence = aucByte_[INST_CADENCE_BYTE];
  94. stState.fAccumEnergy = 0;
  95. stState.fPendingEnergy = 0;
  96. stState.fGapEnergy = 0;
  97. stState.fAccumRotation = 0;
  98. stState.fPendingRotation = 0;
  99. stState.fGapRotation = 0;
  100. stState.ucRecordGapCount = 0;
  101. stState.ulEventTime = 0;
  102. stState.ulLastRecordTime = 0;
  103. stState.dLastMessageTime = dCurrentTime_;
  104. // Update our saved state.
  105. stState.dLastRecordTime = dCurrentRecordEpoch;
  106. stState.usLastAccumTorque = usCurrentAccumTorque;
  107. stState.usLastAccumPeriod = usCurrentAccumPeriod;
  108. stState.ucLastRotationTicks = aucByte_[CRANK_TICKS_BYTE];
  109. stState.ucLastEventCount = aucByte_[UPDATE_EVENT_BYTE];
  110. }
  111. ///////////////////////////////////////////////////////////////////////////////
  112. //
  113. //
  114. ///////////////////////////////////////////////////////////////////////////////
  115. void DecodeCrankTorque(double dTime_, unsigned char aucByte_[])
  116. {
  117. unsigned long ulNewEventTime;
  118. unsigned long ulEventCadence;
  119. unsigned long ulEventPower;
  120. unsigned short usCurrentAccumTorque;
  121. unsigned short usCurrentAccumPeriod;
  122. unsigned short usDeltaTorque;
  123. unsigned short usDeltaPeriod;
  124. unsigned char ucDeltaEventCount;
  125. unsigned char ucDeltaTicks;
  126. float fEventEnergy;
  127. usCurrentAccumPeriod = aucByte_[ACCUM_PERIOD_LSB];
  128. usCurrentAccumPeriod += ((unsigned short)aucByte_[ACCUM_PERIOD_MSB]) << 8;
  129. usCurrentAccumTorque = aucByte_[ACCUM_TORQUE_LSB];
  130. usCurrentAccumTorque += ((unsigned short)aucByte_[ACCUM_TORQUE_MSB]) << 8;
  131. usDeltaTorque = usCurrentAccumTorque - stState.usLastAccumTorque; // make sure this is done in 16 bit word width!
  132. usDeltaPeriod = usCurrentAccumPeriod - stState.usLastAccumPeriod; // make sure this is done in 16 bit word width!
  133. ucDeltaEventCount = aucByte_[UPDATE_EVENT_BYTE] - stState.ucLastEventCount;
  134. ucDeltaTicks = aucByte_[CRANK_TICKS_BYTE] - stState.ucLastRotationTicks;
  135. stState.ucCadence = aucByte_[INST_CADENCE_BYTE];
  136. // 65535 is an invalid value.
  137. if (usDeltaTorque == 65535)
  138. {
  139. usDeltaTorque = 0;
  140. }
  141. if (usDeltaPeriod && (usDeltaPeriod != 0xFFFF))
  142. {
  143. ulNewEventTime = stState.ulEventTime + (unsigned long)usDeltaPeriod;
  144. ulEventPower = ((long)(M_PI*2048.0 + 0.5) * usDeltaTorque / usDeltaPeriod + 8) >> 4;
  145. ulEventCadence = ((long)ucDeltaTicks * 60L * CT_TIME_QUANTIZATION + (usDeltaPeriod >> 1)) / usDeltaPeriod;
  146. fEventEnergy = (float)(M_PI * (float)usDeltaTorque / 16.0);
  147. }
  148. else
  149. {
  150. // This is basically a non-event.
  151. ulEventPower = 0;
  152. ulEventCadence = 0;
  153. fEventEnergy = 0;
  154. ulNewEventTime = stState.ulEventTime;
  155. }
  156. if (((unsigned short)(ulNewEventTime - stState.ulLastRecordTime)) >= stState.usRecordInterval)
  157. {
  158. // The event occurred after the end of the current record epoch.
  159. // First, figure out the number of records in a gap if it exists. This calculation uses
  160. // implicit truncation in the division so the subtraction can't be done first.
  161. stState.ucRecordGapCount = (unsigned char)((ulNewEventTime / stState.usRecordInterval) - (stState.ulLastRecordTime / stState.usRecordInterval) - 1);
  162. // Pending energy goes towards the partial accumulated record we currently have.
  163. stState.fPendingEnergy = stState.fAccumEnergy + fEventEnergy * ((float)(stState.usRecordInterval - (stState.ulEventTime % stState.usRecordInterval))) / ((float)usDeltaPeriod);
  164. // accumulated energy goes towards the *next* event.
  165. stState.fAccumEnergy = fEventEnergy * ((float)(ulNewEventTime % stState.usRecordInterval)) / ((float)usDeltaPeriod);
  166. // Gap energy fills the remainder.
  167. stState.fGapEnergy = fEventEnergy * ((unsigned short)stState.ucRecordGapCount * stState.usRecordInterval) / ((float)usDeltaPeriod);
  168. //Same for rotation.
  169. stState.fPendingRotation = stState.fAccumRotation + (float)ucDeltaTicks * ((float)(stState.usRecordInterval - (stState.ulEventTime % stState.usRecordInterval))) / ((float)usDeltaPeriod);
  170. stState.fAccumRotation = (float)ucDeltaTicks * ((float)(ulNewEventTime % stState.usRecordInterval)) / ((float)usDeltaPeriod);
  171. stState.fGapRotation = (float)((float)ucDeltaTicks * ((unsigned short)stState.ucRecordGapCount * stState.usRecordInterval) / ((float)usDeltaPeriod));
  172. }
  173. else
  174. {
  175. // This event came in before the next record epoch started - this
  176. // will happen when the event period is less than the recording period.
  177. stState.fAccumEnergy += fEventEnergy;
  178. stState.fAccumRotation += (float)ucDeltaTicks;
  179. stState.fPendingEnergy = 0;
  180. stState.fPendingRotation = 0;
  181. stState.ucRecordGapCount = 0;
  182. }
  183. stState.ulEventTime += (unsigned long)usDeltaPeriod;
  184. if (((unsigned short)(stState.ulEventTime - stState.ulLastRecordTime)) >= stState.usRecordInterval)
  185. {
  186. RecordOutput(prrPtr, &stState);
  187. }
  188. else
  189. {
  190. // We've had an event that either didn't have a rotation associated
  191. // with it (no event time increment) or else it was within the
  192. // recording interval.
  193. if ((dTime_ - stState.dLastRecordTime) > dRecordInterval)
  194. {
  195. while ((dTime_ - stState.dLastRecordTime) > dRecordInterval)
  196. {
  197. stState.dLastRecordTime += dRecordInterval;
  198. (prrPtr)(stState.dLastRecordTime, stState.dTotalRotation, stState.dTotalEnergy, 0.0, 0.0);
  199. }
  200. }
  201. }
  202. // Propagate the message state information.
  203. stState.ucLastEventCount = aucByte_[UPDATE_EVENT_BYTE];
  204. stState.ucLastRotationTicks = aucByte_[CRANK_TICKS_BYTE];
  205. stState.usLastAccumPeriod = usCurrentAccumPeriod;
  206. stState.usLastAccumTorque = usCurrentAccumTorque;
  207. }