rowingdata 3.7.3__tar.gz → 3.8.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 (204) hide show
  1. {rowingdata-3.7.3/rowingdata.egg-info → rowingdata-3.8.0}/PKG-INFO +71 -24
  2. {rowingdata-3.7.3 → rowingdata-3.8.0}/README.rst +70 -23
  3. rowingdata-3.8.0/rowingdata/data/fit_export_spec.json +88 -0
  4. rowingdata-3.8.0/rowingdata/fit_garmin_bridge.py +155 -0
  5. rowingdata-3.8.0/rowingdata/fit_transcode.py +127 -0
  6. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/fitwrite.py +513 -146
  7. rowingdata-3.8.0/rowingdata/fitwrite_spec.py +212 -0
  8. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/otherparsers.py +263 -8
  9. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/rowingdata.py +43 -15
  10. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/tcxtools.py +1 -1
  11. {rowingdata-3.7.3 → rowingdata-3.8.0/rowingdata.egg-info}/PKG-INFO +71 -24
  12. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata.egg-info/SOURCES.txt +7 -0
  13. {rowingdata-3.7.3 → rowingdata-3.8.0}/setup.py +4 -1
  14. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testdatasummary.csv +3 -3
  15. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testtcx.tcx +1 -1
  16. rowingdata-3.8.0/tests/test_fit_instroke_export.py +462 -0
  17. rowingdata-3.8.0/tests/test_fit_standard.py +316 -0
  18. rowingdata-3.8.0/tests/test_fit_transcode.py +78 -0
  19. {rowingdata-3.7.3 → rowingdata-3.8.0}/tests/test_rowingdata.py +211 -4
  20. {rowingdata-3.7.3 → rowingdata-3.8.0}/LICENSE +0 -0
  21. {rowingdata-3.7.3 → rowingdata-3.8.0}/MANIFEST.in +0 -0
  22. {rowingdata-3.7.3 → rowingdata-3.8.0}/bin/crewnerddata.csv +0 -0
  23. {rowingdata-3.7.3 → rowingdata-3.8.0}/bin/crewnerddata.tcx +0 -0
  24. {rowingdata-3.7.3 → rowingdata-3.8.0}/bin/testdata.csv +0 -0
  25. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/.buildinfo +0 -0
  26. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/2x20min.png +0 -0
  27. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/editrower.JPG +0 -0
  28. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/greghoc.png +0 -0
  29. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/image001.png +0 -0
  30. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/otwlogbook.JPG +0 -0
  31. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/otwscreenshot.JPG +0 -0
  32. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/screenshot.JPG +0 -0
  33. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/screenshotlogbook.JPG +0 -0
  34. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_images/woensdag.png +0 -0
  35. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_sources/index.txt +0 -0
  36. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_sources/modules.txt +0 -0
  37. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_sources/rowingdata.txt +0 -0
  38. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/ajax-loader.gif +0 -0
  39. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/alabaster.css +0 -0
  40. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/basic.css +0 -0
  41. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/comment-bright.png +0 -0
  42. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/comment-close.png +0 -0
  43. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/comment.png +0 -0
  44. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/custom.css +0 -0
  45. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/doctools.js +0 -0
  46. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/down-pressed.png +0 -0
  47. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/down.png +0 -0
  48. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/file.png +0 -0
  49. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/jquery-1.11.1.js +0 -0
  50. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/jquery.js +0 -0
  51. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/minus.png +0 -0
  52. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/plus.png +0 -0
  53. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/pygments.css +0 -0
  54. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/searchtools.js +0 -0
  55. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/underscore-1.3.1.js +0 -0
  56. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/underscore.js +0 -0
  57. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/up-pressed.png +0 -0
  58. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/up.png +0 -0
  59. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/_static/websupport.js +0 -0
  60. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/genindex.html +0 -0
  61. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/index.html +0 -0
  62. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/modules.html +0 -0
  63. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/objects.inv +0 -0
  64. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/py-modindex.html +0 -0
  65. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/rowingdata.html +0 -0
  66. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/search.html +0 -0
  67. {rowingdata-3.7.3 → rowingdata-3.8.0}/docs/_build/html/searchindex.js +0 -0
  68. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/__init__.py +0 -0
  69. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/__main__.py +0 -0
  70. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/boatedit.py +0 -0
  71. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/checkdatafiles.py +0 -0
  72. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/copystats.py +0 -0
  73. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/crewnerdplot.py +0 -0
  74. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/crewnerdplottime.py +0 -0
  75. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/csvparsers.py +0 -0
  76. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/ergdataplot.py +0 -0
  77. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/ergdataplottime.py +0 -0
  78. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/ergdatatotcx.py +0 -0
  79. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/ergstickplot.py +0 -0
  80. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/ergstickplottime.py +0 -0
  81. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/ergsticktotcx.py +0 -0
  82. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/gpxtools.py +0 -0
  83. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/gpxwrite.py +0 -0
  84. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/konkatenaadje.py +0 -0
  85. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/laptesting.py +0 -0
  86. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/obsolete.py +0 -0
  87. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/painsled_desktop_plot.py +0 -0
  88. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/painsled_desktop_plottime.py +0 -0
  89. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/painsled_desktop_toc2.py +0 -0
  90. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/painsledplot.py +0 -0
  91. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/painsledplottime.py +0 -0
  92. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/painsledtoc2.py +0 -0
  93. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/roweredit.py +0 -0
  94. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/rowproplot.py +0 -0
  95. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/rowproplottime.py +0 -0
  96. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/speedcoachplot.py +0 -0
  97. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/speedcoachplottime.py +0 -0
  98. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/speedcoachtoc2.py +0 -0
  99. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/tcxplot.py +0 -0
  100. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/tcxplot_nogeo.py +0 -0
  101. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/tcxplottime.py +0 -0
  102. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/tcxplottime_nogeo.py +0 -0
  103. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/tcxtoc2.py +0 -0
  104. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/trainingparser.py +0 -0
  105. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/utils.py +0 -0
  106. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/windcorrected.py +0 -0
  107. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata/writetcx.py +0 -0
  108. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata.egg-info/dependency_links.txt +0 -0
  109. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata.egg-info/not-zip-safe +0 -0
  110. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata.egg-info/requires.txt +0 -0
  111. {rowingdata-3.7.3 → rowingdata-3.8.0}/rowingdata.egg-info/top_level.txt +0 -0
  112. {rowingdata-3.7.3 → rowingdata-3.8.0}/setup.cfg +0 -0
  113. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/2016-03-25-0758.CSV +0 -0
  114. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/2016-03-25-0758_data.CSV +0 -0
  115. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/2x20min_o.csv +0 -0
  116. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/2x2km.tcx +0 -0
  117. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/3km_cd_o.csv +0 -0
  118. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/3x250m.tcx +0 -0
  119. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/EUBoatCoach.csv +0 -0
  120. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/EmpowerSpeedCoachForce.csv +0 -0
  121. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/NKEmporfromgreg.csv +0 -0
  122. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/NKLiNKv130.csv +0 -0
  123. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/NoHR.tcx +0 -0
  124. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/PainsledForce.csv +0 -0
  125. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/RP_interval.csv +0 -0
  126. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/RP_interval.csv_o.CSV +0 -0
  127. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/RP_testdata.csv +0 -0
  128. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/SpdCoach2_imp_inconsistent.csv +0 -0
  129. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/SpdCoachAmbiguous.csv +0 -0
  130. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/SpeedCoach GPS Workout.csv +0 -0
  131. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/SpeedCoach2Link_interval.csv +0 -0
  132. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/SpeedCoach2Linkv1.27.csv +0 -0
  133. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/SpeedCoach2v2.12.csv +0 -0
  134. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/Speedcoach2example.csv +0 -0
  135. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/aritmo.csv +0 -0
  136. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/bc1.csv +0 -0
  137. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/bc2.csv +0 -0
  138. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/bc3.csv +0 -0
  139. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/boatcoach.csv +0 -0
  140. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/boatcoach_fixed_distance.csv +0 -0
  141. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/boatcoach_otw.csv +0 -0
  142. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/correctedpainsled.csv +0 -0
  143. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/cottwich.csv +0 -0
  144. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/coxmate.csv +0 -0
  145. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/crewnerd_interval.csv +0 -0
  146. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/crewnerddata.CSV +0 -0
  147. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/crewnerddata.tcx +0 -0
  148. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/crewnerddata_o.CSV +0 -0
  149. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/cumvalues.csv +0 -0
  150. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ergdata_example.csv +0 -0
  151. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ergdata_example.csv_o.CSV +0 -0
  152. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ergstick.csv +0 -0
  153. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ergstick.csv_o.CSV +0 -0
  154. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ergstick2.csv +0 -0
  155. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ergstick_o.csv +0 -0
  156. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/example.csv +0 -0
  157. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/example_data.csv +0 -0
  158. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/faketcx.tcx +0 -0
  159. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/float.csv +0 -0
  160. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/herodata.csv +0 -0
  161. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/humon.csv +0 -0
  162. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/impeller_empower.csv +0 -0
  163. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/invalidchar.tcx +0 -0
  164. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/kinomap.csv +0 -0
  165. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/mystery.csv +0 -0
  166. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/nk_logbook.csv +0 -0
  167. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/painsled.tcx +0 -0
  168. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/painsled_desktop_example.csv +0 -0
  169. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/painsled_out_data.csv +0 -0
  170. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/painsled_to_csv_20160221-105218_erg-400150318_760465m.csv +0 -0
  171. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/quiske_per_stroke_left.csv +0 -0
  172. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/quiske_per_stroke_new.csv +0 -0
  173. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/quiske_per_stroke_right.csv +0 -0
  174. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/quiske_per_stroke_seat.csv +0 -0
  175. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/recover.tcx +0 -0
  176. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/ritmointervals.csv +0 -0
  177. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rowingdata_eth.csv +0 -0
  178. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rowinginmotionexample.TCX +0 -0
  179. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rowinginmotionexample.TCX_o.CSV +0 -0
  180. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rowperfect.csv +0 -0
  181. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rowpro5.csv +0 -0
  182. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rowpro_carrick.csv +0 -0
  183. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rp3_curve.csv +0 -0
  184. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rp3intervals.csv +0 -0
  185. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rp3intervals2.csv +0 -0
  186. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/rp_out.csv +0 -0
  187. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/smartrow.csv +0 -0
  188. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/smartrow_intervals.csv +0 -0
  189. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/spdcoach2noheader.csv +0 -0
  190. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/speedcoach2test2.csv +0 -0
  191. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/speedcoach3test3.csv +0 -0
  192. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/speedcoachexample.csv +0 -0
  193. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/speedcoachexample.csv_o.CSV +0 -0
  194. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/strava_export.tcx +0 -0
  195. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/summarytest.csv +0 -0
  196. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testdata.csv +0 -0
  197. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testdata_part1.csv +0 -0
  198. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testdata_part2.csv +0 -0
  199. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testlapidx.csv +0 -0
  200. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testtcs_210614.tcx +0 -0
  201. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/testtcx_210614.tcx +0 -0
  202. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata/tim.csv +0 -0
  203. {rowingdata-3.7.3 → rowingdata-3.8.0}/testdata//345/210/222/350/210/271.csv" +0 -0
  204. {rowingdata-3.7.3 → rowingdata-3.8.0}/tests/testparser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rowingdata
