kinemotion 0.27.0__tar.gz → 0.28.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. {kinemotion-0.27.0 → kinemotion-0.28.0}/CHANGELOG.md +8 -0
  2. {kinemotion-0.27.0 → kinemotion-0.28.0}/PKG-INFO +1 -1
  3. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/reference/json-output-format.md +11 -11
  4. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/reference/json-structure-comparison.md +3 -3
  5. {kinemotion-0.27.0 → kinemotion-0.28.0}/pyproject.toml +1 -1
  6. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cmj/kinematics.py +33 -23
  7. kinemotion-0.28.0/src/kinemotion/core/formatting.py +75 -0
  8. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/dropjump/kinematics.py +15 -46
  9. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_cli_cmj.py +1 -1
  10. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_cmj_kinematics.py +6 -6
  11. kinemotion-0.28.0/tests/test_formatting.py +179 -0
  12. {kinemotion-0.27.0 → kinemotion-0.28.0}/uv.lock +1 -1
  13. {kinemotion-0.27.0 → kinemotion-0.28.0}/.dockerignore +0 -0
  14. {kinemotion-0.27.0 → kinemotion-0.28.0}/.gitattributes +0 -0
  15. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  16. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  17. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  18. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/pull_request_template.md +0 -0
  19. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/workflows/docs.yml +0 -0
  20. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/workflows/release.yml +0 -0
  21. {kinemotion-0.27.0 → kinemotion-0.28.0}/.github/workflows/test.yml +0 -0
  22. {kinemotion-0.27.0 → kinemotion-0.28.0}/.gitignore +0 -0
  23. {kinemotion-0.27.0 → kinemotion-0.28.0}/.pre-commit-config.yaml +0 -0
  24. {kinemotion-0.27.0 → kinemotion-0.28.0}/.readthedocs.yml +0 -0
  25. {kinemotion-0.27.0 → kinemotion-0.28.0}/.tool-versions +0 -0
  26. {kinemotion-0.27.0 → kinemotion-0.28.0}/CLAUDE.md +0 -0
  27. {kinemotion-0.27.0 → kinemotion-0.28.0}/CODE_OF_CONDUCT.md +0 -0
  28. {kinemotion-0.27.0 → kinemotion-0.28.0}/CONTRIBUTING.md +0 -0
  29. {kinemotion-0.27.0 → kinemotion-0.28.0}/Dockerfile +0 -0
  30. {kinemotion-0.27.0 → kinemotion-0.28.0}/GEMINI.md +0 -0
  31. {kinemotion-0.27.0 → kinemotion-0.28.0}/LICENSE +0 -0
  32. {kinemotion-0.27.0 → kinemotion-0.28.0}/README.md +0 -0
  33. {kinemotion-0.27.0 → kinemotion-0.28.0}/SECURITY.md +0 -0
  34. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/README.md +0 -0
  35. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/api/cmj.md +0 -0
  36. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/api/core.md +0 -0
  37. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/api/dropjump.md +0 -0
  38. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/api/overview.md +0 -0
  39. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/development/errors-findings.md +0 -0
  40. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/development/testing.md +0 -0
  41. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/development/type-hints.md +0 -0
  42. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/development/validation-plan.md +0 -0
  43. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/development/validation-roadmap.md +0 -0
  44. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/development/wallball-norep-detection.md +0 -0
  45. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/guides/bulk-processing.md +0 -0
  46. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/guides/camera-setup.md +0 -0
  47. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/guides/cmj-guide.md +0 -0
  48. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/index.md +0 -0
  49. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/reference/parameters.md +0 -0
  50. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/reference/pose-systems.md +0 -0
  51. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/paper-downloader/HOW-TO-FIND-DOIS.md +0 -0
  52. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/paper-downloader/README.md +0 -0
  53. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/paper-downloader/dois.txt +0 -0
  54. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/paper-downloader/download.py +0 -0
  55. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/paper-downloader/pyproject.toml +0 -0
  56. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/paper-downloader/uv.lock +0 -0
  57. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/sports-biomechanics-pose-estimation.md +0 -0
  58. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/MANUAL-DOWNLOAD-GUIDE.md +0 -0
  59. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/README.md +0 -0
  60. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/convert_pdfs.py +0 -0
  61. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/athlete-monitoring/2001_Foster_Session-RPE-Training-Monitoring.md +0 -0
  62. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/athlete-monitoring/2015_Buchheit_GPS-Accelerometers-Stride-Stiffness.md +0 -0
  63. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/athlete-monitoring/2018_Flatt_HRV-Recovery-Swimmers.md +0 -0
  64. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/athlete-monitoring/2018_Saw_Training-Camps-Monitoring.md +0 -0
  65. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/athlete-monitoring/2018_Ward_Putting-i-in-Team.md +0 -0
  66. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2011_Petersen_Nordic-Hamstring-Prevention.md +0 -0
  67. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2011_Wilk_Shoulder-GIRD-Baseball-Pitchers.md +0 -0
  68. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2012_Hewit_Multidirectional-Leg-Asymmetry.md +0 -0
  69. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2015_Ford_Hip-Neuromuscular-Exercise-Valgus.md +0 -0
  70. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2015_Mosler_Hip-Groin-Pain-Factors.md +0 -0
  71. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2015_Sconce_Nordic-Hamstring-Validity.md +0 -0
  72. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2016_Mendez-Villanueva_Hamstring-MRI-Regional.md +0 -0
  73. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2016_Read_Youth-Soccer-Injury-Risk.md +0 -0
  74. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2017_Mason-Mackay_Ankle-Dorsiflexion-Landing.md +0 -0
  75. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2017_Mendiguchia_Hamstring-Treatment-Algorithm.md +0 -0
  76. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2018_Balsalobre-Fernandez_Ankle-Dorsiflexion-App.md +0 -0
  77. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2018_Bramah_Pathological-Gait-Running-Injuries.md +0 -0
  78. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/injury-prevention/2020_Fidai_Fatigue-Knee-Valgus-Youth.md +0 -0
  79. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2011_Harper_10-to-5-Jump-Test.md +0 -0
  80. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2012_Samozino_Optimal-Force-Velocity-Profile.md +0 -0
  81. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2014_Samozino_FV-Imbalance.md +0 -0
  82. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2016_Jimenez-Reyes_Force-Velocity-Training.md +0 -0
  83. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2016_Morin-Samozino_Power-Force-Velocity-Profiles.md +0 -0
  84. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2018_Garcia-Ramos_Two-Point-Method-Optimization.md +0 -0
  85. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/jump-performance/2022_Wells_Golf-Clubhead-CMJ.md +0 -0
  86. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2005_Morin_Running-Stiffness-Measurement.md +0 -0
  87. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2015_Balsalobre-Fernandez_Strength-Training-Running-Economy.md +0 -0
  88. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2016_Balsalobre-Fernandez_iPhone-Running-Mechanics.md +0 -0
  89. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2016_Moore_Economical-Running-Technique.md +0 -0
  90. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2020_Filter_Curve-Sprint-Test-Soccer.md +0 -0
  91. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2020_Harper_Horizontal-Deceleration-Radar.md +0 -0
  92. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2021_vanOeveren_Running-Biomechanics-Synthesis.md +0 -0
  93. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/running-biomechanics/2024_Bramah_Sprint-Mechanics-Assessment-Score.md +0 -0
  94. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/smartphone-technology/2015_Balsalobre-Fernandez_iPhone-Vertical-Jump.md +0 -0
  95. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/smartphone-technology/2022_Bishop_MyJumpLab-Validation.md +0 -0
  96. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/velocity-based-training/2011_Jidovtseff_Load-Velocity-1RM-Prediction.md +0 -0
  97. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/velocity-based-training/2016_Conceicao_Movement-Velocity-Lower-Limb.md +0 -0
  98. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/velocity-based-training/2016_Pareja-Blanco_Velocity-Loss-Training.md +0 -0
  99. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/velocity-based-training/2017_Balsalobre-Fernandez_Barbell-Velocity-1RM.md +0 -0
  100. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/velocity-based-training/2020_Balsalobre-Fernandez_Barbell-Trajectory-Snatch.md +0 -0
  101. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/markdown/velocity-based-training/2023_Balsalobre-Fernandez_AI-Barbell-Velocity.md +0 -0
  102. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/online-references-for-papers.md +0 -0
  103. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/athlete-monitoring/2001_Foster_Session-RPE-Training-Monitoring.pdf +0 -0
  104. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/athlete-monitoring/2015_Buchheit_GPS-Accelerometers-Stride-Stiffness.pdf +0 -0
  105. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/athlete-monitoring/2018_Flatt_HRV-Recovery-Swimmers.pdf +0 -0
  106. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/athlete-monitoring/2018_Saw_Training-Camps-Monitoring.pdf +0 -0
  107. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/athlete-monitoring/2018_Ward_Putting-i-in-Team.pdf +0 -0
  108. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2011_Petersen_Nordic-Hamstring-Prevention.pdf +0 -0
  109. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2011_Wilk_Shoulder-GIRD-Baseball-Pitchers.pdf +0 -0
  110. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2012_Hewit_Multidirectional-Leg-Asymmetry.pdf +0 -0
  111. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2015_Ford_Hip-Neuromuscular-Exercise-Valgus.pdf +0 -0
  112. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2015_Mosler_Hip-Groin-Pain-Factors.pdf +0 -0
  113. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2015_Sconce_Nordic-Hamstring-Validity.pdf +0 -0
  114. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2016_Mendez-Villanueva_Hamstring-MRI-Regional.pdf +0 -0
  115. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2016_Read_Youth-Soccer-Injury-Risk.pdf +0 -0
  116. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2017_Mason-Mackay_Ankle-Dorsiflexion-Landing.pdf +0 -0
  117. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2017_Mendiguchia_Hamstring-Treatment-Algorithm.pdf +0 -0
  118. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2018_Balsalobre-Fernandez_Ankle-Dorsiflexion-App.pdf +0 -0
  119. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2018_Bramah_Pathological-Gait-Running-Injuries.pdf +0 -0
  120. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/injury-prevention/2020_Fidai_Fatigue-Knee-Valgus-Youth.pdf +0 -0
  121. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2011_Harper_10-to-5-Jump-Test.pdf +0 -0
  122. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2012_Samozino_Optimal-Force-Velocity-Profile.pdf +0 -0
  123. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2014_Samozino_FV-Imbalance.pdf +0 -0
  124. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2016_Jimenez-Reyes_Force-Velocity-Training.pdf +0 -0
  125. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2016_Morin-Samozino_Power-Force-Velocity-Profiles.pdf +0 -0
  126. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2018_Garcia-Ramos_Two-Point-Method-Optimization.pdf +0 -0
  127. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/jump-performance/2022_Wells_Golf-Clubhead-CMJ.pdf +0 -0
  128. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2005_Morin_Running-Stiffness-Measurement.pdf +0 -0
  129. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2015_Balsalobre-Fernandez_Strength-Training-Running-Economy.pdf +0 -0
  130. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2016_Balsalobre-Fernandez_iPhone-Running-Mechanics.pdf +0 -0
  131. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2016_Moore_Economical-Running-Technique.pdf +0 -0
  132. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2020_Filter_Curve-Sprint-Test-Soccer.pdf +0 -0
  133. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2020_Harper_Horizontal-Deceleration-Radar.pdf +0 -0
  134. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2021_vanOeveren_Running-Biomechanics-Synthesis.pdf +0 -0
  135. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/running-biomechanics/2024_Bramah_Sprint-Mechanics-Assessment-Score.pdf +0 -0
  136. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/smartphone-technology/2015_Balsalobre-Fernandez_iPhone-Vertical-Jump.pdf +0 -0
  137. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/smartphone-technology/2022_Bishop_MyJumpLab-Validation.pdf +0 -0
  138. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/velocity-based-training/2011_Jidovtseff_Load-Velocity-1RM-Prediction.pdf +0 -0
  139. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/velocity-based-training/2016_Conceicao_Movement-Velocity-Lower-Limb.pdf +0 -0
  140. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/velocity-based-training/2016_Pareja-Blanco_Velocity-Loss-Training.pdf +0 -0
  141. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/velocity-based-training/2017_Balsalobre-Fernandez_Barbell-Velocity-1RM.pdf +0 -0
  142. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/velocity-based-training/2020_Balsalobre-Fernandez_Barbell-Trajectory-Snatch.pdf +0 -0
  143. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/research/thirdparty/pdfs/velocity-based-training/2023_Balsalobre-Fernandez_AI-Barbell-Velocity.pdf +0 -0
  144. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/technical/framerate.md +0 -0
  145. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/technical/implementation-details.md +0 -0
  146. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/technical/imu-metadata.md +0 -0
  147. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/technical/real-time-analysis.md +0 -0
  148. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/technical/triple-extension.md +0 -0
  149. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/translations/es/camera-setup.md +0 -0
  150. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/validation/determinism-test.md +0 -0
  151. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/validation/known-height-validation.md +0 -0
  152. {kinemotion-0.27.0 → kinemotion-0.28.0}/docs/validation-status.md +0 -0
  153. {kinemotion-0.27.0 → kinemotion-0.28.0}/examples/bulk/README.md +0 -0
  154. {kinemotion-0.27.0 → kinemotion-0.28.0}/examples/bulk/bulk_processing.py +0 -0
  155. {kinemotion-0.27.0 → kinemotion-0.28.0}/examples/bulk/simple_example.py +0 -0
  156. {kinemotion-0.27.0 → kinemotion-0.28.0}/examples/programmatic_usage.py +0 -0
  157. {kinemotion-0.27.0 → kinemotion-0.28.0}/mkdocs.yml +0 -0
  158. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/.gitignore +0 -0
  159. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/README.md +0 -0
  160. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/demos/.gitignore +0 -0
  161. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/demos/README.md +0 -0
  162. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/demos/api_demo.ipynb +0 -0
  163. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/demos/batch_processing_demo.py +0 -0
  164. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/demos/sample_data/.gitkeep +0 -0
  165. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/presentation_guide.md +0 -0
  166. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/revealjs/.gitignore +0 -0
  167. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/revealjs/Makefile +0 -0
  168. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/revealjs/README.md +0 -0
  169. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/revealjs/slides.md +0 -0
  170. {kinemotion-0.27.0 → kinemotion-0.28.0}/presentation/speaker_script.md +0 -0
  171. {kinemotion-0.27.0 → kinemotion-0.28.0}/requirements-docs.txt +0 -0
  172. {kinemotion-0.27.0 → kinemotion-0.28.0}/samples/cmjs/README.md +0 -0
  173. {kinemotion-0.27.0 → kinemotion-0.28.0}/scripts/README.md +0 -0
  174. {kinemotion-0.27.0 → kinemotion-0.28.0}/scripts/analyze_determinism_variance.py +0 -0
  175. {kinemotion-0.27.0 → kinemotion-0.28.0}/scripts/generate_test_data.py +0 -0
  176. {kinemotion-0.27.0 → kinemotion-0.28.0}/scripts/plot_validation_results.py +0 -0
  177. {kinemotion-0.27.0 → kinemotion-0.28.0}/scripts/test_determinism.sh +0 -0
  178. {kinemotion-0.27.0 → kinemotion-0.28.0}/scripts/validate_known_heights.py +0 -0
  179. {kinemotion-0.27.0 → kinemotion-0.28.0}/sonar-project.properties +0 -0
  180. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/__init__.py +0 -0
  181. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/api.py +0 -0
  182. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cli.py +0 -0
  183. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cmj/__init__.py +0 -0
  184. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cmj/analysis.py +0 -0
  185. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cmj/cli.py +0 -0
  186. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cmj/debug_overlay.py +0 -0
  187. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/cmj/joint_angles.py +0 -0
  188. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/__init__.py +0 -0
  189. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/auto_tuning.py +0 -0
  190. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/cli_utils.py +0 -0
  191. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/debug_overlay_utils.py +0 -0
  192. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/filtering.py +0 -0
  193. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/metadata.py +0 -0
  194. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/pose.py +0 -0
  195. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/quality.py +0 -0
  196. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/smoothing.py +0 -0
  197. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/core/video_io.py +0 -0
  198. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/dropjump/__init__.py +0 -0
  199. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/dropjump/analysis.py +0 -0
  200. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/dropjump/cli.py +0 -0
  201. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/dropjump/debug_overlay.py +0 -0
  202. {kinemotion-0.27.0 → kinemotion-0.28.0}/src/kinemotion/py.typed +0 -0
  203. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/__init__.py +0 -0
  204. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/core/test_quality.py +0 -0
  205. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_adaptive_threshold.py +0 -0
  206. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_api.py +0 -0
  207. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_aspect_ratio.py +0 -0
  208. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_cli_dropjump.py +0 -0
  209. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_cli_imports.py +0 -0
  210. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_cmj_analysis.py +0 -0
  211. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_com_estimation.py +0 -0
  212. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_contact_detection.py +0 -0
  213. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_filtering.py +0 -0
  214. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_joint_angles.py +0 -0
  215. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_kinematics.py +0 -0
  216. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_polyorder.py +0 -0
  217. {kinemotion-0.27.0 → kinemotion-0.28.0}/tests/test_video_io.py +0 -0
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  <!-- version list -->
9
9
 
