thrustlab 0.6.0__tar.gz → 0.7.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 (383) hide show
  1. {thrustlab-0.6.0 → thrustlab-0.7.0}/CHANGELOG.md +86 -1
  2. {thrustlab-0.6.0 → thrustlab-0.7.0}/PKG-INFO +21 -1
  3. {thrustlab-0.6.0 → thrustlab-0.7.0}/README.md +20 -0
  4. {thrustlab-0.6.0 → thrustlab-0.7.0}/pyproject.toml +7 -0
  5. thrustlab-0.7.0/tests/estimate/test_equation.py +234 -0
  6. thrustlab-0.7.0/tests/estimate/test_grid.py +663 -0
  7. thrustlab-0.7.0/tests/test_resources/__init__.py +0 -0
  8. thrustlab-0.7.0/tests/test_spec_coverage.py +217 -0
  9. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/__init__.py +74 -6
  10. thrustlab-0.7.0/thrustlab/_models/compare_entry.py +74 -0
  11. thrustlab-0.7.0/thrustlab/_models/compare_entry_in.py +89 -0
  12. thrustlab-0.7.0/thrustlab/_models/compare_lists_body.py +67 -0
  13. thrustlab-0.7.0/thrustlab/_models/compare_lists_in.py +177 -0
  14. thrustlab-0.7.0/thrustlab/_models/compare_lists_out.py +108 -0
  15. thrustlab-0.7.0/thrustlab/_models/compare_lists_resource.py +78 -0
  16. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_create_body.py +40 -8
  17. thrustlab-0.7.0/thrustlab/_models/dynamic_create_body_propeller_model.py +9 -0
  18. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_patch.py +21 -0
  19. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource.py +2 -2
  20. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in.py +66 -9
  21. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in.py +66 -9
  22. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/entitlements_resource.py +28 -4
  23. thrustlab-0.7.0/thrustlab/_models/propeller_label.py +167 -0
  24. thrustlab-0.7.0/thrustlab/_models/propeller_label_family.py +12 -0
  25. thrustlab-0.7.0/thrustlab/_models/public_catalog_redirect.py +79 -0
  26. thrustlab-0.7.0/thrustlab/_models/public_component.py +137 -0
  27. thrustlab-0.7.0/thrustlab/_models/public_component_facets.py +120 -0
  28. thrustlab-0.7.0/thrustlab/_models/public_component_facets_discrete.py +68 -0
  29. thrustlab-0.7.0/thrustlab/_models/public_component_facets_ranges.py +60 -0
  30. thrustlab-0.7.0/thrustlab/_models/public_component_facets_type.py +10 -0
  31. thrustlab-0.7.0/thrustlab/_models/public_component_list.py +128 -0
  32. thrustlab-0.7.0/thrustlab/_models/public_component_resource.py +145 -0
  33. thrustlab-0.7.0/thrustlab/_models/public_component_resource_spec_json.py +50 -0
  34. thrustlab-0.7.0/thrustlab/_models/public_component_resource_type.py +10 -0
  35. thrustlab-0.7.0/thrustlab/_models/public_component_spec_json.py +50 -0
  36. thrustlab-0.7.0/thrustlab/_models/public_component_type.py +10 -0
  37. thrustlab-0.7.0/thrustlab/_models/public_facet_range.py +97 -0
  38. thrustlab-0.7.0/thrustlab/_models/public_facet_value.py +70 -0
  39. thrustlab-0.7.0/thrustlab/_models/public_run_claim.py +93 -0
  40. thrustlab-0.7.0/thrustlab/_models/public_run_claim_kind.py +9 -0
  41. thrustlab-0.7.0/thrustlab/_models/public_run_view.py +88 -0
  42. thrustlab-0.7.0/thrustlab/_models/public_simulate_body.py +467 -0
  43. thrustlab-0.7.0/thrustlab/_models/public_simulate_body_cooling_source_type_0.py +11 -0
  44. thrustlab-0.7.0/thrustlab/_models/public_simulate_body_flight_regime.py +11 -0
  45. thrustlab-0.7.0/thrustlab/_models/public_simulate_body_inflow_mode.py +9 -0
  46. thrustlab-0.7.0/thrustlab/_models/public_simulate_body_propeller_model.py +9 -0
  47. thrustlab-0.7.0/thrustlab/_models/public_simulate_response.py +102 -0
  48. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in.py +59 -10
  49. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_resource.py +61 -9
  50. thrustlab-0.7.0/thrustlab/_models/rotor_group_resource_propeller_label_type_0.py +47 -0
  51. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in.py +59 -10
  52. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body.py +41 -8
  53. thrustlab-0.7.0/thrustlab/_models/simulation_create_body_propeller_model.py +9 -0
  54. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_patch.py +21 -0
  55. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource.py +11 -2
  56. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_config_in.py +111 -0
  57. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_create_body.py +88 -21
  58. thrustlab-0.6.0/thrustlab/_models/sweep_create_body_cooling_source.py → thrustlab-0.7.0/thrustlab/_models/sweep_create_body_cooling_source_type_0.py +1 -1
  59. thrustlab-0.7.0/thrustlab/_models/sweep_create_body_flight_regime.py +11 -0
  60. thrustlab-0.7.0/thrustlab/_models/sweep_create_body_propeller_model.py +9 -0
  61. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_patch.py +21 -0
  62. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_inputs.py +69 -0
  63. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_resource.py +5 -0
  64. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource.py +11 -2
  65. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in.py +59 -10
  66. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in.py +59 -10
  67. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/user_resource.py +31 -0
  68. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_version.py +1 -1
  69. thrustlab-0.7.0/thrustlab/estimate/__init__.py +368 -0
  70. thrustlab-0.7.0/thrustlab/estimate/__main__.py +3 -0
  71. thrustlab-0.7.0/thrustlab/estimate/_fixture/worked-examples.json +22823 -0
  72. thrustlab-0.7.0/thrustlab/estimate/cli.py +202 -0
  73. thrustlab-0.7.0/thrustlab/estimate/fixture.py +437 -0
  74. thrustlab-0.7.0/thrustlab/estimate/grid.py +1250 -0
  75. thrustlab-0.6.0/tests/test_spec_coverage.py +0 -117
  76. thrustlab-0.6.0/thrustlab/_models/list_public_propellers_v1_public_propellers_get_response_list_public_propellers_v1_public_propellers_get.py +0 -47
  77. {thrustlab-0.6.0 → thrustlab-0.7.0}/.gitignore +0 -0
  78. {thrustlab-0.6.0 → thrustlab-0.7.0}/LICENSE +0 -0
  79. {thrustlab-0.6.0 → thrustlab-0.7.0}/NOTICE +0 -0
  80. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/components/create_component.py +0 -0
  81. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/dynamic/run_and_poll.py +0 -0
  82. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/dynamic/watch_and_cancel.py +0 -0
  83. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/README.md +0 -0
  84. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/fly_mission.py +0 -0
  85. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/fmu.parm +0 -0
  86. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/plot_mission.py +0 -0
  87. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/quad_x.yaml +0 -0
  88. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/render_mission_video.py +0 -0
  89. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/README.md +0 -0
  90. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/fly_transition.py +0 -0
  91. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/tilttri.parm +0 -0
  92. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/tilttri.yaml +0 -0
  93. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/README.md +0 -0
  94. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/plot_coax_split.py +0 -0
  95. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/y6.parm +0 -0
  96. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/y6.yaml +0 -0
  97. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/README.md +0 -0
  98. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/betaflight_config.txt +0 -0
  99. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/betaflight_quad_x.yaml +0 -0
  100. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/fly_hover.py +0 -0
  101. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/endurance/README.md +0 -0
  102. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/endurance/hover_endurance.py +0 -0
  103. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/export_and_download.py +0 -0
  104. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/gazebo/README.md +0 -0
  105. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/gazebo/quad_fmu.sdf +0 -0
  106. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/quad_5inch_x.yaml +0 -0
  107. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/simulink/.gitignore +0 -0
  108. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/simulink/README.md +0 -0
  109. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/simulink/fmu_cosim.png +0 -0
  110. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/fmu/simulink/run_fmu_simulink.m +0 -0
  111. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/outputs/read_dynamic.py +0 -0
  112. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/outputs/read_steady_state.py +0 -0
  113. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/projects/create.py +0 -0
  114. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/projects/list.py +0 -0
  115. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/projects/retrieve.py +0 -0
  116. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/quickstart.py +0 -0
  117. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/simulations/run_sync.py +0 -0
  118. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/simulations/wait.py +0 -0
  119. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/sweeps/component_axis_grid.py +0 -0
  120. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/sweeps/run_and_poll.py +0 -0
  121. {thrustlab-0.6.0 → thrustlab-0.7.0}/examples/webhooks/verify_handler.py +0 -0
  122. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/__init__.py +0 -0
  123. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/conftest.py +0 -0
  124. {thrustlab-0.6.0/tests/test_resources → thrustlab-0.7.0/tests/estimate}/__init__.py +0 -0
  125. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/__init__.py +0 -0
  126. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/ardupilot_parser.py +0 -0
  127. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/betaflight_fc.py +0 -0
  128. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/conftest.py +0 -0
  129. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/mock_fmu.py +0 -0
  130. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_betaflight_bridge.py +0 -0
  131. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_betaflight_protocol.py +0 -0
  132. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_kinematics.py +0 -0
  133. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_protocol.py +0 -0
  134. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_rigidbody.py +0 -0
  135. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_state_machine.py +0 -0
  136. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_step_path.py +0 -0
  137. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_tilt_wing.py +0 -0
  138. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/sitl/test_vehicle.py +0 -0
  139. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_async_polling.py +0 -0
  140. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_client.py +0 -0
  141. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_components_create.py +0 -0
  142. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_components_find.py +0 -0
  143. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_dynamic_resource.py +0 -0
  144. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_exceptions.py +0 -0
  145. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_http.py +0 -0
  146. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_pagination.py +0 -0
  147. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_components.py +0 -0
  148. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_compute_units.py +0 -0
  149. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_fmu.py +0 -0
  150. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_projects.py +0 -0
  151. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_simulations.py +0 -0
  152. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_simulations_cancel_batch.py +0 -0
  153. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_simulations_run_batch.py +0 -0
  154. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_starred_components.py +0 -0
  155. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_submissions.py +0 -0
  156. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_sweeps.py +0 -0
  157. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_users.py +0 -0
  158. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_resources/test_webhook_endpoints.py +0 -0
  159. {thrustlab-0.6.0 → thrustlab-0.7.0}/tests/test_webhooks.py +0 -0
  160. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/__init__.py +0 -0
  161. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_async_polling.py +0 -0
  162. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_http.py +0 -0
  163. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/airfoil_breakpoint.py +0 -0
  164. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/airfoil_list_response.py +0 -0
  165. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/airfoil_resource.py +0 -0
  166. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_request.py +0 -0
  167. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_request_mode.py +0 -0
  168. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_request_target_type_type_0.py +0 -0
  169. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_response.py +0 -0
  170. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_response_envelope_limiting_factor_type_0.py +0 -0
  171. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_response_envelope_status_type_0.py +0 -0
  172. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_station_row.py +0 -0
  173. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/body_import_airfoil_v1_airfoils_import_post.py +0 -0
  174. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/calibrate_accepted_response.py +0 -0
  175. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/cancel_batch_result.py +0 -0
  176. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/cancel_selected_body.py +0 -0
  177. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/cancel_skipped.py +0 -0
  178. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/cancel_skipped_reason.py +0 -0
  179. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_brand.py +0 -0
  180. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_brands_response.py +0 -0
  181. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_create_body.py +0 -0
  182. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_create_body_spec_json.py +0 -0
  183. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_filter_bounds.py +0 -0
  184. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_ids_response.py +0 -0
  185. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_ids_response_filters.py +0 -0
  186. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_list_response.py +0 -0
  187. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_list_response_filters.py +0 -0
  188. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_patch.py +0 -0
  189. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_patch_spec_json_type_0.py +0 -0
  190. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_resource.py +0 -0
  191. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_resource_spec_json.py +0 -0
  192. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_specs_response.py +0 -0
  193. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_specs_response_spec_json.py +0 -0
  194. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_sweep_axis_in.py +0 -0
  195. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_sweep_axis_in_axis.py +0 -0
  196. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_write_response.py +0 -0
  197. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_write_response_spec_json.py +0 -0
  198. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/component_write_warning.py +0 -0
  199. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/concurrency_resource.py +0 -0
  200. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post.py +0 -0
  201. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_balance_resource.py +0 -0
  202. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_bucket_breakdown.py +0 -0
  203. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_bucket_breakdown_type.py +0 -0
  204. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_event_resource.py +0 -0
  205. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_event_resource_type.py +0 -0
  206. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_event_resource_unit_type.py +0 -0
  207. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_list_response.py +0 -0
  208. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_related_resource.py +0 -0
  209. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_related_resource_object.py +0 -0
  210. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_summary_resource.py +0 -0
  211. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_summary_resource_window.py +0 -0
  212. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/design_request.py +0 -0
  213. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/design_request_target_mode.py +0 -0
  214. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_estimate_body.py +0 -0
  215. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_estimate_resource.py +0 -0
  216. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_list_response.py +0 -0
  217. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_battery_topology_type_0.py +0 -0
  218. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_display_labels_type_0.py +0 -0
  219. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_error_type_0.py +0 -0
  220. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_input_snapshot_type_0.py +0 -0
  221. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_progress_type_0.py +0 -0
  222. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_result_type_0.py +0 -0
  223. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_status.py +0 -0
  224. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in_esc_timing.py +0 -0
  225. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in_esc_type.py +0 -0
  226. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in_motor_cooling_source.py +0 -0
  227. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_esc_timing.py +0 -0
  228. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_esc_type.py +0 -0
  229. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_motor_cooling_source.py +0 -0
  230. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_rotation_sense.py +0 -0
  231. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/export_geometry_request.py +0 -0
  232. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/export_geometry_request_format.py +0 -0
  233. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/export_geometry_request_rotation_type_0.py +0 -0
  234. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post.py +0 -0
  235. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/fmu_export_accepted_response.py +0 -0
  236. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/fmu_export_job_error.py +0 -0
  237. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/fmu_export_status_response.py +0 -0
  238. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/generate_geometry_request.py +0 -0
  239. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/geometry_response.py +0 -0
  240. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/geometry_style_resource.py +0 -0
  241. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/geometry_styles_list_response.py +0 -0
  242. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get.py +0 -0
  243. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get.py +0 -0
  244. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/get_public_result_v1_public_results_slug_get_response_get_public_result_v1_public_results_slug_get.py +0 -0
  245. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/get_shared_result_v1_public_share_share_token_get_response_get_shared_result_v1_public_share_share_token_get.py +0 -0
  246. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get.py +0 -0
  247. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/http_validation_error.py +0 -0
  248. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get.py +0 -0
  249. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/list_events_v1_events_get_response_list_events_v1_events_get.py +0 -0
  250. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get.py +0 -0
  251. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/pack_leaf_in.py +0 -0
  252. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/pack_parallel_in.py +0 -0
  253. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/pack_series_in.py +0 -0
  254. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/pack_topology_in.py +0 -0
  255. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch.py +0 -0
  256. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/project_create_body.py +0 -0
  257. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/project_list_response.py +0 -0
  258. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/project_patch.py +0 -0
  259. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/project_resource.py +0 -0
  260. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post.py +0 -0
  261. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post.py +0 -0
  262. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in_esc_timing.py +0 -0
  263. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in_esc_type.py +0 -0
  264. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in_motor_cooling_source.py +0 -0
  265. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_esc_timing.py +0 -0
  266. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_esc_type.py +0 -0
  267. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_motor_cooling_source.py +0 -0
  268. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_rotation_sense.py +0 -0
  269. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/run_queued_v1v1_simulations_run_queued_post_response_run_queued_v1v1_simulations_run_queued_post.py +0 -0
  270. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/run_selected_body.py +0 -0
  271. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/run_selected_v1v1_simulations_run_selected_post_response_run_selected_v1v1_simulations_run_selected_post.py +0 -0
  272. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/schedule_in.py +0 -0
  273. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/schedule_in_interpolation_type_0.py +0 -0
  274. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/schedule_in_mode.py +0 -0
  275. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_group_command_in.py +0 -0
  276. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_group_command_in_throttle_ramp_type_0.py +0 -0
  277. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_group_command_in_tilt_ramp_type_0.py +0 -0
  278. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in.py +0 -0
  279. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_airspeed_ramp.py +0 -0
  280. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_per_group.py +0 -0
  281. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_throttle_ramp.py +0 -0
  282. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_vertical_speed_ramp.py +0 -0
  283. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/share_resource.py +0 -0
  284. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_cancel_body.py +0 -0
  285. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_cooling_source_type_0.py +0 -0
  286. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_flight_regime.py +0 -0
  287. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_inflow_mode.py +0 -0
  288. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_launch_intent.py +0 -0
  289. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_inputs.py +0 -0
  290. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_list_response.py +0 -0
  291. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_patch_plot_config_json_type_0_item.py +0 -0
  292. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_battery_topology_type_0.py +0 -0
  293. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_display_labels_type_0.py +0 -0
  294. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_error_type_0.py +0 -0
  295. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_input_snapshot_type_0.py +0 -0
  296. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_plot_config_json_type_0_item.py +0 -0
  297. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_result_type_0.py +0 -0
  298. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_result_type_0_additional_property.py +0 -0
  299. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_status.py +0 -0
  300. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/spline_data.py +0 -0
  301. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/starred_component_create_body.py +0 -0
  302. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/starred_component_list_response.py +0 -0
  303. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/starred_component_resource.py +0 -0
  304. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_create_body.py +0 -0
  305. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_create_body_data_json.py +0 -0
  306. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_list_response.py +0 -0
  307. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_patch.py +0 -0
  308. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_patch_data_json_type_0.py +0 -0
  309. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_resource.py +0 -0
  310. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/submission_resource_data_json.py +0 -0
  311. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_cancel_body.py +0 -0
  312. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_config_in_esc_timing_values_type_0_item.py +0 -0
  313. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_create_body_inflow_mode.py +0 -0
  314. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_create_body_launch_intent.py +0 -0
  315. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_credits_breakdown.py +0 -0
  316. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_inputs.py +0 -0
  317. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_list_response.py +0 -0
  318. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_param_range_in.py +0 -0
  319. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_param_range_in_mode.py +0 -0
  320. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_patch_plot_config_json_type_0_item.py +0 -0
  321. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_component_selection.py +0 -0
  322. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_list_response.py +0 -0
  323. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_list_response_display_labels_type_0.py +0 -0
  324. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_patch.py +0 -0
  325. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_resource_rotors_type_0.py +0 -0
  326. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_resource_rotors_type_0_additional_property.py +0 -0
  327. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_error_type_0.py +0 -0
  328. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_input_snapshot_type_0.py +0 -0
  329. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_plot_config_json_type_0_item.py +0 -0
  330. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_status.py +0 -0
  331. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_summary_type_0.py +0 -0
  332. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_sweep_config_type_0.py +0 -0
  333. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in_esc_timing.py +0 -0
  334. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in_esc_type.py +0 -0
  335. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in_motor_cooling_source.py +0 -0
  336. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_esc_timing.py +0 -0
  337. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_esc_type.py +0 -0
  338. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_motor_cooling_source.py +0 -0
  339. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_rotation_sense.py +0 -0
  340. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/termination_in.py +0 -0
  341. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/termination_in_mode.py +0 -0
  342. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/types.py +0 -0
  343. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/user_resource_gate_required_tiers.py +0 -0
  344. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/user_resource_unit_system.py +0 -0
  345. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/v1_custom_component_override.py +0 -0
  346. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/v1_custom_component_override_spec_json.py +0 -0
  347. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/v1_health_v1_health_get_response_v1_health_v1_health_get.py +0 -0
  348. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/validation_error.py +0 -0
  349. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/webhook_endpoint_create.py +0 -0
  350. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_models/webhook_endpoint_patch.py +0 -0
  351. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/_pagination.py +0 -0
  352. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/client.py +0 -0
  353. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/exceptions.py +0 -0
  354. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/__init__.py +0 -0
  355. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/_base.py +0 -0
  356. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/components.py +0 -0
  357. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/compute_units.py +0 -0
  358. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/dynamic_simulations.py +0 -0
  359. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/fmu.py +0 -0
  360. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/projects.py +0 -0
  361. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/simulations.py +0 -0
  362. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/starred_components.py +0 -0
  363. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/submissions.py +0 -0
  364. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/sweeps.py +0 -0
  365. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/users.py +0 -0
  366. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/resources/webhook_endpoints.py +0 -0
  367. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/README.md +0 -0
  368. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/__init__.py +0 -0
  369. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/__main__.py +0 -0
  370. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/bridge.py +0 -0
  371. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/cli.py +0 -0
  372. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/fmu.py +0 -0
  373. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/kinematics.py +0 -0
  374. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/model.py +0 -0
  375. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/protocol.py +0 -0
  376. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/rigidbody.py +0 -0
  377. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/rpmlog.py +0 -0
  378. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/__init__.py +0 -0
  379. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/ardupilot.py +0 -0
  380. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/base.py +0 -0
  381. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/betaflight.py +0 -0
  382. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/sitl/vehicle.py +0 -0
  383. {thrustlab-0.6.0 → thrustlab-0.7.0}/thrustlab/webhooks.py +0 -0