3
- Version: 3.7.3
3
+ Version: 3.8.0
4
4
  Summary: The rowingdata library to create colorful plots from CrewNerd, Painsled and other rowing data tools
5
5
  Home-page: https://github.com/sanderroosendaal/rowingdata
6
6
  Author: Sander Roosendaal
@@ -546,8 +546,10 @@ The available data fields are
546
546
  * ' StrokeDistance (meters)'
547
547
  * ' DriveTime (ms)'
548
548
  * ' StrokeRecoveryTime (ms)'
549
- * ' AverageDriveForce (lbs)'
550
- * ' PeakDriveForce (lbs)'
549
+ * ' AverageDriveForce (N)' (preferred; SI units)
550
+ * ' PeakDriveForce (N)' (preferred; SI units)
551
+ * ' AverageDriveForce (lbs)' (legacy; still supported)
552
+ * ' PeakDriveForce (lbs)' (legacy; still supported)
551
553
  * 'cum_dist'
552
554
 
553
555
  Oarlock parsers (NK Logbook) may add: ``catch``, ``finish``,
@@ -556,17 +558,22 @@ Oarlock parsers (NK Logbook) may add: ``catch``, ``finish``,
556
558
  If imported from TCX, Rowpro, or other tools, some data fields may not contain
557
559
  useful information.
558
560
 
559
- =================
560
- CSV File Standard
561
- =================
561
+ =======================
562
+ rowingdata CSV Columns
563
+ =======================
562
564
 
563
- The basic rowingdata class reads CSV files that adher to the standard
564
- described here. Any parser implementation should adher to the minimum
565
- standard as described here.
565
+ This section describes the dataframe schema used by the ``rowingdata`` class.
566
+ It is this package's own column layout, not a vendor-neutral standard. For a
567
+ cross-vendor interchange format, see the Rowing Data Standard at
568
+ https://github.com/MoveLab-Studio/rowing-data-standard, which rowingdata
569
+ targets when exporting FIT.
566
570
 
567
- Please send to me any CSV file that adhers to the standard described here
571
+ Any parser implementation feeding the ``rowingdata`` class should produce at
572
+ least the columns described below.
573
+
574
+ Please send to me any CSV file that follows the layout described here
568
575
  but does not parse well in the "rowingdata" module. I will update the module
569
- and add the file to standard testing.
576
+ and add the file to regression testing.
570
577
 
571
578
  Field Names (Columns)
572
579
  ----------------------
@@ -583,14 +590,23 @@ The standard field names are:
583
590
  * ' StrokeDistance (meters)'
584
591
  * ' DriveTime (ms)'
585
592
  * ' StrokeRecoveryTime (ms)'
586
- * ' AverageDriveForce (lbs)'
587
- * ' PeakDriveForce (lbs)'
593
+ * ' AverageDriveForce (N)' (preferred; SI units)
594
+ * ' PeakDriveForce (N)' (preferred; SI units)
595
+ * ' AverageDriveForce (lbs)' (legacy; still supported)
596
+ * ' PeakDriveForce (lbs)' (legacy; still supported)
588
597
  * ' lapIdx'
589
598
  * ' ElapsedTime (sec)'
590
599
 
591
600
  Optional (Oarlock/NK Logbook): ``catch``/``catchAngle``, ``finish``/``finishAngle``,
592
601
  ``slip``, ``wash``, ``peakforceangle``, ``effectiveLength``.
593
602
 
603
+ Planned for dual oarlock (port/starboard symmetry): ``catch_port``, ``catch_starboard``,
604
+ ``finish_port``, ``finish_starboard``, ``slip_port``, ``slip_starboard``, ``wash_port``,
605
+ ``wash_starboard``, ``peakforceangle_port``, ``peakforceangle_starboard``,
606
+ ``effectiveLength_port``, ``effectiveLength_starboard``. See *Dual oarlock
607
+ (port/starboard)* below. **Note:** rowingdata does not yet implement parsing or
608
+ export for these per-side columns.
609
+
594
610
  The CSV file adheres to the US conventions, with fields
595
611
  separated by a comma (',')
596
612
  and using the dot '.' as the decimal symbol.
@@ -792,14 +808,12 @@ The duration of the recovery part. See Drive Time for the definition. Drive Time
792
808
  Average Drive Force
793
809
  --------------------
794
810
 
795
- Field name: ' AverageDriveForce (lbs)' / ' AverageDriveForce (N)'
811
+ Field name: ' AverageDriveForce (N)' (preferred) or ' AverageDriveForce (lbs)' (legacy)
796
812
 
797
- Unit: lbs - or N, see below
813
+ Unit: newtons (N), or pounds (lb) for older files and tools
798
814
 
799
- Currently implemented is only the field name and value in lbs. In the future,
800
- we will implement ' AverageDriveForce (N)' so we can report in SI units.
801
-
802
- This should be the part of the handle force that does actual work.
815
+ Use **Newtons** for new data and integrations. **Pounds** remain supported for
816
+ backward compatibility. This should be the part of the handle force that does actual work.
803
817
 
804
818
  For dynamic ergs and OTW rowing this is not sufficient to describe the
805
819
  complete stroke dynamics, so additional fields can be defined, for example
@@ -822,9 +836,9 @@ as opposed to an average over time or oar angles.
822
836
  Peak Drive Force
823
837
  ------------------
824
838
 
825
- Field name: ' PeakDriveForce (lbs)' or ' PeakDriveForce (N)'
839
+ Field name: ' PeakDriveForce (N)' (preferred) or ' PeakDriveForce (lbs)' (legacy)
826
840
 
827
- Unit: lbs (currently) or N (supported in the future)
841
+ Unit: newtons (N), or pounds (lb) for older files and tools
828
842
 
829
843
  See discussion about measuring forces under Average Drive Force.
830
844
 
@@ -840,11 +854,14 @@ source: NK Logbook uses these columns).
840
854
  These fields come from NK Logbook (Oarlock) and similar OTW
841
855
  measurement systems. They describe rigging geometry and blade angles:
842
856
 
843
- * **catch**, **finish** – oar angles at catch and finish (degrees).
844
- NK uses catchAngle, finishAngle.
857
+ * **catch**, **finish** – oar angles at catch and finish (degrees),
858
+ using the rowing convention where **0° = oar perpendicular to the boat's
859
+ longitudinal axis**. Negative values indicate the catch direction (oar blade
860
+ toward bow, handle toward stern); positive values indicate the finish direction
861
+ (oar blade toward stern, handle toward bow). NK uses catchAngle, finishAngle.
845
862
  * **slip** – blade slip angle.
846
863
  * **wash** – wash/backwash angle.
847
- * **peakforceangle** – oar angle at peak force.
864
+ * **peakforceangle** – oar angle at peak force (same 0° = perpendicular convention).
848
865
  * **effectiveLength** – effective lever length (rigging geometry). *Not*
849
866
  the same as Drive Length: Effective Length is the horizontal component
850
867
  from pin to handle (a rigging/setup measure, often in cm), whereas
@@ -852,6 +869,16 @@ measurement systems. They describe rigging geometry and blade angles:
852
869
  stroke (typically 1.2–1.5 m). Both may appear in the same dataset;
853
870
  they serve different purposes.
854
871
 
872
+ Dual oarlock (port/starboard)
873
+ ..............................
874
+
875
+ When a rower uses two smart oarlocks, oarlock metrics can be reported per side:
876
+ port (left) and starboard (right). Each metric may appear as a per-side column
877
+ (e.g. ``catch_port``, ``catch_starboard``); the unsuffixed summary column
878
+ (``catch``) is the average of the two sides when both exist, and the value of
879
+ whichever side is present otherwise. **Note:** rowingdata does not yet
880
+ implement parsing or FIT export for these per-side columns.
881
+
855
882
  Lap Identifier
856
883
  -------------------
857
884
 
@@ -940,6 +967,26 @@ Boat bearing.
940
967
  Release Notes:
941
968
  ================
942
969
 
970
+ Unreleased
971
+ ----------
972
+
973
+ - **Breaking change to exported FIT file contents.** FIT export now follows the
974
+ Rowing Data Standard (https://github.com/MoveLab-Studio/rowing-data-standard,
975
+ Draft v0.1). The ``exporttofit`` API is unchanged, but the bytes it writes are
976
+ not: the developer application ID is now the standard UUID, ``DriveLength``
977
+ and ``EffectiveLength`` are encoded in millimetres rather than metres,
978
+ ``AverageBoatSpeed`` uses scale 255, stroke rate moves to field ID 93, and
979
+ stroke work to field ID 19. Consumers that hardcoded the previous rowingdata
980
+ application ID or the old metre-based scales must be updated. ``FITParser``
981
+ still reads files written by rowingdata 3.7.3 and earlier.
982
+ - In-stroke curve fields are emitted under a separate, private application ID,
983
+ because those field IDs are not allocated in the standard's registry and so
984
+ carry no interoperable meaning.
985
+ - Removed ``docs/FIT_STANDARD.md`` and ``docs/FIT_STANDARD.pdf``. The standard is
986
+ maintained by a committee at
987
+ https://github.com/MoveLab-Studio/rowing-data-standard, which is the single
988
+ source of truth; this repository no longer ships a copy.
989
+
943
990
  0.97.x
944
991
  ------
945
992
 
@@ -514,8 +514,10 @@ The available data fields are
514
514
  * ' StrokeDistance (meters)'
515
515
  * ' DriveTime (ms)'
516
516
  * ' StrokeRecoveryTime (ms)'
517
- * ' AverageDriveForce (lbs)'
518
- * ' PeakDriveForce (lbs)'
517
+ * ' AverageDriveForce (N)' (preferred; SI units)
518
+ * ' PeakDriveForce (N)' (preferred; SI units)
519
+ * ' AverageDriveForce (lbs)' (legacy; still supported)
520
+ * ' PeakDriveForce (lbs)' (legacy; still supported)
519
521
  * 'cum_dist'
520
522
 
521
523
  Oarlock parsers (NK Logbook) may add: ``catch``, ``finish``,
@@ -524,17 +526,22 @@ Oarlock parsers (NK Logbook) may add: ``catch``, ``finish``,
524
526
  If imported from TCX, Rowpro, or other tools, some data fields may not contain
525
527
  useful information.
526
528
 
527
- =================
528
- CSV File Standard
529
- =================
529
+ =======================
530
+ rowingdata CSV Columns
531
+ =======================
530
532
 
531
- The basic rowingdata class reads CSV files that adher to the standard
532
- described here. Any parser implementation should adher to the minimum
533
- standard as described here.
533
+ This section describes the dataframe schema used by the ``rowingdata`` class.
534
+ It is this package's own column layout, not a vendor-neutral standard. For a
535
+ cross-vendor interchange format, see the Rowing Data Standard at
536
+ https://github.com/MoveLab-Studio/rowing-data-standard, which rowingdata
537
+ targets when exporting FIT.
534
538
 
535
- Please send to me any CSV file that adhers to the standard described here
539
+ Any parser implementation feeding the ``rowingdata`` class should produce at
540
+ least the columns described below.
541
+
542
+ Please send to me any CSV file that follows the layout described here
536
543
  but does not parse well in the "rowingdata" module. I will update the module
537
- and add the file to standard testing.
544
+ and add the file to regression testing.
538
545
 
539
546
  Field Names (Columns)
540
547
  ----------------------
@@ -551,14 +558,23 @@ The standard field names are:
551
558
  * ' StrokeDistance (meters)'
552
559
  * ' DriveTime (ms)'
553
560
  * ' StrokeRecoveryTime (ms)'
554
- * ' AverageDriveForce (lbs)'
555
- * ' PeakDriveForce (lbs)'
561
+ * ' AverageDriveForce (N)' (preferred; SI units)
562
+ * ' PeakDriveForce (N)' (preferred; SI units)
563
+ * ' AverageDriveForce (lbs)' (legacy; still supported)
564
+ * ' PeakDriveForce (lbs)' (legacy; still supported)
556
565
  * ' lapIdx'
557
566
  * ' ElapsedTime (sec)'
558
567
 
559
568
  Optional (Oarlock/NK Logbook): ``catch``/``catchAngle``, ``finish``/``finishAngle``,
560
569
  ``slip``, ``wash``, ``peakforceangle``, ``effectiveLength``.
561
570
 
571
+ Planned for dual oarlock (port/starboard symmetry): ``catch_port``, ``catch_starboard``,
572
+ ``finish_port``, ``finish_starboard``, ``slip_port``, ``slip_starboard``, ``wash_port``,
573
+ ``wash_starboard``, ``peakforceangle_port``, ``peakforceangle_starboard``,
574
+ ``effectiveLength_port``, ``effectiveLength_starboard``. See *Dual oarlock
575
+ (port/starboard)* below. **Note:** rowingdata does not yet implement parsing or
576
+ export for these per-side columns.
577
+
562
578
  The CSV file adheres to the US conventions, with fields
563
579
  separated by a comma (',')
564
580
  and using the dot '.' as the decimal symbol.
@@ -760,14 +776,12 @@ The duration of the recovery part. See Drive Time for the definition. Drive Time
760
776
  Average Drive Force
761
777
  --------------------
762
778
 
763
- Field name: ' AverageDriveForce (lbs)' / ' AverageDriveForce (N)'
779
+ Field name: ' AverageDriveForce (N)' (preferred) or ' AverageDriveForce (lbs)' (legacy)
764
780
 
765
- Unit: lbs - or N, see below
781
+ Unit: newtons (N), or pounds (lb) for older files and tools
766
782
 
767
- Currently implemented is only the field name and value in lbs. In the future,
768
- we will implement ' AverageDriveForce (N)' so we can report in SI units.
769
-
770
- This should be the part of the handle force that does actual work.
783
+ Use **Newtons** for new data and integrations. **Pounds** remain supported for
784
+ backward compatibility. This should be the part of the handle force that does actual work.
771
785
 
772
786
  For dynamic ergs and OTW rowing this is not sufficient to describe the
773
787
  complete stroke dynamics, so additional fields can be defined, for example
@@ -790,9 +804,9 @@ as opposed to an average over time or oar angles.
790
804
  Peak Drive Force
791
805
  ------------------
792
806
 
793
- Field name: ' PeakDriveForce (lbs)' or ' PeakDriveForce (N)'
807
+ Field name: ' PeakDriveForce (N)' (preferred) or ' PeakDriveForce (lbs)' (legacy)
794
808
 
795
- Unit: lbs (currently) or N (supported in the future)
809
+ Unit: newtons (N), or pounds (lb) for older files and tools
796
810
 
797
811
  See discussion about measuring forces under Average Drive Force.
798
812
 
@@ -808,11 +822,14 @@ source: NK Logbook uses these columns).
808
822
  These fields come from NK Logbook (Oarlock) and similar OTW
809
823
  measurement systems. They describe rigging geometry and blade angles:
810
824
 
811
- * **catch**, **finish** – oar angles at catch and finish (degrees).
812
- NK uses catchAngle, finishAngle.
825
+ * **catch**, **finish** – oar angles at catch and finish (degrees),
826
+ using the rowing convention where **0° = oar perpendicular to the boat's
827
+ longitudinal axis**. Negative values indicate the catch direction (oar blade
828
+ toward bow, handle toward stern); positive values indicate the finish direction
829
+ (oar blade toward stern, handle toward bow). NK uses catchAngle, finishAngle.
813
830
  * **slip** – blade slip angle.
814
831
  * **wash** – wash/backwash angle.
815
- * **peakforceangle** – oar angle at peak force.
832
+ * **peakforceangle** – oar angle at peak force (same 0° = perpendicular convention).
816
833
  * **effectiveLength** – effective lever length (rigging geometry). *Not*
817
834
  the same as Drive Length: Effective Length is the horizontal component
818
835
  from pin to handle (a rigging/setup measure, often in cm), whereas
@@ -820,6 +837,16 @@ measurement systems. They describe rigging geometry and blade angles:
820
837
  stroke (typically 1.2–1.5 m). Both may appear in the same dataset;
821
838
  they serve different purposes.
822
839
 
840
+ Dual oarlock (port/starboard)
841
+ ..............................
842
+
843
+ When a rower uses two smart oarlocks, oarlock metrics can be reported per side:
844
+ port (left) and starboard (right). Each metric may appear as a per-side column
845
+ (e.g. ``catch_port``, ``catch_starboard``); the unsuffixed summary column
846
+ (``catch``) is the average of the two sides when both exist, and the value of
847
+ whichever side is present otherwise. **Note:** rowingdata does not yet
848
+ implement parsing or FIT export for these per-side columns.
849
+
823
850
  Lap Identifier
824
851
  -------------------
825
852
 
@@ -908,6 +935,26 @@ Boat bearing.
908
935
  Release Notes:
909
936
  ================
910
937
 
938
+ Unreleased
939
+ ----------
940
+
941
+ - **Breaking change to exported FIT file contents.** FIT export now follows the
942
+ Rowing Data Standard (https://github.com/MoveLab-Studio/rowing-data-standard,
943
+ Draft v0.1). The ``exporttofit`` API is unchanged, but the bytes it writes are
944
+ not: the developer application ID is now the standard UUID, ``DriveLength``
945
+ and ``EffectiveLength`` are encoded in millimetres rather than metres,
946
+ ``AverageBoatSpeed`` uses scale 255, stroke rate moves to field ID 93, and
947
+ stroke work to field ID 19. Consumers that hardcoded the previous rowingdata
948
+ application ID or the old metre-based scales must be updated. ``FITParser``
949
+ still reads files written by rowingdata 3.7.3 and earlier.
950
+ - In-stroke curve fields are emitted under a separate, private application ID,
951
+ because those field IDs are not allocated in the standard's registry and so
952
+ carry no interoperable meaning.
953
+ - Removed ``docs/FIT_STANDARD.md`` and ``docs/FIT_STANDARD.pdf``. The standard is
954
+ maintained by a committee at
955
+ https://github.com/MoveLab-Studio/rowing-data-standard, which is the single
956
+ source of truth; this repository no longer ships a copy.
957
+
911
958
  0.97.x
912
959
  ------
913
960
 
