You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

474 lines
16 KiB

  1. #include "plsr_profile.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. static int TestFailures;
  5. static int TestChecks;
  6. #define CHECK(condition) \
  7. do \
  8. { \
  9. TestChecks++; \
  10. if (!(condition)) \
  11. { \
  12. TestFailures++; \
  13. (void)printf("FAIL line %d: %s\n", __LINE__, #condition); \
  14. } \
  15. } while (0)
  16. /* 跑完一段曲线,返回实际脉冲数与峰值频率。 */
  17. static void RunProfile(const PLSR_PROFILE_REQUEST *request,
  18. int64_t pulses,
  19. uint32_t refreshHz,
  20. uint32_t retargetHz,
  21. uint32_t retargetAfterSteps,
  22. int64_t *actualPulses,
  23. uint32_t *peakHz,
  24. int64_t *steps)
  25. {
  26. PLSR_PROFILE_STATE state;
  27. uint32_t frequency = 0U;
  28. uint8_t completed = 0U;
  29. int64_t stepCount = 0;
  30. uint32_t peak = 0U;
  31. CHECK(PlsrProfileStart(&state, request, pulses, refreshHz)
  32. == PLSR_RESULT_OK);
  33. while ((completed == 0U) && (stepCount < 5000000))
  34. {
  35. (void)PlsrProfileStep(&state, &frequency, &completed);
  36. if (frequency > peak)
  37. {
  38. peak = frequency;
  39. }
  40. stepCount++;
  41. if ((retargetHz != 0U) && (stepCount == retargetAfterSteps))
  42. {
  43. CHECK(PlsrProfileRetarget(&state, retargetHz) == PLSR_RESULT_OK);
  44. }
  45. }
  46. CHECK(completed != 0U);
  47. *actualPulses = (int64_t)(state.emittedPulsesQ32 >> 32U);
  48. *peakHz = peak;
  49. *steps = stepCount;
  50. }
  51. static PLSR_PROFILE_REQUEST MakeRequest(uint32_t targetHz,
  52. uint32_t startHz,
  53. uint32_t stopHz,
  54. uint32_t accelSlope,
  55. uint32_t decelSlope,
  56. uint8_t curveMode)
  57. {
  58. PLSR_PROFILE_REQUEST request;
  59. (void)memset(&request, 0, sizeof(request));
  60. request.targetFrequencyHz = targetHz;
  61. request.startFrequencyHz = startHz;
  62. request.stopFrequencyHz = stopHz;
  63. request.maxFrequencyHz = 100000UL;
  64. request.accelSlopeHzPerMs = accelSlope;
  65. request.decelSlopeHzPerMs = decelSlope;
  66. request.curveMode = curveMode;
  67. return request;
  68. }
  69. static void TestTrapezoid(void)
  70. {
  71. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 0U);
  72. int64_t pulses;
  73. uint32_t peak;
  74. int64_t steps;
  75. RunProfile(&request, 20000, 1000U, 0U, 0, &pulses, &peak, &steps);
  76. CHECK(pulses == 20000);
  77. CHECK(peak == 10000U);
  78. CHECK(steps > 2000);
  79. }
  80. static void TestTriangle(void)
  81. {
  82. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 0U);
  83. int64_t pulses;
  84. uint32_t peak;
  85. int64_t steps;
  86. /* 脉冲太少:永远到不了目标频率,三角曲线。 */
  87. RunProfile(&request, 100, 1000U, 0U, 0, &pulses, &peak, &steps);
  88. CHECK(pulses == 100);
  89. CHECK(peak < 10000U);
  90. CHECK(peak > 0U);
  91. }
  92. static void TestStartStopSpeed(void)
  93. {
  94. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 1000U, 500U,
  95. 100U, 80U, 0U);
  96. int64_t pulses;
  97. uint32_t peak;
  98. int64_t steps;
  99. RunProfile(&request, 50000, 1000U, 0U, 0, &pulses, &peak, &steps);
  100. CHECK(pulses == 50000);
  101. CHECK(peak == 10000U);
  102. }
  103. static void TestSCurve(void)
  104. {
  105. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 1U);
  106. int64_t pulses;
  107. uint32_t peak;
  108. int64_t steps;
  109. RunProfile(&request, 20000, 1000U, 0U, 0, &pulses, &peak, &steps);
  110. CHECK(pulses == 20000);
  111. CHECK(peak == 10000U);
  112. CHECK(steps > 2000);
  113. }
  114. static void TestSine(void)
  115. {
  116. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 2U);
  117. int64_t pulses;
  118. uint32_t peak;
  119. int64_t steps;
  120. RunProfile(&request, 20000, 1000U, 0U, 0, &pulses, &peak, &steps);
  121. CHECK(pulses == 20000);
  122. CHECK(peak == 10000U);
  123. }
  124. static void TestRetargetUp(void)
  125. {
  126. PLSR_PROFILE_REQUEST request = MakeRequest(5000U, 0U, 0U, 100U, 100U, 0U);
  127. int64_t pulses;
  128. uint32_t peak;
  129. int64_t steps;
  130. /* 运行中升频到 10000:平滑过渡且总脉冲精确。 */
  131. RunProfile(&request, 30000, 1000U, 10000U, 500, &pulses, &peak, &steps);
  132. CHECK(pulses == 30000);
  133. CHECK(peak == 10000U);
  134. }
  135. static void TestRetargetDown(void)
  136. {
  137. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 0U);
  138. int64_t pulses;
  139. uint32_t peak;
  140. int64_t steps;
  141. /* 运行中降频到 3000:先减速到新目标再匀速,总脉冲精确。 */
  142. RunProfile(&request, 30000, 1000U, 3000U, 500, &pulses, &peak, &steps);
  143. CHECK(pulses == 30000);
  144. CHECK(peak == 10000U);
  145. CHECK(steps > 600);
  146. }
  147. static void TestNoAccel(void)
  148. {
  149. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 0U, 0U, 0U);
  150. int64_t pulses;
  151. uint32_t peak;
  152. int64_t steps;
  153. RunProfile(&request, 5000, 1000U, 0U, 0, &pulses, &peak, &steps);
  154. CHECK(pulses == 5000);
  155. CHECK(peak == 10000U);
  156. CHECK(steps == 500);
  157. }
  158. static void TestZeroRampRetarget(void)
  159. {
  160. PLSR_PROFILE_REQUEST request = MakeRequest(100000U, 100000U, 0U,
  161. 0U, 0U, 0U);
  162. PLSR_PROFILE_STATE state;
  163. uint32_t frequency = 0U;
  164. uint8_t completed = 0U;
  165. CHECK(PlsrProfileStart(&state, &request, 1000000, 10000U)
  166. == PLSR_RESULT_OK);
  167. CHECK(state.phase == PLSR_PROFILE_PHASE_CRUISE);
  168. CHECK(PlsrProfileRetarget(&state, 50000U) == PLSR_RESULT_OK);
  169. CHECK(state.phase == PLSR_PROFILE_PHASE_CRUISE);
  170. CHECK((state.frequencyQ32 >> 32U) == 50000U);
  171. CHECK(PlsrProfileStep(&state, &frequency, &completed) == PLSR_RESULT_OK);
  172. CHECK(frequency == 50000U);
  173. CHECK(completed == 0U);
  174. CHECK(PlsrProfileRetarget(&state, 100000U) == PLSR_RESULT_OK);
  175. CHECK(state.phase == PLSR_PROFILE_PHASE_CRUISE);
  176. CHECK((state.frequencyQ32 >> 32U) == 100000U);
  177. CHECK(PlsrProfileStep(&state, &frequency, &completed) == PLSR_RESULT_OK);
  178. CHECK(frequency == 100000U);
  179. CHECK(completed == 0U);
  180. }
  181. static void TestSubMillisecondRefresh(void)
  182. {
  183. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 0U);
  184. int64_t pulses;
  185. uint32_t peak;
  186. int64_t steps;
  187. /* 0.1ms 刷新:精度更高,步数是 1ms 的约 10 倍。 */
  188. RunProfile(&request, 20000, 10000U, 0U, 0, &pulses, &peak, &steps);
  189. CHECK(pulses == 20000);
  190. CHECK(peak == 10000U);
  191. CHECK(steps > 20000);
  192. }
  193. static void TestSinglePulse(void)
  194. {
  195. PLSR_PROFILE_REQUEST request = MakeRequest(1000U, 0U, 0U, 100U, 100U, 0U);
  196. int64_t pulses;
  197. uint32_t peak;
  198. int64_t steps;
  199. RunProfile(&request, 1, 1000U, 0U, 0, &pulses, &peak, &steps);
  200. CHECK(pulses == 1);
  201. CHECK(peak <= 1000U);
  202. }
  203. static void TestHighFrequency(void)
  204. {
  205. PLSR_PROFILE_REQUEST request = MakeRequest(100000U, 0U, 0U,
  206. 10000U, 10000U, 0U);
  207. int64_t pulses;
  208. uint32_t peak;
  209. int64_t steps;
  210. RunProfile(&request, 1000000, 1000U, 0U, 0, &pulses, &peak, &steps);
  211. CHECK(pulses == 1000000);
  212. CHECK(peak == 100000U);
  213. }
  214. static void TestArgumentValidation(void)
  215. {
  216. PLSR_PROFILE_STATE state;
  217. PLSR_PROFILE_REQUEST request = MakeRequest(1000U, 0U, 0U, 100U, 100U, 0U);
  218. uint32_t frequency;
  219. uint8_t completed;
  220. CHECK(PlsrProfileStart(NULL, &request, 100, 1000U)
  221. == PLSR_RESULT_INVALID_ARGUMENT);
  222. CHECK(PlsrProfileStart(&state, NULL, 100, 1000U)
  223. == PLSR_RESULT_INVALID_ARGUMENT);
  224. CHECK(PlsrProfileStart(&state, &request, 0, 1000U)
  225. == PLSR_RESULT_INVALID_ARGUMENT);
  226. CHECK(PlsrProfileStart(&state, &request, 100, 0U)
  227. == PLSR_RESULT_INVALID_ARGUMENT);
  228. request.targetFrequencyHz = 0U;
  229. CHECK(PlsrProfileStart(&state, &request, 100, 1000U)
  230. == PLSR_RESULT_INVALID_ARGUMENT);
  231. request.targetFrequencyHz = 1000U;
  232. CHECK(PlsrProfileStep(NULL, &frequency, &completed)
  233. == PLSR_RESULT_INVALID_ARGUMENT);
  234. CHECK(PlsrProfileStep(&state, NULL, &completed)
  235. == PLSR_RESULT_INVALID_ARGUMENT);
  236. CHECK(PlsrProfileStep(&state, &frequency, NULL)
  237. == PLSR_RESULT_INVALID_ARGUMENT);
  238. CHECK(PlsrProfileRetarget(NULL, 2000U) == PLSR_RESULT_INVALID_ARGUMENT);
  239. CHECK(PlsrProfileRetarget(&state, 0U) == PLSR_RESULT_INVALID_ARGUMENT);
  240. }
  241. static void TestSyncPulses(void)
  242. {
  243. /* 校准虚拟计数到硬件计数:模拟硬件滞后 38 个脉冲(预装载滞后),
  244. * 校准后 DONE 判定与硬件同步,段尾不再冻结频率补发。 */
  245. PLSR_PROFILE_REQUEST request = MakeRequest(1000U, 0U, 0U, 100U, 100U, 0U);
  246. PLSR_PROFILE_STATE state;
  247. uint32_t frequency;
  248. uint8_t completed;
  249. int i;
  250. CHECK(PlsrProfileStart(&state, &request, 500, 1000U) == PLSR_RESULT_OK);
  251. /* 自然推进 100 步:虚拟计数已超前(未校准)。 */
  252. for (i = 0; i < 100; i++)
  253. {
  254. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  255. == PLSR_RESULT_OK);
  256. }
  257. CHECK(completed == 0U);
  258. /* 校准到硬件计数(硬件只发了 400 个)——虚拟被拉回。 */
  259. PlsrProfileSyncPulses(&state, 400U);
  260. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  261. == PLSR_RESULT_OK);
  262. CHECK(completed == 0U); /* 400+本步增量 < 500,不提前 DONE */
  263. /* 校准到 499(还差 1 个):下一步 DONE。 */
  264. PlsrProfileSyncPulses(&state, 499U);
  265. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  266. == PLSR_RESULT_OK);
  267. CHECK(completed != 0U);
  268. CHECK(frequency == 0U);
  269. }
  270. static void TestInitialOutputFrequency(void)
  271. {
  272. PLSR_PROFILE_REQUEST request = MakeRequest(1000U, 0U, 0U,
  273. 10U, 10U, 0U);
  274. PLSR_PROFILE_STATE state;
  275. uint32_t frequency;
  276. uint8_t completed;
  277. CHECK(PlsrProfileStart(&state, &request, 500, 1000U)
  278. == PLSR_RESULT_OK);
  279. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  280. == PLSR_RESULT_OK);
  281. CHECK(frequency == 10U);
  282. /* 10Hz 以10Hz/ms连续加速,首脉冲末约141Hz,
  283. * 首周期等效频率约75Hz(约13.3ms),不再等100ms。 */
  284. CHECK(PlsrProfileGetInitialOutputFrequency(&state) == 75U);
  285. request.targetFrequencyHz = 20U;
  286. request.maxFrequencyHz = 20U;
  287. CHECK(PlsrProfileStart(&state, &request, 50, 1000U)
  288. == PLSR_RESULT_OK);
  289. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  290. == PLSR_RESULT_OK);
  291. CHECK(PlsrProfileGetInitialOutputFrequency(&state) == 19U);
  292. state.curveMode = PLSR_PROFILE_CURVE_S;
  293. CHECK(PlsrProfileGetInitialOutputFrequency(&state) == frequency);
  294. CHECK(PlsrProfileGetInitialOutputFrequency(NULL) == 0U);
  295. }
  296. static void TestBrakingOutputFrequency(void)
  297. {
  298. PLSR_PROFILE_REQUEST request = MakeRequest(2000U, 0U, 0U,
  299. 10U, 10U, 0U);
  300. PLSR_PROFILE_STATE state;
  301. CHECK(PlsrProfileStart(&state, &request, 1000, 1000U)
  302. == PLSR_RESULT_OK);
  303. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 201U) == 2000U);
  304. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, UINT64_MAX)
  305. == 2000U);
  306. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 5U) == 299U);
  307. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 4U) == 263U);
  308. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 3U) == 222U);
  309. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 2U) == 170U);
  310. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 1U) == 70U);
  311. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 0U) == 0U);
  312. CHECK(PlsrProfileGetBrakingOutputFrequency(NULL, 1U) == 0U);
  313. state.curveMode = PLSR_PROFILE_CURVE_S;
  314. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, 1U) == 2000U);
  315. /* 极端参数也不得让制动距离乘法溢出。 */
  316. state.curveMode = PLSR_PROFILE_CURVE_LINEAR;
  317. state.targetFrequencyHz = UINT32_MAX;
  318. state.stopFrequencyHz = 0U;
  319. state.decelSlopeHzPerMs = UINT32_MAX;
  320. CHECK(PlsrProfileGetBrakingOutputFrequency(&state, UINT64_MAX)
  321. == UINT32_MAX);
  322. }
  323. static void TestControlledStop(void)
  324. {
  325. PLSR_PROFILE_REQUEST request = MakeRequest(1000U, 1000U, 0U,
  326. 10U, 10U, 0U);
  327. PLSR_PROFILE_STATE state;
  328. uint32_t frequency = 0U;
  329. uint64_t pausedPulsesQ32;
  330. uint8_t completed = 0U;
  331. int step;
  332. CHECK(PlsrProfileStart(&state, &request, 10000, 1000U)
  333. == PLSR_RESULT_OK);
  334. CHECK(state.phase == PLSR_PROFILE_PHASE_CRUISE);
  335. CHECK(PlsrProfileRequestStop(&state) == PLSR_RESULT_OK);
  336. CHECK(state.phase == PLSR_PROFILE_PHASE_DECEL);
  337. for (step = 0; (step < 110) && (completed == 0U); step++)
  338. {
  339. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  340. == PLSR_RESULT_OK);
  341. }
  342. CHECK(completed != 0U);
  343. CHECK(frequency == 0U);
  344. CHECK(state.phase == PLSR_PROFILE_PHASE_DONE);
  345. pausedPulsesQ32 = state.emittedPulsesQ32;
  346. CHECK(PlsrProfileResume(&state, 500U, 2000U, 0U)
  347. == PLSR_RESULT_OK);
  348. CHECK(state.phase == PLSR_PROFILE_PHASE_ACCEL);
  349. CHECK(state.emittedPulsesQ32 == pausedPulsesQ32);
  350. CHECK(PlsrProfileStep(&state, &frequency, &completed)
  351. == PLSR_RESULT_OK);
  352. CHECK(completed == 0U);
  353. CHECK(frequency > 500U);
  354. request.decelSlopeHzPerMs = 0U;
  355. CHECK(PlsrProfileStart(&state, &request, 10000, 1000U)
  356. == PLSR_RESULT_OK);
  357. CHECK(PlsrProfileRequestStop(&state) == PLSR_RESULT_OK);
  358. CHECK(state.phase == PLSR_PROFILE_PHASE_DONE);
  359. CHECK(PlsrProfileRequestStop(NULL) == PLSR_RESULT_INVALID_ARGUMENT);
  360. CHECK(PlsrProfileResume(NULL, 500U, 2000U, 0U)
  361. == PLSR_RESULT_INVALID_ARGUMENT);
  362. state.emittedPulsesQ32 = (uint64_t)state.totalPulses
  363. * PLSR_PROFILE_Q32_ONE;
  364. CHECK(PlsrProfileResume(&state, 500U, 2000U, 0U)
  365. == PLSR_RESULT_INVALID_STATE);
  366. }
  367. static void TestPlan(void)
  368. {
  369. PLSR_PROFILE_REQUEST request = MakeRequest(10000U, 0U, 0U, 100U, 100U, 0U);
  370. PLSR_PROFILE_PLAN plan;
  371. PLSR_PROFILE_REQUEST triangleRequest =
  372. MakeRequest(10000U, 0U, 0U, 100U, 100U, 0U);
  373. CHECK(PlsrProfilePlan(&request, 20000, &plan) == PLSR_RESULT_OK);
  374. CHECK(plan.accelTimeMs == 100U);
  375. CHECK(plan.decelTimeMs == 100U);
  376. CHECK(plan.triangular == 0U);
  377. CHECK(plan.peakFrequencyHz == 10000U);
  378. CHECK(plan.cruiseTimeMs > 0U);
  379. CHECK(PlsrProfilePlan(&triangleRequest, 100, &plan) == PLSR_RESULT_OK);
  380. CHECK(plan.triangular != 0U);
  381. CHECK(plan.peakFrequencyHz < 10000U);
  382. CHECK(plan.accelTimeMs > 0U);
  383. CHECK(PlsrProfilePlan(NULL, 100, &plan) == PLSR_RESULT_INVALID_ARGUMENT);
  384. CHECK(PlsrProfilePlan(&request, 0, &plan) == PLSR_RESULT_INVALID_ARGUMENT);
  385. CHECK(PlsrProfilePlan(&request, 100, NULL) == PLSR_RESULT_INVALID_ARGUMENT);
  386. }
  387. int main(void)
  388. {
  389. TestTrapezoid();
  390. TestTriangle();
  391. TestStartStopSpeed();
  392. TestSCurve();
  393. TestSine();
  394. TestRetargetUp();
  395. TestRetargetDown();
  396. TestNoAccel();
  397. TestZeroRampRetarget();
  398. TestSubMillisecondRefresh();
  399. TestSinglePulse();
  400. TestHighFrequency();
  401. TestArgumentValidation();
  402. TestSyncPulses();
  403. TestInitialOutputFrequency();
  404. TestBrakingOutputFrequency();
  405. TestControlledStop();
  406. TestPlan();
  407. if (TestFailures != 0)
  408. {
  409. (void)printf("FAIL: %d of %d PLSR profile checks failed\n",
  410. TestFailures,
  411. TestChecks);
  412. return 1;
  413. }
  414. (void)printf("PASS: %d PLSR profile checks\n", TestChecks);
  415. return 0;
  416. }