@@ -6,7 +6,92 @@ ThrustLab API version (which is permanently `/v1/`).
6
6
 
7
7
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
8
8
 
9
- ## [Unreleased]
9
+ ## [0.7.0] - 2026-10-02
10
+
11
+ ### Added
12
+
13
+ - `thrustlab.estimate`: the ThrustLab propeller equation, offline. Thrust, torque,
14
+ shaft power and the coefficients from a propeller's printed size, blade count,
15
+ rpm and airspeed, with an ISA atmosphere and a zero-thrust airspeed search. No API
16
+ key, no network, no dependencies. `thrustlab-estimate --diameter 10 --pitch 5
17
+ --rpm 8000` on the command line (`--airspeed 0:30:5` for a table, `--json`).
18
+ This is the closed form of the Fast propeller model in the simulator; the
19
+ simulator adds the motor, ESC, battery and thermal coupling.
20
+ - The **Fast propeller model**. `propeller_model="fast"` on a single-point,
21
+ sweep or batch submission runs from a propeller's printed label rather than
22
+ its measured blade geometry: `PropellerLabel` carries `diameter_m`,
23
+ `pitch_m`, `num_blades`, a planform `family` (and `chord_ratio` for
24
+ `custom`) and `camber_pct`. Send it on a rotor whose propeller you typed;
25
+ send `propeller_component_id` alone for a catalog pick and the server derives
26
+ the label from that row. `propeller_model="full"` (the measured blade) is
27
+ still the default, so an existing call is byte-for-byte unchanged.
28
+ - **Anonymous runs.** `POST /v1/public/simulate` needs no credentials at all —
29
+ it answers synchronously on the Fast model with a `run_…` token, a `/s/<token>`
30
+ link and the result — and `POST /v1/runs/{token}/claim` imports one into your
31
+ account's default project. Generated models for both, plus the public read
32
+ `GET /v1/public/runs/{token}`.
33
+ - **The open catalog**, unauthenticated: `GET /v1/public/motors`,
34
+ `/v1/public/batteries` and the per-component `…/{id}` reads. Each list is
35
+ paged with a `starting_after` cursor and states the data licence in a
36
+ `license` field. There is no bulk export.
37
+ - The five public routes now declare real response models, so
38
+ `PublicComponentList`, `PublicComponentResource`, `PublicSimulateResponse`,
39
+ `PublicRunView` and `PublicRunClaim` are generated types rather than untyped
40
+ objects. `PublicRunView` is deliberately open: its body is the same
41
+ projection the share route returns and it grows with the result shape.
42
+ - `default_project_id`, `full_model_daily` and `full_model_used_today` on
43
+ `client.users.me()`.
44
+
45
+ ### Changed
46
+
47
+ - **Every datasheet value is now public on every tier.** Motor `n`,
48
+ `iq_nominal`, `R`, `L` and `inertia` and battery `pack_resistance_mOhm` and
49
+ `internal_resistance_mOhm` come back from the component list, the detail read
50
+ and `/specs` alike. `/specs` no longer requires a paid plan, a Turnstile
51
+ challenge or a per-account budget, and filter bounds are arbitrary rather than
52
+ snapped to preset edges. A propeller's station arrays are still never served.
53
+ - **`project_id` is optional** on the single-point, sweep and dynamic create
54
+ bodies. Omit it and the run lands in your account's default project ("My
55
+ simulations"), which is created on first use. `PATCH` accepts `project_id` on
56
+ all three resources, so a run can be moved after the fact.
57
+ - The free tier's limit is no longer a daily simulation cap. Fast-model runs are
58
+ unlimited; `full_model_daily` counts Full-model JOBS per rolling 24 h, one per
59
+ run whether it is a single point or a bounded sweep.
60
+
61
+ ### Breaking (import names only)
62
+
63
+ The wire contract is unchanged — every request and response body is the same
64
+ shape 0.6.0 sent and received. What moved is the NAME of some generated model
65
+ classes, because the OpenAPI schemas they are derived from changed. Only code
66
+ that imports these by name from `thrustlab` breaks, and only at import time.
67
+
68
+ - `SweepCreateBodyCoolingSource` → `SweepCreateBodyCoolingSourceType0`.
69
+ `cooling_source` on a sweep is nullable now (null means "resolve from the
70
+ deprecated `flight_regime`", matching the single-point body), and the
71
+ generator names the enum arm of a nullable union `…Type0`. The accepted
72
+ values are identical.
73
+ - `PublicSimulateBodyInputSnapshotType0` is **gone**. `input_snapshot` is a
74
+ first-party field and is no longer published in the contract, so no model is
75
+ generated for it. The endpoint still accepts it.
76
+ - Four inline response models are **gone**, replaced by the named models listed
77
+ under Added above:
78
+ `CreatePublicSimulationV1PublicSimulatePostResponseCreatePublicSimulationV1PublicSimulatePost`
79
+ → `PublicSimulateResponse`;
80
+ `ReadPublicRunV1PublicRunsTokenGetResponseReadPublicRunV1PublicRunsTokenGet`
81
+ → `PublicRunView`;
82
+ `ClaimPublicRunV1RunsTokenClaimPostResponseClaimPublicRunV1RunsTokenClaimPost`
83
+ → `PublicRunClaim`;
84
+ `ListPublicMotorsV1PublicMotorsGetResponseListPublicMotorsV1PublicMotorsGet`
85
+ and its batteries twin → `PublicComponentList`.
86
+ These existed for one unreleased day and named nothing a caller would have
87
+ typed on purpose, but they were importable, so they are listed.
88
+
89
+ ### Deprecated
90
+
91
+ - `entitlements.db_access` and `entitlements.component_specs` on
92
+ `client.users.me()` are frozen at `"full"` and `True`, and `daily_cap` at
93
+ `null`. They are kept for one major so a pinned 0.6.x client keeps validating;
94
+ read `full_model_daily` instead of `daily_cap`.
10
95
 
11
96
  ## [0.6.0] - 2026-09-09
12
97
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thrustlab
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Official Python SDK for the ThrustLab API
5
5
  Project-URL: Homepage, https://thrustlab.com
6
6
  Project-URL: Documentation, https://thrustlab.com/docs
@@ -120,6 +120,26 @@ See [`examples/fmu/`](./examples/fmu/) for bundled ready-to-fly FMUs with
120
120
  quadcopter, Y6 coax, and tilt-tricopter missions, an endurance study, and
121
121
  replay rendering.
122
122
 
123
+ ## Offline propeller estimate
124
+
125
+ `thrustlab.estimate` is the ThrustLab propeller equation: thrust, torque and
126
+ power from the printed size, with no account and no network.
127
+
128
+ ```python
129
+ from thrustlab.estimate import from_label
130
+
131
+ point = from_label(diameter_in=10, pitch_in=5, rpm=8000, airspeed_mps=12.0)
132
+ print(point.T, point.Q, point.P_shaft) # N, N m, W
133
+ ```
134
+
135
+ ```shell
136
+ thrustlab-estimate --diameter 10 --pitch 5 --rpm 8000 --airspeed 0:30:5
137
+ ```
138
+
139
+ It is the closed form of the simulator's Fast propeller model. The simulator adds
140
+ the motor, ESC, battery and thermal coupling; the Full model adds the measured
141
+ blade geometry.
142
+
123
143
  ## Why the SDK (and not `urllib` / `wget`)?
124
144
 
125
145
  The SDK gives you automatic retries with backoff, idempotency keys, cursor
@@ -84,6 +84,26 @@ See [`examples/fmu/`](./examples/fmu/) for bundled ready-to-fly FMUs with
84
84
  quadcopter, Y6 coax, and tilt-tricopter missions, an endurance study, and
85
85
  replay rendering.
86
86
 
87
+ ## Offline propeller estimate
88
+
89
+ `thrustlab.estimate` is the ThrustLab propeller equation: thrust, torque and
90
+ power from the printed size, with no account and no network.
91
+
92
+ ```python
93
+ from thrustlab.estimate import from_label
94
+
95
+ point = from_label(diameter_in=10, pitch_in=5, rpm=8000, airspeed_mps=12.0)
96
+ print(point.T, point.Q, point.P_shaft) # N, N m, W
97
+ ```
98
+
99
+ ```shell
100
+ thrustlab-estimate --diameter 10 --pitch 5 --rpm 8000 --airspeed 0:30:5
101
+ ```
102
+
103
+ It is the closed form of the simulator's Fast propeller model. The simulator adds
104
+ the motor, ESC, battery and thermal coupling; the Full model adds the measured
105
+ blade geometry.
106
+
87
107
  ## Why the SDK (and not `urllib` / `wget`)?
88
108
 
89
109
  The SDK gives you automatic retries with backoff, idempotency keys, cursor
@@ -49,6 +49,7 @@ sitl = []
49
49
 
50
50
  [project.scripts]
51
51
  thrustlab-sitl = "thrustlab.sitl.cli:main"
52
+ thrustlab-estimate = "thrustlab.estimate.cli:main"
52
53
 
53
54
  [project.urls]
54
55
  Homepage = "https://thrustlab.com"
@@ -61,11 +62,17 @@ path = "thrustlab/_version.py"
61
62
 
62
63
  [tool.hatch.build.targets.wheel]
63
64
  packages = ["thrustlab"]
65
+ # The committed worked-example fixture is data, not code. `artifacts` names it
66
+ # explicitly so neither a VCS-ignore rule nor a future `exclude` can drop it from the
67
+ # wheel: `thrustlab.estimate` reads it at runtime and the Rust Fast-model port is gated
68
+ # against it.
69
+ artifacts = ["thrustlab/estimate/_fixture/*.json"]
64
70
 
65
71
  [tool.hatch.build.targets.sdist]
66
72
  # The bundled example FMUs live in the repo for out-of-the-box examples; the
67
73
  # sdist stays source-only (each is 4-10 MB of binary).
68
74
  exclude = ["*.fmu"]
75
+ artifacts = ["thrustlab/estimate/_fixture/*.json"]
69
76
  include = [
70
77
  "/thrustlab",
71
78
  "/tests",
@@ -0,0 +1,234 @@
1
+ """Regression and invariant tests for ``thrustlab.estimate`` (the label-only propeller
2
+ thrust and torque equation). Pure Python plus numpy for the quadrature oracle."""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import math
7
+
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from thrustlab import estimate as te
12
+ from thrustlab.estimate import cli
13
+
14
+ IN = 0.0254
15
+ PI = math.pi
16
+ APC_10x5 = dict(D=10 * IN, P=5 * IN, B=2, c=0.064 * 10 * IN, n=8000 / 60)
17
+
18
+
19
+ # ----------------------------------------------------------------------------- pinned values
20
+
21
+
22
+ def test_worked_examples_pinned():
23
+ """The four worked examples in the design spec, thrust in N and shaft power in W."""
24
+ cases = [
25
+ (dict(diameter_in=10, pitch_in=5, rpm=8000, chord_ratio=0.064), 8.10, 95.5),
26
+ (dict(diameter_in=10, pitch_in=5, rpm=8000, chord_ratio=0.064, airspeed_mps=15.0), 2.69, 59.4),
27
+ (dict(diameter_in=5, pitch_in=4.5, rpm=20000, blades=3, chord_ratio=0.11), 8.92, 245.4),
28
+ (dict(diameter_in=20, pitch_in=10, rpm=4000, chord_ratio=0.064), 32.87, 373.4),
29
+ ]
30
+ for kw, T, P in cases:
31
+ r = te.from_label(**kw, camber_pct=3.5 / 1.1)
32
+ assert r.converged
33
+ assert r.T == pytest.approx(T, rel=2e-3)
34
+ assert r.P_shaft == pytest.approx(P, rel=2e-3)
35
+ labels = [label for label, _, _ in te.worked_examples()]
36
+ assert labels[0].startswith("APC 10x5E static")
37
+
38
+
39
+ # ----------------------------------------------------------------------------- the equation itself
40
+
41
+
42
+ def _oracle(r, D, P, B, c, kw):
43
+ """Integrate the section law numerically at the returned state; return (ct_r, profile cq_r)."""
44
+ sec = kw.get("section", te.DEFAULT_SECTION)
45
+ xh = min(max(kw.get("x_hub", 0.15), 0.0), 0.5)
46
+ sigma, theta = B * c / (PI * D / 2), P / (PI * D)
47
+ a_eff = sec.a / math.sqrt(1 - min(r.mach, te.MACH_CORR_CAP) ** 2)
48
+ g = theta - r.lam
49
+ clmax, cd0 = r.CLmax, sec.cd0(r.Re)
50
+ alpha_stall = sec.alpha0 + clmax / a_eff
51
+ x = np.linspace(max(xh, 1e-6), 1.0, 400_001)
52
+ cl = np.minimum(a_eff * (g / x - sec.alpha0), clmax)
53
+ cd = cd0 + sec.k * (cl - sec.cl_d) ** 2 + sec.cd_stall * np.maximum(g / x - alpha_stall, 0.0)
54
+ lift = np.where(x <= r.Bt, cl * x ** 2, 0.0)
55
+ ct_r = 0.5 * sigma * np.trapezoid(lift, x)
56
+ prof = 0.5 * sigma * np.trapezoid(cd * x ** 3, x)
57
+ return ct_r, prof
58
+
59
+
60
+ @pytest.mark.parametrize("case", [
61
+ dict(APC_10x5), # ordinary static, partial stall
62
+ dict(APC_10x5, V=15.0), # cruise, nearly attached
63
+ dict(APC_10x5, V=19.0), # close to zero thrust
64
+ dict(D=10 * IN, P=10 * IN, B=1, c=0.064 * 10 * IN, n=8000 / 60), # single blade, fully stalled past B_t
65
+ dict(D=5 * IN, P=4.5 * IN, B=4, c=0.11 * 5 * IN, n=20000 / 60), # high solidity
66
+ dict(APC_10x5, x_hub=0.0), # no hub cut-out
67
+ dict(APC_10x5, section=te.Section.from_camber(5.0)), # more camber
68
+ ])
69
+ def test_closed_form_matches_quadrature_and_momentum(case):
70
+ r = te.propeller_performance(**case)
71
+ assert r.converged and abs(r.residual) < 1e-9
72
+ f = (1 - r.a_prime) ** 2
73
+ ct_r = r.CT / f / (PI ** 3 / 4)
74
+ cq_r = r.CP / f / (PI ** 4 / 4)
75
+ lam_c = r.lam - r.lam_i
76
+ assert 2 * r.lam_i * (lam_c + r.lam_i) == pytest.approx(ct_r, rel=1e-7)
77
+ ct_oracle, prof_oracle = _oracle(r, case["D"], case["P"], case["B"], case["c"], case)
78
+ assert ct_r == pytest.approx(ct_oracle, rel=1e-5)
79
+ prof_code = cq_r - lam_c * ct_r - 1.10 * r.lam_i * ct_r
80
+ assert prof_code == pytest.approx(prof_oracle, rel=1e-5)
81
+
82
+
83
+ def test_thrust_falls_with_airspeed_and_crosses_zero_above_pitch_speed():
84
+ pts = te.airspeed_sweep(**APC_10x5, speeds=[0, 5, 10, 15, 20])
85
+ thrusts = [p.T for p in pts]
86
+ assert thrusts == sorted(thrusts, reverse=True)
87
+ V0 = te.zero_thrust_speed(**APC_10x5)
88
+ pitch_speed = APC_10x5["n"] * APC_10x5["P"]
89
+ assert 1.05 * pitch_speed < V0 < 1.35 * pitch_speed
90
+ assert abs(te.propeller_performance(**APC_10x5, V=V0).T) < 1e-3
91
+
92
+
93
+ def test_zero_thrust_speed_low_pitch_is_bracketed():
94
+ """Camber adds an aerodynamic pitch offset, so a near-zero label pitch still has a
95
+ zero-thrust speed well above 2 n P; the search must expand until it brackets it."""
96
+ kw = dict(D=10 * IN, P=0.01, B=2, c=0.064 * 10 * IN, n=8000 / 60)
97
+ V0 = te.zero_thrust_speed(**kw)
98
+ assert V0 > 2 * kw["n"] * kw["P"] + 1
99
+ assert abs(te.propeller_performance(**kw, V=V0).T) < 1e-3
100
+
101
+
102
+ def test_brake_regime_is_flagged_not_faked():
103
+ r = te.propeller_performance(**APC_10x5, V=30.0)
104
+ assert r.T < 0 and r.regime == "brake"
105
+
106
+
107
+ def test_more_blades_more_thrust_sublinear():
108
+ t2 = te.propeller_performance(**APC_10x5).T
109
+ t3 = te.propeller_performance(**{**APC_10x5, "B": 3}).T
110
+ t4 = te.propeller_performance(**{**APC_10x5, "B": 4}).T
111
+ assert 1.2 < t3 / t2 < 1.6
112
+ assert 1.4 < t4 / t2 < 2.0
113
+ assert t4 / t3 < t3 / t2
114
+
115
+
116
+ def test_density_scaling_exact_and_torque_identities():
117
+ r1 = te.propeller_performance(**APC_10x5)
118
+ r2 = te.propeller_performance(**APC_10x5, rho=0.9 * 1.225)
119
+ assert r2.T == pytest.approx(0.9 * r1.T, rel=1e-9)
120
+ assert r2.CT == pytest.approx(r1.CT, rel=1e-9)
121
+ assert r1.Q == pytest.approx(r1.P_shaft / (2 * PI * APC_10x5["n"]), rel=1e-12)
122
+ assert r1.CQ == pytest.approx(r1.CP / (2 * PI), rel=1e-12)
123
+
124
+
125
+ def test_similarity_scaling_with_matched_reynolds_and_mach():
126
+ """Double D, P, c, halve n, double nu: same Re and Mach, so CT and CP are unchanged,
127
+ thrust scales by 4 and torque by 8."""
128
+ r1 = te.propeller_performance(**APC_10x5)
129
+ r2 = te.propeller_performance(D=2 * APC_10x5["D"], P=2 * APC_10x5["P"], B=2, c=2 * APC_10x5["c"],
130
+ n=APC_10x5["n"] / 2, nu=2 * 1.46e-5)
131
+ assert r2.Re == pytest.approx(r1.Re, rel=1e-9) and r2.mach == pytest.approx(r1.mach, rel=1e-9)
132
+ assert r2.CT == pytest.approx(r1.CT, rel=1e-8) and r2.CP == pytest.approx(r1.CP, rel=1e-8)
133
+ assert r2.T == pytest.approx(4 * r1.T, rel=1e-8) and r2.Q == pytest.approx(8 * r1.Q, rel=1e-8)
134
+
135
+
136
+ def test_swirl_converges_and_state_is_consistent_at_high_solidity():
137
+ """Six wide blades (solidity 0.5) at high pitch and airspeed: the swirl fixed point
138
+ must converge and the returned inflow must be consistent with the returned swirl."""
139
+ r = te.propeller_performance(D=10 * IN, P=20 * IN, B=6, c=1.3 * IN, n=8000 / 60, V=60.0)
140
+ assert r.converged and r.swirl_passes < 30
141
+ lam_c_from_swirl = 60.0 / (2 * PI * (8000 / 60) * (1 - r.a_prime) * 5 * IN)
142
+ assert r.lam - r.lam_i == pytest.approx(lam_c_from_swirl, rel=1e-5)
143
+
144
+
145
+ def test_absurd_solidity_is_rejected_not_solved():
146
+ with pytest.raises(ValueError):
147
+ te.propeller_performance(D=0.254, P=2.0, B=100, c=1.0, n=8000 / 60)
148
+ with pytest.raises(ValueError):
149
+ te.propeller_performance(D=0.254, P=0.508, B=8, c=0.127, n=8000 / 60, V=67.7)
150
+
151
+
152
+ def test_camber_raises_thrust_and_moves_zero_thrust_speed():
153
+ lo = te.propeller_performance(**APC_10x5, section=te.Section.from_camber(2.0))
154
+ hi = te.propeller_performance(**APC_10x5, section=te.Section.from_camber(5.0))
155
+ assert hi.T > lo.T
156
+ assert te.zero_thrust_speed(**APC_10x5, section=te.Section.from_camber(5.0)) > te.zero_thrust_speed(
157
+ **APC_10x5, section=te.Section.from_camber(2.0))
158
+
159
+
160
+ def test_section_law_and_guards():
161
+ s = te.Section()
162
+ assert s.clmax(1e5) == pytest.approx(1.30)
163
+ assert s.clmax(5e4) == pytest.approx(1.30 * 0.5 ** 0.15)
164
+ assert s.clmax(1e7) == 1.45 and s.clmax(100) == 0.6
165
+ assert s.cd0(1e5) == pytest.approx(0.019)
166
+ with pytest.raises(ValueError): # CL_max below the zero-angle lift is not a section
167
+ te.propeller_performance(**APC_10x5, section=te.Section(clmax_ref=0.3, clmax_cap=0.3, clmax_re_exp=0.0))
168
+
169
+
170
+ def test_mach_is_reported_unclipped():
171
+ r = te.from_label(10, 5, 40000)
172
+ assert r.mach > 1.0
173
+
174
+
175
+ # ----------------------------------------------------------------------------- helpers and API
176
+
177
+
178
+ def test_air_density_isa_layers():
179
+ rho0, nu0 = te.air_density(0.0)
180
+ rho3, nu3 = te.air_density(3000.0)
181
+ assert rho0 == pytest.approx(1.225, rel=2e-3)
182
+ assert nu0 == pytest.approx(1.46e-5, rel=3e-2)
183
+ assert rho3 / rho0 == pytest.approx(0.742, rel=5e-3) and nu3 > nu0
184
+ lo, hi = te.air_density(10999.0)[0], te.air_density(11001.0)[0]
185
+ assert lo == pytest.approx(hi, rel=2e-3)
186
+ assert te.air_density(15000.0)[0] == pytest.approx(0.1937, rel=2e-3)
187
+ assert te.air_density(0.0, temperature_c=35.0)[0] < rho0
188
+ for bad in ((25000.0, None), (0.0, -300.0)):
189
+ with pytest.raises(ValueError):
190
+ te.air_density(*bad)
191
+
192
+
193
+ def test_from_label_matches_si_api_and_forwards_section_and_atmosphere():
194
+ sec = te.Section.from_camber(4.0)
195
+ a = te.from_label(10, 5, 8000, chord_in=0.64, section=sec, rho=1.1, nu=1.5e-5, airspeed_mps=3.0)
196
+ b = te.propeller_performance(10 * IN, 5 * IN, 2, 0.64 * IN, 8000 / 60, 3.0, rho=1.1, nu=1.5e-5, section=sec)
197
+ assert a == b
198
+ with pytest.raises(ValueError):
199
+ te.from_label(10, 5, 8000, chord_in=0.0)
200
+
201
+
202
+ def test_bad_inputs_raise():
203
+ bad = [
204
+ dict(D=0.0), dict(P=-1.0), dict(c=float("nan")), dict(n=float("inf")), dict(B=0), dict(B=2.5),
205
+ dict(V=-1.0), dict(V=float("nan")), dict(rho=0.0), dict(rho=-1.0), dict(nu=0.0), dict(nu=-1e-5),
206
+ ]
207
+ for kw in bad:
208
+ with pytest.raises(ValueError):
209
+ te.propeller_performance(**{**APC_10x5, **kw})
210
+
211
+
212
+ # ----------------------------------------------------------------------------- command line
213
+
214
+
215
+ def test_cli_table_and_json(capsys):
216
+ assert cli.main(["--diameter", "10", "--pitch", "5", "--rpm", "8000", "--chord-ratio", "0.064",
217
+ "--camber", str(3.5 / 1.1)]) == 0
218
+ out = capsys.readouterr().out
219
+ assert "8.10" in out and "zero thrust at" in out
220
+
221
+ assert cli.main(["--diameter", "10", "--pitch", "5", "--rpm", "8000", "--airspeed", "0:10:5", "--json"]) == 0
222
+ payload = json.loads(capsys.readouterr().out)
223
+ assert [p["airspeed_mps"] for p in payload["points"]] == [0.0, 5.0, 10.0]
224
+ assert payload["points"][0]["T"] > payload["points"][2]["T"]
225
+ assert payload["zero_thrust_airspeed_mps"] > 10.0
226
+
227
+
228
+ def test_cli_examples_and_errors(capsys):
229
+ assert cli.main(["--examples", "--json"]) == 0
230
+ rows = json.loads(capsys.readouterr().out)
231
+ assert len(rows) == 4 and rows[0]["T"] == pytest.approx(8.10, rel=2e-3)
232
+ assert cli.main(["--diameter", "10", "--pitch", "5", "--rpm", "8000", "--chord-in", "0"]) == 2
233
+ with pytest.raises(SystemExit):
234
+ cli.main(["--diameter", "10"])