@@ -0,0 +1,88 @@
1
+ {
2
+ "version": 2,
3
+ "_comment": "Authoritative FIT developer field definitions for rowingdata fitwrite. Field IDs, scales and units must match the Rowing Data Standard registry at https://github.com/MoveLab-Studio/rowing-data-standard (registry/field-ids.md); that repository is the source of truth, this file is only rowingdata's machine-readable copy of the subset it emits. v2: mm precision for DriveLength/PeakForcePositionAbs (scale 1000); StrokeRate dev field (93); instroke curve Y-scale registry.",
4
+ "instroke_dynamic": {
5
+ "summary_start": 20,
6
+ "summary_id_padding": "ceil_to_10",
7
+ "curve_start": 60
8
+ },
9
+ "instroke_curve_types": {
10
+ "HandleForceCurve": {
11
+ "y_scale": 10,
12
+ "y_units": "N",
13
+ "default_abscissa": "HANDLE_DISTANCE_UNIFORM_M"
14
+ },
15
+ "BoatAcceleratorCurve": {
16
+ "y_scale": 100,
17
+ "y_units": "m/s^2",
18
+ "default_abscissa": "TIME_UNIFORM_MS"
19
+ },
20
+ "OarAngleVelocityCurve": {
21
+ "y_scale": 10,
22
+ "y_units": "deg/s",
23
+ "default_abscissa": "TIME_UNIFORM_MS"
24
+ },
25
+ "SeatCurve": {
26
+ "y_scale": 255,
27
+ "y_units": "m",
28
+ "default_abscissa": "HANDLE_DISTANCE_UNIFORM_M"
29
+ }
30
+ },
31
+ "abscissa_enum": {
32
+ "UNKNOWN": 0,
33
+ "TIME_UNIFORM_MS": 1,
34
+ "HANDLE_DISTANCE_UNIFORM_M": 2,
35
+ "OAR_ANGLE_UNIFORM_DEG": 3,
36
+ "NORMALIZED_DRIVE_0_1": 4
37
+ },
38
+ "instroke_axis_field_ids": [90, 91, 92],
39
+ "always_emit_field_ids": [9, 90, 91, 92],
40
+ "instroke_column_map": {
41
+ "curve_data": "HandleForceCurve",
42
+ "boat accelerator curve": "BoatAcceleratorCurve",
43
+ "oar angle velocity curve": "OarAngleVelocityCurve",
44
+ "seat curve": "SeatCurve"
45
+ },
46
+ "developer_fields": [
47
+ {"field_id": 0, "group": "rowing", "fit_name": "DriveLength", "base_type": "UINT16", "size": 2, "scale": 1, "units": "mm", "df_columns": [" DriveLength (meters)"]},
48
+ {"field_id": 1, "group": "rowing", "fit_name": "StrokeDriveTime", "base_type": "UINT16", "size": 2, "scale": 1, "units": "ms", "df_columns": [" DriveTime (ms)"]},
49
+ {"field_id": 2, "group": "rowing", "fit_name": "DragFactor", "base_type": "UINT16", "size": 2, "scale": 1, "units": "", "df_columns": [" DragFactor"]},
50
+ {"field_id": 3, "group": "rowing", "fit_name": "StrokeRecoveryTime", "base_type": "UINT16", "size": 2, "scale": 1, "units": "ms", "df_columns": [" StrokeRecoveryTime (ms)"]},
51
+ {"field_id": 4, "group": "rowing", "fit_name": "AverageDriveForceLbs", "base_type": "UINT16", "size": 2, "scale": 10, "units": "lbs", "df_columns": [" AverageDriveForce (lbs)"]},
52
+ {"field_id": 5, "group": "rowing", "fit_name": "PeakDriveForceLbs", "base_type": "UINT16", "size": 2, "scale": 10, "units": "lbs", "df_columns": [" PeakDriveForce (lbs)"]},
53
+ {"field_id": 6, "group": "rowing", "fit_name": "AverageDriveForceN", "base_type": "UINT16", "size": 2, "scale": 10, "units": "N", "df_columns": [" AverageDriveForce (N)"]},
54
+ {"field_id": 7, "group": "rowing", "fit_name": "PeakDriveForceN", "base_type": "UINT16", "size": 2, "scale": 10, "units": "N", "df_columns": [" PeakDriveForce (N)"]},
55
+ {"field_id": 8, "group": "rowing", "fit_name": "AverageBoatSpeed", "base_type": "UINT16", "size": 2, "scale": 255, "units": "m/s", "df_columns": [" AverageBoatSpeed (m/s)"]},
56
+ {"field_id": 9, "group": "rowing", "fit_name": "WorkoutState", "base_type": "UINT8", "size": 1, "scale": 1, "units": "", "df_columns": [" WorkoutState"]},
57
+ {"field_id": 10, "group": "metadata", "fit_name": "RecordingStrategy", "base_type": "UINT8", "size": 1, "scale": 1, "units": "", "df_columns": [], "message_type": "session"},
58
+ {"field_id": 19, "group": "rowing", "fit_name": "StrokeWork", "base_type": "UINT16", "size": 2, "scale": 1, "units": "J", "df_columns": [" WorkPerStroke (joules)", "driveenergy"]},
59
+ {"field_id": 93, "group": "rowing", "fit_name": "StrokeRate", "base_type": "UINT16", "size": 2, "scale": 100, "units": "spm", "df_columns": [" Cadence (stokes/min)"]},
60
+
61
+ {"field_id": 11, "group": "oarlock", "fit_name": "Catch", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["catch", " catch", "catchAngle"]},
62
+ {"field_id": 12, "group": "oarlock", "fit_name": "Finish", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["finish", " finish", "finishAngle"]},
63
+ {"field_id": 13, "group": "oarlock", "fit_name": "Slip", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["slip", " slip"]},
64
+ {"field_id": 14, "group": "oarlock", "fit_name": "Wash", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["wash", " wash"]},
65
+ {"field_id": 15, "group": "oarlock", "fit_name": "PeakForceAngle", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["peakforceangle", " peakforceangle"]},
66
+ {"field_id": 16, "group": "oarlock", "fit_name": "EffectiveLength", "base_type": "UINT16", "size": 2, "scale": 1, "units": "mm", "df_columns": ["effectiveLength", " effectiveLength", "effective length"]},
67
+
68
+ {"field_id": 200, "group": "oarlock_dual", "dual_pair": "catch", "side": "port", "fit_name": "CatchPort", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["catch_port", "catchAngle_port"]},
69
+ {"field_id": 201, "group": "oarlock_dual", "dual_pair": "catch", "side": "starboard", "fit_name": "CatchStarboard", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["catch_starboard", "catchAngle_starboard"]},
70
+ {"field_id": 202, "group": "oarlock_dual", "dual_pair": "finish", "side": "port", "fit_name": "FinishPort", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["finish_port", "finishAngle_port"]},
71
+ {"field_id": 203, "group": "oarlock_dual", "dual_pair": "finish", "side": "starboard", "fit_name": "FinishStarboard", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["finish_starboard", "finishAngle_starboard"]},
72
+ {"field_id": 204, "group": "oarlock_dual", "dual_pair": "slip", "side": "port", "fit_name": "SlipPort", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["slip_port"]},
73
+ {"field_id": 205, "group": "oarlock_dual", "dual_pair": "slip", "side": "starboard", "fit_name": "SlipStarboard", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["slip_starboard"]},
74
+ {"field_id": 206, "group": "oarlock_dual", "dual_pair": "wash", "side": "port", "fit_name": "WashPort", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["wash_port"]},
75
+ {"field_id": 207, "group": "oarlock_dual", "dual_pair": "wash", "side": "starboard", "fit_name": "WashStarboard", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["wash_starboard"]},
76
+ {"field_id": 208, "group": "oarlock_dual", "dual_pair": "peakforceangle", "side": "port", "fit_name": "PeakForceAnglePort", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["peakforceangle_port"]},
77
+ {"field_id": 209, "group": "oarlock_dual", "dual_pair": "peakforceangle", "side": "starboard", "fit_name": "PeakForceAngleStarboard", "base_type": "SINT16", "size": 2, "scale": 10, "units": "deg", "df_columns": ["peakforceangle_starboard"]},
78
+ {"field_id": 210, "group": "oarlock_dual", "dual_pair": "effectiveLength", "side": "port", "fit_name": "EffectiveLengthPort", "base_type": "UINT16", "size": 2, "scale": 1, "units": "mm", "df_columns": ["effectiveLength_port"]},
79
+ {"field_id": 211, "group": "oarlock_dual", "dual_pair": "effectiveLength", "side": "starboard", "fit_name": "EffectiveLengthStarboard", "base_type": "UINT16", "size": 2, "scale": 1, "units": "mm", "df_columns": ["effectiveLength_starboard"]},
80
+
81
+ {"field_id": 17, "group": "peak_position", "fit_name": "PeakForcePositionNorm", "base_type": "UINT16", "size": 2, "scale": 1, "units": "", "df_columns": ["rel_peak_force_pos", "PeakForcePositionNorm", "% of Stroke Complete When Peak Force Is Reached"], "transformer": "peak_norm", "clip_uint16_max": 10000},
82
+ {"field_id": 18, "group": "peak_position", "fit_name": "PeakForcePositionAbs", "base_type": "UINT16", "size": 2, "scale": 1, "units": "mm", "df_columns": ["peak_force_pos", "PeakForcePositionAbs"], "transformer": "peak_abs"},
83
+
84
+ {"field_id": 90, "group": "instroke_axis", "fit_name": "InstrokeAbscissaType", "base_type": "UINT8", "size": 1, "scale": 1, "units": "", "df_columns": []},
85
+ {"field_id": 91, "group": "instroke_axis", "fit_name": "InstrokeSampleInterval", "base_type": "UINT16", "size": 2, "scale": 1, "units": "", "df_columns": []},
86
+ {"field_id": 92, "group": "instroke_axis", "fit_name": "InstrokePointCount", "base_type": "UINT8", "size": 1, "scale": 1, "units": "", "df_columns": []}
87
+ ]
88
+ }
@@ -0,0 +1,155 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Bridge Garmin / OpenRowingMonitor FIT messages (Split, SplitSummary, Workout, WorkoutStep)
4
+ from fitparse into fit_tool GenericMessage for re-emission with rowingdata exports.
5
+
6
+ Global message numbers 312 and 313 are not in fitparse's profile (shown as unknown_312 / unknown_313);
7
+ they correspond to Split and SplitSummary in current Garmin FIT profiles.
8
+ """
9
+ from __future__ import absolute_import
10
+
11
+ from fit_tool.base_type import BaseType
12
+ from fit_tool.definition_message import DefinitionMessage
13
+ from fit_tool.endian import Endian
14
+ from fit_tool.field import Field
15
+ from fit_tool.field_definition import FieldDefinition
16
+ from fit_tool.generic_message import GenericMessage
17
+
18
+ from fitparse.base import FitFile as FitparseFitFile
19
+ # Garmin FIT profile (extended): Split / SplitSummary message numbers
20
+ MESG_SPLIT = 312
21
+ MESG_SPLIT_SUMMARY = 313
22
+ MESG_WORKOUT = 26
23
+ MESG_WORKOUT_STEP = 27
24
+
25
+ _DEFAULT_PRESERVE = (MESG_WORKOUT, MESG_WORKOUT_STEP, MESG_SPLIT_SUMMARY, MESG_SPLIT)
26
+
27
+
28
+ def _fitparse_base_type_to_fit_tool(fp_bt):
29
+ """Map fitparse records.BaseType name to fit_tool BaseType."""
30
+ name = fp_bt.name.lower()
31
+ mapping = {
32
+ 'enum': BaseType.ENUM,
33
+ 'sint8': BaseType.SINT8,
34
+ 'uint8': BaseType.UINT8,
35
+ 'string': BaseType.STRING,
36
+ 'sint16': BaseType.SINT16,
37
+ 'uint16': BaseType.UINT16,
38
+ 'sint32': BaseType.SINT32,
39
+ 'uint32': BaseType.UINT32,
40
+ 'float32': BaseType.FLOAT32,
41
+ 'float64': BaseType.FLOAT64,
42
+ 'byte': BaseType.BYTE,
43
+ 'uint8z': BaseType.UINT8Z,
44
+ 'uint16z': BaseType.UINT16Z,
45
+ 'uint32z': BaseType.UINT32Z,
46
+ 'uint64': BaseType.UINT64,
47
+ 'sint64': BaseType.SINT64,
48
+ }
49
+ return mapping.get(name, BaseType.UINT8)
50
+
51
+
52
+ def _field_definition_from_fitparse(fd):
53
+ bt = _fitparse_base_type_to_fit_tool(fd.base_type)
54
+ return FieldDefinition(field_id=fd.def_num, size=fd.size, base_type=bt)
55
+
56
+
57
+ def _set_field_from_raw(fit_field, raw_value):
58
+ """Copy raw decoded value from fitparse into fit_tool Field.encoded_values."""
59
+ bt = fit_field.base_type
60
+ if raw_value is None:
61
+ inv = bt.invalid_raw_value() if hasattr(bt, 'invalid_raw_value') else 0
62
+ fit_field.set_encoded_value(0, inv, check_validity=False)
63
+ return
64
+ if isinstance(raw_value, tuple):
65
+ for i, rv in enumerate(raw_value):
66
+ fit_field.set_encoded_value(i, int(rv) & 0xFFFFFFFF, check_validity=False)
67
+ return
68
+ if bt == BaseType.STRING:
69
+ fit_field.growable = True
70
+ s = raw_value if isinstance(raw_value, str) else str(raw_value)
71
+ fit_field.set_value(0, s[: fit_field.size] if fit_field.size else s)
72
+ return
73
+ n = len(fit_field.encoded_values)
74
+ if n == 1:
75
+ fit_field.set_encoded_value(0, int(raw_value), check_validity=False)
76
+ else:
77
+ fit_field.set_encoded_value(0, int(raw_value), check_validity=False)
78
+
79
+
80
+ def _generic_from_fitparse_data(def_mesg, data_msg, local_id):
81
+ """Build fit_tool DefinitionMessage + GenericMessage from fitparse messages."""
82
+ endian = Endian.LITTLE if def_mesg.endian == '<' else Endian.BIG
83
+ field_defs = [_field_definition_from_fitparse(fd) for fd in def_mesg.field_defs]
84
+ definition = DefinitionMessage(
85
+ local_id=local_id,
86
+ global_id=def_mesg.mesg_num,
87
+ endian=endian,
88
+ field_definitions=field_defs,
89
+ developer_field_definitions=[],
90
+ )
91
+ gen = GenericMessage(definition_message=definition, developer_fields=None)
92
+ # Zip field data: fitparse DataMessage.fields order matches def field_defs + dev
93
+ fp_fields = list(data_msg.fields)
94
+ for i, fd in enumerate(def_mesg.field_defs):
95
+ if i >= len(fp_fields):
96
+ break
97
+ fld = gen.get_field(fd.def_num)
98
+ if fld is None and i < len(gen.fields):
99
+ fld = gen.fields[i]
100
+ if fld is None:
101
+ continue
102
+ fv = fp_fields[i]
103
+ raw = fv.raw_value if fv.raw_value is not None else fv.value
104
+ _set_field_from_raw(fld, raw)
105
+ return definition, gen
106
+
107
+
108
+ def iter_preserved_generic_messages(fit_path, mesg_nums=None):
109
+ """
110
+ Parse FIT with fitparse low-level API; yield (definition, GenericMessage) pairs
111
+ for each data message whose global mesg_num is in mesg_nums, in file order.
112
+ Local message IDs are reassigned uniquely for fit_tool (starting at 8 to avoid
113
+ clashing with common record/lap local IDs).
114
+ """
115
+ if mesg_nums is None:
116
+ mesg_nums = _DEFAULT_PRESERVE
117
+ mesg_set = set(mesg_nums)
118
+ f = FitparseFitFile(fit_path)
119
+ while f._parse_message():
120
+ pass
121
+ # Map (mesg_num, tuple of field def signatures) -> next local_id
122
+ # Use local_id >= 8 to avoid clashing with fit_tool defaults (record/lap/event often use 0–3).
123
+ def_key_to_local = {}
124
+ next_local = 8
125
+ for m in f._messages:
126
+ if getattr(m, 'type', None) != 'data':
127
+ continue
128
+ if m.mesg_num not in mesg_set:
129
+ continue
130
+ def_mesg = m.def_mesg
131
+ key = (m.mesg_num, tuple((fd.def_num, fd.size, fd.base_type.name) for fd in def_mesg.field_defs))
132
+ if key not in def_key_to_local:
133
+ def_key_to_local[key] = next_local
134
+ next_local += 1
135
+ local_id = def_key_to_local[key]
136
+ definition, gen = _generic_from_fitparse_data(def_mesg, m, local_id)
137
+ yield definition, gen
138
+
139
+
140
+ def add_preserved_messages_to_builder(builder, fit_path, mesg_nums=None):
141
+ """
142
+ Add preserved native messages to a FitFileBuilder.
143
+
144
+ Emits each DefinitionMessage once (required by FIT), then each data message with
145
+ ``definition_message`` cleared so FitFileBuilder binds the message to the stored
146
+ definition (see fit_tool FitFileBuilder.add).
147
+ """
148
+ seen_def = set()
149
+ for definition, gen in iter_preserved_generic_messages(fit_path, mesg_nums=mesg_nums):
150
+ key = (definition.global_id, definition.local_id, tuple((fd.field_id, fd.size, fd.base_type) for fd in definition.field_definitions))
151
+ if key not in seen_def:
152
+ builder.add(definition)
153
+ seen_def.add(key)
154
+ gen.definition_message = None
155
+ builder.add(gen)