10
+ ## v0.28.0 (2025-11-15)
11
+
12
+ ### Features
13
+
14
+ - Standardize numeric precision across jump types
15
+ ([`def84b0`](https://github.com/feniix/kinemotion/commit/def84b00ba4aef35f398ac615e328ba15e50133d))
16
+
17
+
10
18
  ## v0.27.0 (2025-11-14)
11
19
 
12
20
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kinemotion
3
- Version: 0.27.0
3
+ Version: 0.28.0
4
4
  Summary: Video-based kinematic analysis for athletic performance
5
5
  Project-URL: Homepage, https://github.com/feniix/kinemotion
6
6
  Project-URL: Repository, https://github.com/feniix/kinemotion
@@ -37,14 +37,14 @@ ______________________________________________________________________
37
37
  {
38
38
  "data": {
39
39
  "jump_height_m": 0.506,
40
- "flight_time_s": 0.642,
40
+ "flight_time_ms": 642.0,
41
41
  "countermovement_depth_m": 0.045,
42
- "eccentric_duration_s": 0.412,
43
- "concentric_duration_s": 0.305,
44
- "total_movement_time_s": 0.717,
42
+ "eccentric_duration_ms": 412.0,
43
+ "concentric_duration_ms": 305.0,
44
+ "total_movement_time_ms": 717.0,
45
45
  "peak_eccentric_velocity_m_s": -1.234,
46
46
  "peak_concentric_velocity_m_s": 2.634,
47
- "transition_time_s": 0.135,
47
+ "transition_time_ms": 135.0,
48
48
  "standing_start_frame": 145.0,
49
49
  "lowest_point_frame": 146.0,
50
50
  "takeoff_frame": 154.3,
@@ -200,14 +200,14 @@ ______________________________________________________________________
200
200
  **CMJ-specific fields:**
201
201
 
202
202
  - `jump_height_m` (float): Maximum vertical displacement
203
- - `flight_time_s` (float): Time in the air
203
+ - `flight_time_ms` (float): Time in the air in milliseconds
204
204
  - `countermovement_depth_m` (float): Depth of eccentric phase
205
- - `eccentric_duration_s` (float): Time from start to lowest point
206
- - `concentric_duration_s` (float): Time from lowest point to takeoff
207
- - `total_movement_time_s` (float): Total time from start to takeoff
205
+ - `eccentric_duration_ms` (float): Time from start to lowest point in milliseconds
206
+ - `concentric_duration_ms` (float): Time from lowest point to takeoff in milliseconds
207
+ - `total_movement_time_ms` (float): Total time from start to takeoff in milliseconds
208
208
  - `peak_eccentric_velocity_m_s` (float): Max downward velocity (negative)
209
209
  - `peak_concentric_velocity_m_s` (float): Max upward velocity (negative in coords)
210
- - `transition_time_s` (float | null): Amortization phase duration
210
+ - `transition_time_ms` (float | null): Amortization phase duration in milliseconds
211
211
  - `standing_start_frame` (float | null): Frame where movement begins
212
212
  - `lowest_point_frame` (float): Frame at deepest countermovement
213
213
  - `takeoff_frame` (float): Frame where feet leave ground
@@ -414,7 +414,7 @@ ______________________________________________________________________
414
414
  {
415
415
  "data": {
416
416
  "jump_height_m": 0.352,
417
- "flight_time_s": 0.534
417
+ "flight_time_ms": 534.0
418
418
  },
419
419
  "metadata": {
420
420
  "quality": {
@@ -11,7 +11,7 @@ ______________________________________________________________________
11
11
  ```json
12
12
  {
13
13
  "jump_height_m": 0.352,
14
- "flight_time_s": 0.534,
14
+ "flight_time_ms": 534.0,
15
15
  "takeoff_frame": 154.3,
16
16
  "landing_frame": 172.8,
17
17
  "confidence": "high",
@@ -121,7 +121,7 @@ ______________________________________________________________________
121
121
  ```json
122
122
  {
123
123
  "jump_height_m": 0.352,
124
- "flight_time_s": 0.534,
124
+ "flight_time_ms": 534.0,
125
125
  "takeoff_frame": 154.3,
126
126
  "landing_frame": 172.8,
127
127
  "quality": {
@@ -185,7 +185,7 @@ ______________________________________________________________________
185
185
  class JumpData:
186
186
  """Physical measurements."""
187
187
  jump_height_m: float
188
- flight_time_s: float
188
+ flight_time_ms: float
189
189
  ...
190
190
 
191
191
  @dataclass
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kinemotion"
3
- version = "0.27.0"
3
+ version = "0.28.0"
4
4
  description = "Video-based kinematic analysis for athletic performance"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10,<3.13"
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, TypedDict
6
6
  import numpy as np
7
7
  from numpy.typing import NDArray
8
8
 
9
+ from ..core.formatting import format_float_metric
10
+
9
11
  if TYPE_CHECKING:
10
12
  from ..core.metadata import ResultMetadata
11
13
  from ..core.quality import QualityAssessment
@@ -15,14 +17,14 @@ class CMJDataDict(TypedDict, total=False):
15
17
  """Type-safe dictionary for CMJ measurement data."""
16
18
 
17
19
  jump_height_m: float
18
- flight_time_s: float
20
+ flight_time_ms: float
19
21
  countermovement_depth_m: float
20
- eccentric_duration_s: float
21
- concentric_duration_s: float
22
- total_movement_time_s: float
22
+ eccentric_duration_ms: float
23
+ concentric_duration_ms: float
24
+ total_movement_time_ms: float
23
25
  peak_eccentric_velocity_m_s: float
24
26
  peak_concentric_velocity_m_s: float
25
- transition_time_s: float | None
27
+ transition_time_ms: float | None
26
28
  standing_start_frame: float | None
27
29
  lowest_point_frame: float
28
30
  takeoff_frame: float
@@ -43,14 +45,14 @@ class CMJMetrics:
43
45
 
44
46
  Attributes:
45
47
  jump_height: Maximum jump height in meters
46
- flight_time: Time spent in the air in seconds
48
+ flight_time: Time spent in the air in milliseconds
47
49
  countermovement_depth: Vertical distance traveled during eccentric phase in meters
48
- eccentric_duration: Time from countermovement start to lowest point in seconds
49
- concentric_duration: Time from lowest point to takeoff in seconds
50
- total_movement_time: Total time from countermovement start to takeoff in seconds
50
+ eccentric_duration: Time from countermovement start to lowest point in milliseconds
51
+ concentric_duration: Time from lowest point to takeoff in milliseconds
52
+ total_movement_time: Total time from countermovement start to takeoff in milliseconds
51
53
  peak_eccentric_velocity: Maximum downward velocity during countermovement in m/s
52
54
  peak_concentric_velocity: Maximum upward velocity during propulsion in m/s
53
- transition_time: Duration at lowest point (amortization phase) in seconds
55
+ transition_time: Duration at lowest point (amortization phase) in milliseconds
54
56
  standing_start_frame: Frame where standing phase ends (countermovement begins)
55
57
  lowest_point_frame: Frame at lowest point of countermovement
56
58
  takeoff_frame: Frame where athlete leaves ground
@@ -85,19 +87,27 @@ class CMJMetrics:
85
87
  Dictionary with nested data and metadata structure.
86
88
  """
87
89
  data: CMJDataDict = {
88
- "jump_height_m": float(self.jump_height),
89
- "flight_time_s": float(self.flight_time),
90
- "countermovement_depth_m": float(self.countermovement_depth),
91
- "eccentric_duration_s": float(self.eccentric_duration),
92
- "concentric_duration_s": float(self.concentric_duration),
93
- "total_movement_time_s": float(self.total_movement_time),
94
- "peak_eccentric_velocity_m_s": float(self.peak_eccentric_velocity),
95
- "peak_concentric_velocity_m_s": float(self.peak_concentric_velocity),
96
- "transition_time_s": (
97
- float(self.transition_time)
98
- if self.transition_time is not None
99
- else None
100
- ),
90
+ "jump_height_m": format_float_metric(self.jump_height, 1, 3), # type: ignore[typeddict-item]
91
+ "flight_time_ms": format_float_metric(self.flight_time, 1000, 2), # type: ignore[typeddict-item]
92
+ "countermovement_depth_m": format_float_metric(
93
+ self.countermovement_depth, 1, 3
94
+ ), # type: ignore[typeddict-item]
95
+ "eccentric_duration_ms": format_float_metric(
96
+ self.eccentric_duration, 1000, 2
97
+ ), # type: ignore[typeddict-item]
98
+ "concentric_duration_ms": format_float_metric(
99
+ self.concentric_duration, 1000, 2
100
+ ), # type: ignore[typeddict-item]
101
+ "total_movement_time_ms": format_float_metric(
102
+ self.total_movement_time, 1000, 2
103
+ ), # type: ignore[typeddict-item]
104
+ "peak_eccentric_velocity_m_s": format_float_metric(
105
+ self.peak_eccentric_velocity, 1, 4
106
+ ), # type: ignore[typeddict-item]
107
+ "peak_concentric_velocity_m_s": format_float_metric(
108
+ self.peak_concentric_velocity, 1, 4
109
+ ), # type: ignore[typeddict-item]
110
+ "transition_time_ms": format_float_metric(self.transition_time, 1000, 2),
101
111
  "standing_start_frame": (
102
112
  float(self.standing_start_frame)
103
113
  if self.standing_start_frame is not None
@@ -0,0 +1,75 @@
1
+ """Formatting utilities for consistent numeric output across jump analysis types.
2
+
3
+ This module provides shared helpers for formatting numeric values with appropriate
4
+ precision based on measurement type and capabilities of video-based analysis.
5
+ """
6
+
7
+ # Standard precision values for different measurement types
8
+ # These values are chosen based on:
9
+ # - Video analysis capabilities (30-240 fps)
10
+ # - Typical measurement uncertainty in video-based biomechanics
11
+ # - Balance between accuracy and readability
12
+
13
+ PRECISION_TIME_MS = 2 # Time in milliseconds: ±0.01ms (e.g., 534.12)
14
+ PRECISION_DISTANCE_M = 3 # Distance in meters: ±1mm (e.g., 0.352)
15
+ PRECISION_VELOCITY_M_S = 4 # Velocity in m/s: ±0.0001 m/s (e.g., 2.6340)
16
+ PRECISION_FRAME = 3 # Sub-frame interpolation precision (e.g., 154.342)
17
+ PRECISION_NORMALIZED = 4 # Normalized values 0-1 ratios (e.g., 0.0582)
18
+
19
+
20
+ def format_float_metric(
21
+ value: float | None,
22
+ multiplier: float = 1.0,
23
+ decimals: int = 2,
24
+ ) -> float | None:
25
+ """Format a float metric value with optional scaling and rounding.
26
+
27
+ This helper ensures consistent precision across all jump analysis outputs,
28
+ preventing false precision in measurements while maintaining appropriate
29
+ accuracy for the measurement type.
30
+
31
+ Args:
32
+ value: The value to format, or None
33
+ multiplier: Factor to multiply value by (e.g., 1000 for seconds→milliseconds)
34
+ decimals: Number of decimal places to round to
35
+
36
+ Returns:
37
+ Formatted value rounded to specified decimals, or None if input is None
38
+
39
+ Examples:
40
+ >>> format_float_metric(0.534123, 1000, 2) # seconds to ms
41
+ 534.12
42
+ >>> format_float_metric(0.3521234, 1, 3) # meters
43
+ 0.352
44
+ >>> format_float_metric(None, 1, 2)
45
+ None
46
+ >>> format_float_metric(-1.23456, 1, 4) # negative values preserved
47
+ -1.2346
48
+ """
49
+ if value is None:
50
+ return None
51
+ return round(value * multiplier, decimals)
52
+
53
+
54
+ def format_int_metric(value: float | int | None) -> int | None:
55
+ """Format a value as an integer.
56
+
57
+ Used for frame numbers and other integer-valued metrics.
58
+
59
+ Args:
60
+ value: The value to format, or None
61
+
62
+ Returns:
63
+ Value converted to int, or None if input is None
64
+
65
+ Examples:
66
+ >>> format_int_metric(42.7)
67
+ 42
68
+ >>> format_int_metric(None)
69
+ None
70
+ >>> format_int_metric(154)
71
+ 154
72
+ """
73
+ if value is None:
74
+ return None
75
+ return int(value)
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, TypedDict
5
5
  import numpy as np
6
6
  from numpy.typing import NDArray
7
7
 
8
+ from ..core.formatting import format_float_metric, format_int_metric
8
9
  from ..core.smoothing import compute_acceleration_from_derivative
9
10
  from .analysis import (
10
11
  ContactState,
@@ -19,38 +20,6 @@ if TYPE_CHECKING:
19
20
  from ..core.quality import QualityAssessment
20
21
 
21
22
 
22
- def _format_float_metric(
23
- value: float | None, multiplier: float = 1, decimals: int = 2
24
- ) -> float | None:
25
- """Format a float metric value with optional scaling and rounding.
26
-
27
- Args:
28
- value: The value to format, or None
29
- multiplier: Factor to multiply value by (default: 1)
30
- decimals: Number of decimal places to round to (default: 2)
31
-
32
- Returns:
33
- Formatted value rounded to specified decimals, or None if input is None
34
- """
35
- if value is None:
36
- return None
37
- return round(value * multiplier, decimals)
38
-
39
-
40
- def _format_int_metric(value: float | int | None) -> int | None:
41
- """Format a value as an integer.
42
-
43
- Args:
44
- value: The value to format, or None
45
-
46
- Returns:
47
- Value converted to int, or None if input is None
48
- """
49
- if value is None:
50
- return None
51
- return int(value)
52
-
53
-
54
23
  class DropJumpDataDict(TypedDict, total=False):
55
24
  """Type-safe dictionary for drop jump measurement data."""
56
25
 
@@ -108,32 +77,32 @@ class DropJumpMetrics:
108
77
  Dictionary containing formatted metric values.
109
78
  """
110
79
  return {
111
- "ground_contact_time_ms": _format_float_metric(
80
+ "ground_contact_time_ms": format_float_metric(
112
81
  self.ground_contact_time, 1000, 2
113
82
  ),
114
- "flight_time_ms": _format_float_metric(self.flight_time, 1000, 2),
115
- "jump_height_m": _format_float_metric(self.jump_height, 1, 3),
116
- "jump_height_kinematic_m": _format_float_metric(
83
+ "flight_time_ms": format_float_metric(self.flight_time, 1000, 2),
84
+ "jump_height_m": format_float_metric(self.jump_height, 1, 3),
85
+ "jump_height_kinematic_m": format_float_metric(
117
86
  self.jump_height_kinematic, 1, 3
118
87
  ),
119
- "jump_height_trajectory_normalized": _format_float_metric(
88
+ "jump_height_trajectory_normalized": format_float_metric(
120
89
  self.jump_height_trajectory, 1, 4
121
90
  ),
122
- "contact_start_frame": _format_int_metric(self.contact_start_frame),
123
- "contact_end_frame": _format_int_metric(self.contact_end_frame),
124
- "flight_start_frame": _format_int_metric(self.flight_start_frame),
125
- "flight_end_frame": _format_int_metric(self.flight_end_frame),
126
- "peak_height_frame": _format_int_metric(self.peak_height_frame),
127
- "contact_start_frame_precise": _format_float_metric(
91
+ "contact_start_frame": format_int_metric(self.contact_start_frame),
92
+ "contact_end_frame": format_int_metric(self.contact_end_frame),
93
+ "flight_start_frame": format_int_metric(self.flight_start_frame),
94
+ "flight_end_frame": format_int_metric(self.flight_end_frame),
95
+ "peak_height_frame": format_int_metric(self.peak_height_frame),
96
+ "contact_start_frame_precise": format_float_metric(
128
97
  self.contact_start_frame_precise, 1, 3
129
98
  ),
130
- "contact_end_frame_precise": _format_float_metric(
99
+ "contact_end_frame_precise": format_float_metric(
131
100
  self.contact_end_frame_precise, 1, 3
132
101
  ),
133
- "flight_start_frame_precise": _format_float_metric(
102
+ "flight_start_frame_precise": format_float_metric(
134
103
  self.flight_start_frame_precise, 1, 3
135
104
  ),
136
- "flight_end_frame_precise": _format_float_metric(
105
+ "flight_end_frame_precise": format_float_metric(
137
106
  self.flight_end_frame_precise, 1, 3
138
107
  ),
139
108
  }
@@ -118,7 +118,7 @@ class TestCMJCLIFileOperations:
118
118
  # Test keys exist, not their values (which may be None)
119
119
  assert isinstance(data, dict)
120
120
  assert "jump_height_m" in data
121
- assert "flight_time_s" in data
121
+ assert "flight_time_ms" in data
122
122
 
123
123
  def test_debug_video_output_created(self, cli_runner, minimal_video, tmp_path):
124
124
  """Test debug video file is created when analysis succeeds."""
@@ -97,14 +97,14 @@ def test_cmj_metrics_to_dict() -> None:
97
97
  # Verify all expected keys are present in data
98
98
  expected_keys = [
99
99
  "jump_height_m",
100
- "flight_time_s",
100
+ "flight_time_ms",
101
101
  "countermovement_depth_m",
102
- "eccentric_duration_s",
103
- "concentric_duration_s",
104
- "total_movement_time_s",
102
+ "eccentric_duration_ms",
103
+ "concentric_duration_ms",
104
+ "total_movement_time_ms",
105
105
  "peak_eccentric_velocity_m_s",
106
106
  "peak_concentric_velocity_m_s",
107
- "transition_time_s",
107
+ "transition_time_ms",
108
108
  "standing_start_frame",
109
109
  "lowest_point_frame",
110
110
  "takeoff_frame",
@@ -117,7 +117,7 @@ def test_cmj_metrics_to_dict() -> None:
117
117
 
118
118
  # Verify all numeric values are Python types (not NumPy)
119
119
  assert isinstance(result_dict["data"]["jump_height_m"], float)
120
- assert isinstance(result_dict["data"]["flight_time_s"], float)
120
+ assert isinstance(result_dict["data"]["flight_time_ms"], float)
121
121
  assert isinstance(result_dict["data"]["tracking_method"], str)
122
122
 
123
123
 
@@ -0,0 +1,179 @@
1
+ """Tests for numeric formatting utilities."""
2
+
3
+ from kinemotion.core.formatting import (
4
+ PRECISION_DISTANCE_M,
5
+ PRECISION_FRAME,
6
+ PRECISION_NORMALIZED,
7
+ PRECISION_TIME_MS,
8
+ PRECISION_VELOCITY_M_S,
9
+ format_float_metric,
10
+ format_int_metric,
11
+ )
12
+
13
+
14
+ class TestFormatFloatMetric:
15
+ """Test float metric formatting."""
16
+
17
+ def test_basic_rounding(self) -> None:
18
+ """Test basic rounding without scaling."""
19
+ assert format_float_metric(1.23456, 1, 2) == 1.23
20
+ assert format_float_metric(1.23456, 1, 3) == 1.235
21
+ assert format_float_metric(1.23456, 1, 4) == 1.2346
22
+
23
+ def test_scaling_with_rounding(self) -> None:
24
+ """Test scaling (e.g., seconds to milliseconds) with rounding."""
25
+ # Seconds to milliseconds
26
+ assert format_float_metric(0.534123, 1000, 2) == 534.12
27
+ assert format_float_metric(0.001, 1000, 2) == 1.0
28
+ assert format_float_metric(1.999999, 1000, 2) == 2000.0
29
+
30
+ def test_none_handling(self) -> None:
31
+ """Test that None values pass through."""
32
+ assert format_float_metric(None, 1, 2) is None
33
+ assert format_float_metric(None, 1000, 3) is None
34
+
35
+ def test_negative_values(self) -> None:
36
+ """Test that negative values are preserved."""
37
+ assert format_float_metric(-1.23456, 1, 2) == -1.23
38
+ assert format_float_metric(-0.534, 1000, 2) == -534.0
39
+
40
+ def test_zero_values(self) -> None:
41
+ """Test zero handling."""
42
+ assert format_float_metric(0.0, 1, 2) == 0.0
43
+ assert format_float_metric(0.0, 1000, 2) == 0.0
44
+
45
+ def test_precision_constants(self) -> None:
46
+ """Test that precision constants are defined correctly."""
47
+ assert PRECISION_TIME_MS == 2
48
+ assert PRECISION_DISTANCE_M == 3
49
+ assert PRECISION_VELOCITY_M_S == 4
50
+ assert PRECISION_FRAME == 3
51
+ assert PRECISION_NORMALIZED == 4
52
+
53
+
54
+ class TestFormatIntMetric:
55
+ """Test integer metric formatting."""
56
+
57
+ def test_float_to_int(self) -> None:
58
+ """Test float to integer conversion."""
59
+ assert format_int_metric(42.7) == 42
60
+ assert format_int_metric(42.2) == 42
61
+ assert format_int_metric(42.9) == 42
62
+
63
+ def test_int_passthrough(self) -> None:
64
+ """Test that integers pass through."""
65
+ assert format_int_metric(42) == 42
66
+ assert format_int_metric(0) == 0
67
+
68
+ def test_none_handling(self) -> None:
69
+ """Test that None values pass through."""
70
+ assert format_int_metric(None) is None
71
+
72
+ def test_negative_values(self) -> None:
73
+ """Test negative value handling."""
74
+ assert format_int_metric(-42.7) == -42
75
+
76
+
77
+ class TestPrecisionConsistency:
78
+ """Test precision consistency across measurement types."""
79
+
80
+ def test_time_precision(self) -> None:
81
+ """Test time measurements use 2 decimal places."""
82
+ # Time in milliseconds
83
+ time_ms = format_float_metric(534.123456, 1, PRECISION_TIME_MS)
84
+ assert time_ms == 534.12
85
+
86
+ # Seconds to milliseconds
87
+ time_s_to_ms = format_float_metric(0.534123456, 1000, PRECISION_TIME_MS)
88
+ assert time_s_to_ms == 534.12
89
+
90
+ def test_distance_precision(self) -> None:
91
+ """Test distance measurements use 3 decimal places."""
92
+ # Jump height in meters
93
+ height = format_float_metric(0.35212345, 1, PRECISION_DISTANCE_M)
94
+ assert height == 0.352
95
+
96
+ # Countermovement depth
97
+ depth = format_float_metric(0.04512345, 1, PRECISION_DISTANCE_M)
98
+ assert depth == 0.045
99
+
100
+ def test_velocity_precision(self) -> None:
101
+ """Test velocity measurements use 4 decimal places."""
102
+ # Velocity in m/s
103
+ velocity = format_float_metric(2.63401234, 1, PRECISION_VELOCITY_M_S)
104
+ assert velocity == 2.634
105
+
106
+ # Negative velocity (downward)
107
+ neg_velocity = format_float_metric(-1.23459999, 1, PRECISION_VELOCITY_M_S)
108
+ assert neg_velocity == -1.2346
109
+
110
+ def test_frame_precision(self) -> None:
111
+ """Test frame numbers use 3 decimal places for sub-frame precision."""
112
+ frame = format_float_metric(154.342567, 1, PRECISION_FRAME)
113
+ assert frame == 154.343
114
+
115
+ def test_normalized_precision(self) -> None:
116
+ """Test normalized values use 4 decimal places."""
117
+ normalized = format_float_metric(0.058234567, 1, PRECISION_NORMALIZED)
118
+ assert normalized == 0.0582
119
+
120
+
121
+ class TestRealWorldExamples:
122
+ """Test with real-world measurement examples."""
123
+
124
+ def test_cmj_measurements(self) -> None:
125
+ """Test CMJ measurement formatting."""
126
+ # Jump height: 0.40502274976565683 → 0.405
127
+ assert format_float_metric(0.40502274976565683, 1, 3) == 0.405
128
+
129
+ # Flight time: 0.57471 seconds → 574.71 ms
130
+ assert format_float_metric(0.57471, 1000, 2) == 574.71
131
+
132
+ # Countermovement depth: 0.0024646043777466486 → 0.002
133
+ assert format_float_metric(0.0024646043777466486, 1, 3) == 0.002
134
+
135
+ # Peak velocity: 0.0028023441632588253 → 0.0028
136
+ assert format_float_metric(0.0028023441632588253, 1, 4) == 0.0028
137
+
138
+ def test_dropjump_measurements(self) -> None:
139
+ """Test drop jump measurement formatting."""
140
+ # Ground contact time: 0.35709 seconds → 357.09 ms
141
+ assert format_float_metric(0.35709, 1000, 2) == 357.09
142
+
143
+ # Jump height: 0.259123 → 0.259
144
+ assert format_float_metric(0.259123, 1, 3) == 0.259
145
+
146
+ # Trajectory normalized: 0.058234 → 0.0582
147
+ assert format_float_metric(0.058234, 1, 4) == 0.0582
148
+
149
+ # Precise frame: 32.034567 → 32.035
150
+ assert format_float_metric(32.034567, 1, 3) == 32.035
151
+
152
+
153
+ class TestEdgeCases:
154
+ """Test edge cases and boundary conditions."""
155
+
156
+ def test_very_small_values(self) -> None:
157
+ """Test very small values near zero."""
158
+ # Should not use scientific notation
159
+ assert format_float_metric(0.0001, 1, 4) == 0.0001
160
+ assert format_float_metric(0.00001, 1, 5) == 0.00001
161
+
162
+ def test_very_large_values(self) -> None:
163
+ """Test very large values."""
164
+ assert format_float_metric(999999.123, 1, 2) == 999999.12
165
+ assert format_float_metric(1000000.5, 1, 1) == 1000000.5
166
+
167
+ def test_exact_values(self) -> None:
168
+ """Test values that are exact after rounding."""
169
+ assert format_float_metric(1.5, 1, 2) == 1.5
170
+ assert format_float_metric(1.50, 1, 2) == 1.5
171
+ assert format_float_metric(0.0, 1, 3) == 0.0
172
+
173
+ def test_rounding_at_boundary(self) -> None:
174
+ """Test rounding at .5 boundary (Python uses banker's rounding)."""
175
+ # Banker's rounding: round to nearest even
176
+ assert format_float_metric(2.5, 1, 0) == 2.0 # rounds to even (2)
177
+ assert format_float_metric(3.5, 1, 0) == 4.0 # rounds to even (4)
178
+ assert format_float_metric(1.25, 1, 1) == 1.2 # rounds to even (1.2)
179
+ assert format_float_metric(1.35, 1, 1) == 1.4 # rounds to even (1.4)
@@ -700,7 +700,7 @@ wheels = [
700
700
 
701
701
  [[package]]
702
702
  name = "kinemotion"
703
- version = "0.27.0"
703
+ version = "0.28.0"
704
704
  source = { editable = "." }
705
705
  dependencies = [
706
706
  { name = "click" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes