thrustlab 0.5.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.
- {thrustlab-0.5.0 → thrustlab-0.7.0}/CHANGELOG.md +122 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/PKG-INFO +49 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/README.md +48 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/components/create_component.py +2 -2
- thrustlab-0.7.0/examples/dynamic/watch_and_cancel.py +132 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/README.md +10 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/README.md +10 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/README.md +21 -8
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/README.md +10 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/endurance/README.md +10 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/gazebo/README.md +10 -1
- thrustlab-0.7.0/examples/fmu/simulink/.gitignore +2 -0
- thrustlab-0.7.0/examples/fmu/simulink/README.md +53 -0
- thrustlab-0.7.0/examples/fmu/simulink/fmu_cosim.png +0 -0
- thrustlab-0.7.0/examples/fmu/simulink/run_fmu_simulink.m +93 -0
- thrustlab-0.7.0/examples/sweeps/component_axis_grid.py +85 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/pyproject.toml +10 -0
- thrustlab-0.7.0/tests/estimate/test_equation.py +234 -0
- thrustlab-0.7.0/tests/estimate/test_grid.py +663 -0
- thrustlab-0.7.0/tests/test_resources/__init__.py +0 -0
- thrustlab-0.7.0/tests/test_resources/test_simulations_cancel_batch.py +62 -0
- thrustlab-0.7.0/tests/test_resources/test_simulations_run_batch.py +80 -0
- thrustlab-0.7.0/tests/test_spec_coverage.py +217 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/__init__.py +94 -6
- thrustlab-0.7.0/thrustlab/_models/cancel_batch_result.py +97 -0
- thrustlab-0.7.0/thrustlab/_models/cancel_selected_body.py +42 -0
- thrustlab-0.7.0/thrustlab/_models/cancel_skipped.py +71 -0
- thrustlab-0.7.0/thrustlab/_models/cancel_skipped_reason.py +10 -0
- thrustlab-0.7.0/thrustlab/_models/compare_entry.py +74 -0
- thrustlab-0.7.0/thrustlab/_models/compare_entry_in.py +89 -0
- thrustlab-0.7.0/thrustlab/_models/compare_lists_body.py +67 -0
- thrustlab-0.7.0/thrustlab/_models/compare_lists_in.py +177 -0
- thrustlab-0.7.0/thrustlab/_models/compare_lists_out.py +108 -0
- thrustlab-0.7.0/thrustlab/_models/compare_lists_resource.py +78 -0
- thrustlab-0.7.0/thrustlab/_models/component_filter_bounds.py +97 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_ids_response.py +27 -3
- thrustlab-0.7.0/thrustlab/_models/component_ids_response_filters.py +63 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_list_response.py +19 -0
- thrustlab-0.7.0/thrustlab/_models/component_list_response_filters.py +63 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_sweep_axis_in.py +1 -1
- thrustlab-0.7.0/thrustlab/_models/concurrency_resource.py +108 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_balance_resource.py +1 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_event_resource.py +1 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_summary_resource.py +1 -1
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_create_body.py +40 -8
- thrustlab-0.7.0/thrustlab/_models/dynamic_create_body_propeller_model.py +9 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_patch.py +21 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource.py +87 -4
- thrustlab-0.7.0/thrustlab/_models/dynamic_resource_progress_type_0.py +47 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in.py +66 -9
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in.py +66 -9
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/entitlements_resource.py +28 -4
- thrustlab-0.7.0/thrustlab/_models/propeller_label.py +167 -0
- thrustlab-0.7.0/thrustlab/_models/propeller_label_family.py +12 -0
- thrustlab-0.7.0/thrustlab/_models/public_catalog_redirect.py +79 -0
- thrustlab-0.7.0/thrustlab/_models/public_component.py +137 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_facets.py +120 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_facets_discrete.py +68 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_facets_ranges.py +60 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_facets_type.py +10 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_list.py +128 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_resource.py +145 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_resource_spec_json.py +50 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_resource_type.py +10 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_spec_json.py +50 -0
- thrustlab-0.7.0/thrustlab/_models/public_component_type.py +10 -0
- thrustlab-0.7.0/thrustlab/_models/public_facet_range.py +97 -0
- thrustlab-0.7.0/thrustlab/_models/public_facet_value.py +70 -0
- thrustlab-0.7.0/thrustlab/_models/public_run_claim.py +93 -0
- thrustlab-0.7.0/thrustlab/_models/public_run_claim_kind.py +9 -0
- thrustlab-0.7.0/thrustlab/_models/public_run_view.py +88 -0
- thrustlab-0.7.0/thrustlab/_models/public_simulate_body.py +467 -0
- thrustlab-0.7.0/thrustlab/_models/public_simulate_body_cooling_source_type_0.py +11 -0
- thrustlab-0.7.0/thrustlab/_models/public_simulate_body_flight_regime.py +11 -0
- thrustlab-0.7.0/thrustlab/_models/public_simulate_body_inflow_mode.py +9 -0
- thrustlab-0.7.0/thrustlab/_models/public_simulate_body_propeller_model.py +9 -0
- thrustlab-0.7.0/thrustlab/_models/public_simulate_response.py +102 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in.py +59 -10
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_resource.py +61 -9
- thrustlab-0.7.0/thrustlab/_models/rotor_group_resource_propeller_label_type_0.py +47 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in.py +59 -10
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body.py +41 -8
- thrustlab-0.7.0/thrustlab/_models/simulation_create_body_propeller_model.py +9 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_patch.py +21 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource.py +76 -31
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_config_in.py +111 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_create_body.py +94 -24
- thrustlab-0.5.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
- thrustlab-0.7.0/thrustlab/_models/sweep_create_body_flight_regime.py +11 -0
- thrustlab-0.7.0/thrustlab/_models/sweep_create_body_propeller_model.py +9 -0
- thrustlab-0.7.0/thrustlab/_models/sweep_credits_breakdown.py +73 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_param_range_in.py +12 -6
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_patch.py +21 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_inputs.py +69 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_resource.py +5 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource.py +131 -31
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in.py +59 -10
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in.py +59 -10
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/user_resource.py +60 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_version.py +1 -1
- thrustlab-0.7.0/thrustlab/estimate/__init__.py +368 -0
- thrustlab-0.7.0/thrustlab/estimate/__main__.py +3 -0
- thrustlab-0.7.0/thrustlab/estimate/_fixture/worked-examples.json +22823 -0
- thrustlab-0.7.0/thrustlab/estimate/cli.py +202 -0
- thrustlab-0.7.0/thrustlab/estimate/fixture.py +437 -0
- thrustlab-0.7.0/thrustlab/estimate/grid.py +1250 -0
- thrustlab-0.7.0/thrustlab/resources/simulations.py +186 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/sweeps.py +1 -1
- thrustlab-0.5.0/tests/test_spec_coverage.py +0 -117
- thrustlab-0.5.0/thrustlab/_models/list_public_propellers_v1_public_propellers_get_response_list_public_propellers_v1_public_propellers_get.py +0 -47
- thrustlab-0.5.0/thrustlab/resources/simulations.py +0 -87
- {thrustlab-0.5.0 → thrustlab-0.7.0}/.gitignore +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/LICENSE +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/NOTICE +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/dynamic/run_and_poll.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/fly_mission.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/fmu.parm +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/plot_mission.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/quad_x.yaml +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl/render_mission_video.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/fly_transition.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/tilttri.parm +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_tilttri/tilttri.yaml +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/plot_coax_split.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/y6.parm +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/ardupilot_sitl_y6/y6.yaml +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/betaflight_config.txt +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/betaflight_quad_x.yaml +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/betaflight_sitl/fly_hover.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/endurance/hover_endurance.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/export_and_download.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/gazebo/quad_fmu.sdf +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/fmu/quad_5inch_x.yaml +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/outputs/read_dynamic.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/outputs/read_steady_state.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/projects/create.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/projects/list.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/projects/retrieve.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/quickstart.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/simulations/run_sync.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/simulations/wait.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/sweeps/run_and_poll.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/examples/webhooks/verify_handler.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/conftest.py +0 -0
- {thrustlab-0.5.0/tests/test_resources → thrustlab-0.7.0/tests/estimate}/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/ardupilot_parser.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/betaflight_fc.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/conftest.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/mock_fmu.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_betaflight_bridge.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_betaflight_protocol.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_kinematics.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_protocol.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_rigidbody.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_state_machine.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_step_path.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_tilt_wing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/sitl/test_vehicle.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_async_polling.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_client.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_components_create.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_components_find.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_dynamic_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_exceptions.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_http.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_pagination.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_components.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_compute_units.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_fmu.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_projects.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_simulations.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_starred_components.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_submissions.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_sweeps.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_users.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_resources/test_webhook_endpoints.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/tests/test_webhooks.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_async_polling.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_http.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/airfoil_breakpoint.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/airfoil_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/airfoil_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_request.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_request_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_request_target_type_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_response_envelope_limiting_factor_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_geometry_response_envelope_status_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/analyze_station_row.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/body_import_airfoil_v1_airfoils_import_post.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/calibrate_accepted_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_brand.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_brands_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_create_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_create_body_spec_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_patch.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_patch_spec_json_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_resource_spec_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_specs_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_specs_response_spec_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_sweep_axis_in_axis.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_write_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_write_response_spec_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/component_write_warning.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_bucket_breakdown.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_bucket_breakdown_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_event_resource_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_event_resource_unit_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_related_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_related_resource_object.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/credit_usage_summary_resource_window.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/design_request.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/design_request_target_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_estimate_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_estimate_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_battery_topology_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_display_labels_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_error_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_input_snapshot_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_result_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_resource_status.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in_esc_timing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in_esc_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_group_in_motor_cooling_source.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_esc_timing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_esc_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_motor_cooling_source.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/dynamic_rotor_in_rotation_sense.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/export_geometry_request.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/export_geometry_request_format.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/export_geometry_request_rotation_type_0.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/fmu_export_accepted_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/fmu_export_job_error.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/fmu_export_status_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/generate_geometry_request.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/geometry_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/geometry_style_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/geometry_styles_list_response.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.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
- {thrustlab-0.5.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
- {thrustlab-0.5.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
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/http_validation_error.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/list_events_v1_events_get_response_list_events_v1_events_get.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/pack_leaf_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/pack_parallel_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/pack_series_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/pack_topology_in.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/project_create_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/project_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/project_patch.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/project_resource.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in_esc_timing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in_esc_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_group_in_motor_cooling_source.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_esc_timing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_esc_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_motor_cooling_source.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/rotor_in_rotation_sense.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/run_selected_body.py +0 -0
- {thrustlab-0.5.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
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/schedule_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/schedule_in_interpolation_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/schedule_in_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_group_command_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_group_command_in_throttle_ramp_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_group_command_in_tilt_ramp_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_airspeed_ramp.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_per_group.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_throttle_ramp.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/segment_in_vertical_speed_ramp.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/share_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_cancel_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_cooling_source_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_flight_regime.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_inflow_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_create_body_launch_intent.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_inputs.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_patch_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_battery_topology_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_display_labels_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_error_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_input_snapshot_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_result_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_result_type_0_additional_property.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/simulation_resource_status.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/spline_data.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/starred_component_create_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/starred_component_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/starred_component_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_create_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_create_body_data_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_patch.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_patch_data_json_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_resource.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/submission_resource_data_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_cancel_body.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_config_in_esc_timing_values_type_0_item.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_create_body_inflow_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_create_body_launch_intent.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_inputs.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_param_range_in_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_patch_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_component_selection.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_list_response.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_list_response_display_labels_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_patch.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_resource_rotors_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_point_resource_rotors_type_0_additional_property.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_error_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_input_snapshot_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_plot_config_json_type_0_item.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_status.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_summary_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_resource_sweep_config_type_0.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in_esc_timing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in_esc_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_group_in_motor_cooling_source.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_esc_timing.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_esc_type.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_motor_cooling_source.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/sweep_rotor_in_rotation_sense.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/termination_in.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/termination_in_mode.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/types.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/user_resource_gate_required_tiers.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/user_resource_unit_system.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/v1_custom_component_override.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/v1_custom_component_override_spec_json.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/v1_health_v1_health_get_response_v1_health_v1_health_get.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/validation_error.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/webhook_endpoint_create.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_models/webhook_endpoint_patch.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/_pagination.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/client.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/exceptions.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/_base.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/components.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/compute_units.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/dynamic_simulations.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/fmu.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/projects.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/starred_components.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/submissions.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/users.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/resources/webhook_endpoints.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/README.md +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/__main__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/bridge.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/cli.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/fmu.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/kinematics.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/model.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/protocol.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/rigidbody.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/rpmlog.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/__init__.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/ardupilot.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/base.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/targets/betaflight.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/sitl/vehicle.py +0 -0
- {thrustlab-0.5.0 → thrustlab-0.7.0}/thrustlab/webhooks.py +0 -0
|
@@ -6,7 +6,128 @@ 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
|
-
## [
|
|
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`.
|
|
95
|
+
|
|
96
|
+
## [0.6.0] - 2026-09-09
|
|
97
|
+
|
|
98
|
+
### Added
|
|
99
|
+
|
|
100
|
+
- `client.simulations.cancel_selected(run_ids)` cancels up to 1000 runs of any
|
|
101
|
+
kind (`sim_`, `sweep_`, `dyn_`) in one call and reports which were canceled,
|
|
102
|
+
which were asked to stop, and which were skipped and why.
|
|
103
|
+
`client.simulations.cancel_queued(project_id=...)` cancels every queued run
|
|
104
|
+
you own, optionally within one project.
|
|
105
|
+
- `client.simulations.run_queued()` and
|
|
106
|
+
`client.simulations.run_selected(simulation_ids)` dispatch runs parked with
|
|
107
|
+
`launch_intent="queue"` — all of them, or a named subset. Both accept sweep
|
|
108
|
+
ids as well as single-point ids.
|
|
109
|
+
- Generated models for the batch cancel response (`CancelBatchResult`,
|
|
110
|
+
`CancelSkipped`) and request body (`CancelSelectedBody`).
|
|
111
|
+
- Two runnable examples. `examples/sweeps/component_axis_grid.py` crosses a
|
|
112
|
+
propeller component axis with a throttle range in one submission and reads
|
|
113
|
+
the propeller name back off each point.
|
|
114
|
+
`examples/dynamic/watch_and_cancel.py` watches a mission's `progress` and
|
|
115
|
+
cancels it part-way through.
|
|
116
|
+
|
|
117
|
+
### Changed
|
|
118
|
+
|
|
119
|
+
- Cancelling a running sweep is now a durable request: the server records it
|
|
120
|
+
and the worker stops after the point it is computing, even if the stop takes
|
|
121
|
+
longer than a minute to reach.
|
|
122
|
+
- Cancelling a RUNNING dynamic mission now reports under `requested` rather
|
|
123
|
+
than `canceled`. The mission keeps its partial result (`run_meta.partial`,
|
|
124
|
+
`run_meta.canceled_at_t_s`), the credits are refunded, and the execution slot
|
|
125
|
+
frees when the worker stops at its next integration window.
|
|
126
|
+
- Run resources carry `started_at`, the instant the solver began, alongside
|
|
127
|
+
`created_at`, `dispatched_at` and `completed_at`. Queue wait is
|
|
128
|
+
`started_at - dispatched_at`.
|
|
129
|
+
- Dynamic run resources carry `progress` and `cancel_requested_at`. Sweep
|
|
130
|
+
resources carry `cancel_requested_at` and `credits_breakdown`.
|
|
10
131
|
|
|
11
132
|
## [0.5.0] - 2026-08-31
|
|
12
133
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thrustlab
|
|
3
|
-
Version: 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
|
|
@@ -93,6 +93,53 @@ Runnable end-to-end examples for every simulation type live in
|
|
|
93
93
|
[`sweeps/run_and_poll.py`](./examples/sweeps/run_and_poll.py), and
|
|
94
94
|
[`dynamic/run_and_poll.py`](./examples/dynamic/run_and_poll.py).
|
|
95
95
|
|
|
96
|
+
## FMU export and SITL
|
|
97
|
+
|
|
98
|
+
Export a completed simulation as an FMI 3.0 co-simulation FMU:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
job_id = client.fmu.export(sim_id)["id"]
|
|
102
|
+
client.fmu.wait(job_id)
|
|
103
|
+
client.fmu.download(job_id, "powertrain.fmu")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The FMU exposes per-rotor inputs and outputs, battery and thermal state, and a
|
|
107
|
+
validity envelope; it runs in any FMI 3.0 co-simulation host and is verified
|
|
108
|
+
in Simulink.
|
|
109
|
+
|
|
110
|
+
`thrustlab-sitl`, installed with the SDK, flies the FMU in ArduPilot SITL or
|
|
111
|
+
Betaflight SITL (`--target betaflight`). See
|
|
112
|
+
[`thrustlab/sitl/README.md`](./thrustlab/sitl/README.md) for the full
|
|
113
|
+
documentation.
|
|
114
|
+
|
|
115
|
+
```shell
|
|
116
|
+
thrustlab-sitl --fmu powertrain.fmu --vehicle quad_x.yaml --target betaflight
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
See [`examples/fmu/`](./examples/fmu/) for bundled ready-to-fly FMUs with
|
|
120
|
+
quadcopter, Y6 coax, and tilt-tricopter missions, an endurance study, and
|
|
121
|
+
replay rendering.
|
|
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
|
+
|
|
96
143
|
## Why the SDK (and not `urllib` / `wget`)?
|
|
97
144
|
|
|
98
145
|
The SDK gives you automatic retries with backoff, idempotency keys, cursor
|
|
@@ -131,6 +178,7 @@ client.submissions
|
|
|
131
178
|
client.starred_components
|
|
132
179
|
client.compute_units
|
|
133
180
|
client.webhook_endpoints
|
|
181
|
+
client.fmu
|
|
134
182
|
```
|
|
135
183
|
|
|
136
184
|
## Error handling
|
|
@@ -57,6 +57,53 @@ Runnable end-to-end examples for every simulation type live in
|
|
|
57
57
|
[`sweeps/run_and_poll.py`](./examples/sweeps/run_and_poll.py), and
|
|
58
58
|
[`dynamic/run_and_poll.py`](./examples/dynamic/run_and_poll.py).
|
|
59
59
|
|
|
60
|
+
## FMU export and SITL
|
|
61
|
+
|
|
62
|
+
Export a completed simulation as an FMI 3.0 co-simulation FMU:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
job_id = client.fmu.export(sim_id)["id"]
|
|
66
|
+
client.fmu.wait(job_id)
|
|
67
|
+
client.fmu.download(job_id, "powertrain.fmu")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The FMU exposes per-rotor inputs and outputs, battery and thermal state, and a
|
|
71
|
+
validity envelope; it runs in any FMI 3.0 co-simulation host and is verified
|
|
72
|
+
in Simulink.
|
|
73
|
+
|
|
74
|
+
`thrustlab-sitl`, installed with the SDK, flies the FMU in ArduPilot SITL or
|
|
75
|
+
Betaflight SITL (`--target betaflight`). See
|
|
76
|
+
[`thrustlab/sitl/README.md`](./thrustlab/sitl/README.md) for the full
|
|
77
|
+
documentation.
|
|
78
|
+
|
|
79
|
+
```shell
|
|
80
|
+
thrustlab-sitl --fmu powertrain.fmu --vehicle quad_x.yaml --target betaflight
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
See [`examples/fmu/`](./examples/fmu/) for bundled ready-to-fly FMUs with
|
|
84
|
+
quadcopter, Y6 coax, and tilt-tricopter missions, an endurance study, and
|
|
85
|
+
replay rendering.
|
|
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
|
+
|
|
60
107
|
## Why the SDK (and not `urllib` / `wget`)?
|
|
61
108
|
|
|
62
109
|
The SDK gives you automatic retries with backoff, idempotency keys, cursor
|
|
@@ -95,6 +142,7 @@ client.submissions
|
|
|
95
142
|
client.starred_components
|
|
96
143
|
client.compute_units
|
|
97
144
|
client.webhook_endpoints
|
|
145
|
+
client.fmu
|
|
98
146
|
```
|
|
99
147
|
|
|
100
148
|
## Error handling
|
|
@@ -18,8 +18,8 @@ Notes on validation:
|
|
|
18
18
|
one resistance figure (c_rating or pack_resistance_mOhm), and bounding-box
|
|
19
19
|
dims; form_factor is derived from chemistry when omitted.
|
|
20
20
|
* Propeller: per-station radius/chord/twist and rotation are required, and
|
|
21
|
-
persisting a propeller needs the
|
|
22
|
-
|
|
21
|
+
persisting a propeller needs the Pro plan — the create below reports the
|
|
22
|
+
tier gate cleanly on a free account.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
from thrustlab import Client
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Dynamic progress + cancel: submit -> watch `progress` -> cancel -> read the partial.
|
|
2
|
+
|
|
3
|
+
Run it:
|
|
4
|
+
export THRUSTLAB_API_KEY=key_... # never hard-code the key
|
|
5
|
+
python examples/dynamic/watch_and_cancel.py
|
|
6
|
+
|
|
7
|
+
Submits a mission that flies until the pack is empty, watches `progress` while
|
|
8
|
+
it runs, cancels it part-way through, and reads the partial result.
|
|
9
|
+
|
|
10
|
+
`progress` is {fraction, sim_time_s, sim_time_total_s, wall_s, updated_at}, and
|
|
11
|
+
is None until the worker picks the run up.
|
|
12
|
+
|
|
13
|
+
Cancelling a RUNNING dynamic mission does not stop it instantly. The response
|
|
14
|
+
keeps status "running" and sets `cancel_requested_at`; the worker stops at its
|
|
15
|
+
next integration window, at most 20 seconds of mission time. What comes back is
|
|
16
|
+
a real partial result: `run_meta.partial` is true, `run_meta.canceled_at_t_s`
|
|
17
|
+
gives the mission time it stopped at, and the credits are refunded.
|
|
18
|
+
|
|
19
|
+
This mission runs to depletion, so `fraction` is an estimate rather than an
|
|
20
|
+
exact ratio.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import time
|
|
24
|
+
|
|
25
|
+
from thrustlab import Client
|
|
26
|
+
|
|
27
|
+
client = Client() # reads $THRUSTLAB_API_KEY from the environment
|
|
28
|
+
|
|
29
|
+
project = client.projects.create(name="sdk dynamic progress example")
|
|
30
|
+
|
|
31
|
+
motor = client.components.find(name="BadAss 2826-820Kv")
|
|
32
|
+
prop = client.components.find(name="10.5x4.5")
|
|
33
|
+
battery = client.components.find(
|
|
34
|
+
name="Liperior 5000mAh 4S 35C 14.8V Lipo Battery With XT90 Plug"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# A hover flown until the pack is empty.
|
|
38
|
+
#
|
|
39
|
+
# The schedule steps to 15% before it ramps, rather than ramping up from rest.
|
|
40
|
+
# One external study observed ramps starting at near-zero duty producing a
|
|
41
|
+
# handful of non-converged rows in their first milliseconds; as a fraction of a
|
|
42
|
+
# short flight's rows that was enough to get a run rejected with
|
|
43
|
+
# `solver_no_convergence`. Stepping to a working throttle first avoided it.
|
|
44
|
+
dyn = client.dynamic_simulations.create(
|
|
45
|
+
project_id=project["id"],
|
|
46
|
+
battery_component_id=battery["id"],
|
|
47
|
+
density_kg_m3=1.225,
|
|
48
|
+
battery_charge_pct=100,
|
|
49
|
+
ambient_temp_c=25,
|
|
50
|
+
rotor_groups=[
|
|
51
|
+
{
|
|
52
|
+
"label": "main",
|
|
53
|
+
"count": 4,
|
|
54
|
+
"motor_component_id": motor["id"],
|
|
55
|
+
"propeller_component_id": prop["id"],
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
schedule={
|
|
59
|
+
"mode": "segments",
|
|
60
|
+
"segments": [
|
|
61
|
+
# Step to the ESC's working range first.
|
|
62
|
+
{
|
|
63
|
+
"duration_s": 0.2,
|
|
64
|
+
"airspeed_target": 0.0,
|
|
65
|
+
"per_group": {"main": {"throttle_target": 15, "throttle_ramp": "step"}},
|
|
66
|
+
},
|
|
67
|
+
# Then ramp from there to the hover setting.
|
|
68
|
+
{
|
|
69
|
+
"duration_s": 2.0,
|
|
70
|
+
"airspeed_target": 0.0,
|
|
71
|
+
"per_group": {"main": {"throttle_target": 65, "throttle_ramp": "linear"}},
|
|
72
|
+
},
|
|
73
|
+
# Hold until the pack is empty. `until_depleted` takes no duration.
|
|
74
|
+
{
|
|
75
|
+
"until_depleted": True,
|
|
76
|
+
"airspeed_target": 0.0,
|
|
77
|
+
"per_group": {"main": {"throttle_target": 65}},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
# The total mission time is not known in advance, so progress["fraction"]
|
|
82
|
+
# is an estimate rather than an exact ratio.
|
|
83
|
+
termination={"mode": "until_depleted", "soc_cutoff_pct": 20.0},
|
|
84
|
+
)
|
|
85
|
+
print(f"created {dyn['id']}, status={dyn['status']}")
|
|
86
|
+
|
|
87
|
+
# Watch progress. 2-5 s is the right cadence; anything faster spends requests
|
|
88
|
+
# on a value the worker only rewrites once per integration window.
|
|
89
|
+
CANCEL_AFTER_S = 45.0
|
|
90
|
+
deadline = time.monotonic() + CANCEL_AFTER_S
|
|
91
|
+
|
|
92
|
+
while time.monotonic() < deadline:
|
|
93
|
+
time.sleep(3.0)
|
|
94
|
+
run = client.dynamic_simulations.retrieve(dyn["id"])
|
|
95
|
+
if run["status"] not in ("queued", "running"):
|
|
96
|
+
break
|
|
97
|
+
# `progress` is None until the worker picks the run up.
|
|
98
|
+
p = run.get("progress")
|
|
99
|
+
if p is None:
|
|
100
|
+
print(f" {run['status']}, no progress yet")
|
|
101
|
+
continue
|
|
102
|
+
frac = p.get("fraction")
|
|
103
|
+
pct = f"{frac * 100:5.1f}%" if frac is not None else " ? %"
|
|
104
|
+
print(f" {pct} sim {p['sim_time_s']:.1f} s wall {p['wall_s']:.1f} s")
|
|
105
|
+
|
|
106
|
+
run = client.dynamic_simulations.retrieve(dyn["id"])
|
|
107
|
+
if run["status"] in ("queued", "running"):
|
|
108
|
+
# Cancel whatever state the deadline caught it in. A queued run comes back
|
|
109
|
+
# `canceled` immediately, with the compute units refunded and no result. A
|
|
110
|
+
# running one comes back still `running` with cancel_requested_at set, and
|
|
111
|
+
# the worker stops at its next integration window.
|
|
112
|
+
#
|
|
113
|
+
# Cancelling both matters here: with every execution slot busy this run can
|
|
114
|
+
# still be queued at the deadline, and skipping it would leave it to
|
|
115
|
+
# execute later against a script that has already exited.
|
|
116
|
+
accepted = client.dynamic_simulations.cancel(dyn["id"])
|
|
117
|
+
print(f"\ncancel accepted: status={accepted['status']}, "
|
|
118
|
+
f"cancel_requested_at={accepted.get('cancel_requested_at')}")
|
|
119
|
+
run = client.dynamic_simulations.wait(dyn["id"], timeout=300)
|
|
120
|
+
|
|
121
|
+
print(f"final status: {run['status']}")
|
|
122
|
+
|
|
123
|
+
blob = run.get("result")
|
|
124
|
+
if blob:
|
|
125
|
+
meta = blob["run_meta"]
|
|
126
|
+
if meta.get("partial"):
|
|
127
|
+
print(f"partial result, stopped at t = {meta['canceled_at_t_s']:.1f} s of mission time")
|
|
128
|
+
sc = blob["scorecard"]
|
|
129
|
+
print(f"flight time: {sc['flight_time_s']:.1f} s")
|
|
130
|
+
print(f"energy: {sc['energy_wh']:.1f} Wh")
|
|
131
|
+
print(f"peak winding: {sc['peak_winding_temp_c']:.1f} C")
|
|
132
|
+
print(f"samples returned: {len(blob['samples'])}")
|
|
@@ -22,7 +22,7 @@ state of charge — all from the `.fmu` you exported.
|
|
|
22
22
|
```sh
|
|
23
23
|
mkdir -p mission_out
|
|
24
24
|
python -m thrustlab.sitl \
|
|
25
|
-
--fmu
|
|
25
|
+
--fmu ../validation-quad.fmu --vehicle quad_x.yaml \
|
|
26
26
|
--rpm-log mission_out/mission_rpm_log.csv
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -78,3 +78,12 @@ RPM lane — so the bridge logs it to `mission_out/mission_rpm_log.csv` for
|
|
|
78
78
|
reconciliation against flight logs. If you changed the vehicle mass in
|
|
79
79
|
`quad_x.yaml`, set `WEIGHT_N` at the top of `plot_mission.py` to match — it
|
|
80
80
|
aligns the two recordings' clocks on the takeoff event.
|
|
81
|
+
|
|
82
|
+
## The bundled FMU
|
|
83
|
+
|
|
84
|
+
This example ships with a ready-to-fly FMU exported from ThrustLab
|
|
85
|
+
(`../validation-quad.fmu`). The exact powertrain it reproduces is documented
|
|
86
|
+
in the archive's own `resources/README.md`; unzip the FMU to read it. To
|
|
87
|
+
recreate the FMU, build the same powertrain in ThrustLab or through the
|
|
88
|
+
API/SDK, then export it with **Export → FMU** or `client.fmu`; your export
|
|
89
|
+
will fly these examples identically.
|
|
@@ -31,7 +31,7 @@ Everything the quad example needs (`../ardupilot_sitl/README.md`), plus:
|
|
|
31
31
|
```sh
|
|
32
32
|
mkdir -p mission_out
|
|
33
33
|
python -m thrustlab.sitl \
|
|
34
|
-
--fmu
|
|
34
|
+
--fmu example-tri.fmu --vehicle tilttri.yaml \
|
|
35
35
|
--rpm-log mission_out/mission_rpm_log.csv
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -74,3 +74,12 @@ physics. And a stopped rotor's flag means nothing: the rear rotor is
|
|
|
74
74
|
deliberately stopped for most of the circuit while the wing carries the
|
|
75
75
|
weight, so judge the envelope over spinning rotors only — the replay video
|
|
76
76
|
does exactly that.
|
|
77
|
+
|
|
78
|
+
## The bundled FMU
|
|
79
|
+
|
|
80
|
+
This example ships with a ready-to-fly FMU exported from ThrustLab
|
|
81
|
+
(`example-tri.fmu`). The exact powertrain it reproduces is documented in the
|
|
82
|
+
archive's own `resources/README.md`; unzip the FMU to read it. To recreate
|
|
83
|
+
the FMU, build the same powertrain in ThrustLab or through the API/SDK, then
|
|
84
|
+
export it with **Export → FMU** or `client.fmu`; your export will fly these
|
|
85
|
+
examples identically.
|
|
@@ -22,7 +22,7 @@ Same three terminals as the quad example, swapping in this directory's files:
|
|
|
22
22
|
```sh
|
|
23
23
|
mkdir -p mission_out
|
|
24
24
|
python -m thrustlab.sitl \
|
|
25
|
-
--fmu
|
|
25
|
+
--fmu example-y6.fmu --vehicle y6.yaml \
|
|
26
26
|
--rpm-log mission_out/mission_rpm_log.csv
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -32,15 +32,19 @@ python -m thrustlab.sitl \
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
|
-
python ../ardupilot_sitl/fly_mission.py --out-dir ./mission_out --speed-scale 0.
|
|
35
|
+
python ../ardupilot_sitl/fly_mission.py --out-dir ./mission_out --speed-scale 0.42
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`--speed-scale 0.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
`--speed-scale 0.42` uses the bundled FMU's wider sampled coax envelope: at
|
|
39
|
+
hover rotor speed, it covers level edgewise flight to about 7 m/s, descent to
|
|
40
|
+
3 m/s, and gentle approach legs (about 1.5 m/s down) to about 4.5 m/s
|
|
41
|
+
edgewise. The entire 397-second demo mission stays on measured physics, with
|
|
42
|
+
99.2% of armed frames `all_in_envelope`; the remainder are brief arm/disarm
|
|
43
|
+
spool transients. The binding limit is the slowest rotor because the edgewise
|
|
44
|
+
axis scales with rotor speed, advance-ratio style: in forward flight the
|
|
45
|
+
unloaded rear-lower rotor reaches the envelope edge near 6.3 m/s, while
|
|
46
|
+
loaded rotors remain covered to about 7.3 m/s. Beyond the envelope, outputs
|
|
47
|
+
clamp to its edge and set `all_in_envelope=false` rather than extrapolating.
|
|
44
48
|
|
|
45
49
|
## The plots
|
|
46
50
|
|
|
@@ -60,3 +64,12 @@ python plot_coax_split.py ./mission_out ./mission_out/plots
|
|
|
60
64
|
Its bottom panel is the point of the whole example: the lower rotor's share
|
|
61
65
|
of each stack's thrust sits below 50% for the entire flight — the wake
|
|
62
66
|
coupling, resolved per rotor, in a model the autopilot drives at 400 Hz.
|
|
67
|
+
|
|
68
|
+
## The bundled FMU
|
|
69
|
+
|
|
70
|
+
This example ships with a ready-to-fly FMU exported from ThrustLab
|
|
71
|
+
(`example-y6.fmu`). The exact powertrain it reproduces is documented in the
|
|
72
|
+
archive's own `resources/README.md`; unzip the FMU to read it. To recreate
|
|
73
|
+
the FMU, build the same powertrain in ThrustLab or through the API/SDK, then
|
|
74
|
+
export it with **Export → FMU** or `client.fmu`; your export will fly these
|
|
75
|
+
examples identically.
|
|
@@ -81,7 +81,7 @@ ls eeprom.bin
|
|
|
81
81
|
```bash
|
|
82
82
|
python -m thrustlab.sitl \
|
|
83
83
|
--target betaflight \
|
|
84
|
-
--fmu
|
|
84
|
+
--fmu ../validation-quad.fmu \
|
|
85
85
|
--vehicle betaflight_quad_x.yaml \
|
|
86
86
|
--rpm-log mission_rpm_log.csv \
|
|
87
87
|
--truth-port 9005 \
|
|
@@ -240,3 +240,12 @@ servo channels at all. The error reports the received size.
|
|
|
240
240
|
- Betaflight master provides waypoint navigation through `BOX_AUTOPILOT` and
|
|
241
241
|
`waypoint insert` in the CLI. The bridge supplies the required data, but this
|
|
242
242
|
example does not test it.
|
|
243
|
+
|
|
244
|
+
## The bundled FMU
|
|
245
|
+
|
|
246
|
+
This example ships with a ready-to-fly FMU exported from ThrustLab
|
|
247
|
+
(`../validation-quad.fmu`). The exact powertrain it reproduces is documented
|
|
248
|
+
in the archive's own `resources/README.md`; unzip the FMU to read it. To
|
|
249
|
+
recreate the FMU, build the same powertrain in ThrustLab or through the
|
|
250
|
+
API/SDK, then export it with **Export → FMU** or `client.fmu`; your export
|
|
251
|
+
will fly these examples identically.
|
|
@@ -16,7 +16,7 @@ of extrapolated.
|
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
18
|
pip install fmpy numpy matplotlib
|
|
19
|
-
python hover_endurance.py
|
|
19
|
+
python hover_endurance.py ../validation-quad.fmu --mass-kg 1.20
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
`--mass-kg` is the all-up mass WITHOUT payload (airframe + battery).
|
|
@@ -29,3 +29,12 @@ each flight — the spread between the two is the battery sag made visible.
|
|
|
29
29
|
|
|
30
30
|
Export an FMU with `examples/fmu/export_and_download.py` or the dashboard's
|
|
31
31
|
Share menu.
|
|
32
|
+
|
|
33
|
+
## The bundled FMU
|
|
34
|
+
|
|
35
|
+
This example ships with a ready-to-fly FMU exported from ThrustLab
|
|
36
|
+
(`../validation-quad.fmu`). The exact powertrain it reproduces is documented
|
|
37
|
+
in the archive's own `resources/README.md`; unzip the FMU to read it. To
|
|
38
|
+
recreate the FMU, build the same powertrain in ThrustLab or through the
|
|
39
|
+
API/SDK, then export it with **Export → FMU** or `client.fmu`; your export
|
|
40
|
+
will fly these examples identically.
|
|
@@ -7,7 +7,7 @@ links — spin-up dynamics, battery state and envelope flags included.
|
|
|
7
7
|
|
|
8
8
|
## What you need
|
|
9
9
|
|
|
10
|
-
- An exported FMU, **unzipped** (`unzip
|
|
10
|
+
- An exported FMU, **unzipped** (`unzip ../validation-quad.fmu -d validation-quad/`).
|
|
11
11
|
- The `fmu-gz-plugin` ThrustlabFmuSystem plugin, built against gz-sim ≥ 9
|
|
12
12
|
(see its README for the CMake build; it dlopens the FMU's own
|
|
13
13
|
`binaries/x86_64-linux/*.so`).
|
|
@@ -61,3 +61,12 @@ For an ArduPilot- or PX4-controlled Gazebo vehicle, keep their standard
|
|
|
61
61
|
Gazebo integration for control surfaces and use this plugin as the
|
|
62
62
|
propulsion + battery physics; the ArduPilot JSON bridge
|
|
63
63
|
(`thrustlab.sitl`) is the lighter path when you don't need a 3D world.
|
|
64
|
+
|
|
65
|
+
## The bundled FMU
|
|
66
|
+
|
|
67
|
+
This example ships with a ready-to-fly FMU exported from ThrustLab
|
|
68
|
+
(`../validation-quad.fmu`). The exact powertrain it reproduces is documented
|
|
69
|
+
in the archive's own `resources/README.md`; unzip the FMU to read it. To
|
|
70
|
+
recreate the FMU, build the same powertrain in ThrustLab or through the
|
|
71
|
+
API/SDK, then export it with **Export → FMU** or `client.fmu`; your export
|
|
72
|
+
will fly these examples identically.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Simulink co-simulation
|
|
2
|
+
|
|
3
|
+
`run_fmu_simulink.m` builds a Simulink model programmatically, imports the
|
|
4
|
+
bundled quadcopter FMU, commands a fixed throttle on every rotor, and runs
|
|
5
|
+
FMI 3.0 Co-Simulation. It plots thrust, bus voltage, and battery state of
|
|
6
|
+
charge to `fmu_cosim.png`.
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
MATLAB + Simulink, verified on R2025a. No toolboxes beyond Simulink are
|
|
13
|
+
required.
|
|
14
|
+
|
|
15
|
+
## Run it
|
|
16
|
+
|
|
17
|
+
From this folder:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
matlab -batch "run('run_fmu_simulink.m')"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or open `run_fmu_simulink.m` in MATLAB and run it interactively.
|
|
24
|
+
|
|
25
|
+
## FMU import gotchas
|
|
26
|
+
|
|
27
|
+
Three things that will silently ruin the model if changed:
|
|
28
|
+
|
|
29
|
+
- The FMU Import block lives in the Simulink Extras library
|
|
30
|
+
(`'simulink_extras/FMU Import/FMU'`), NOT in User-Defined Functions.
|
|
31
|
+
- The block wants a BARE file name with the FMU's folder on the MATLAB
|
|
32
|
+
path — a full path in `FMUName` is not accepted.
|
|
33
|
+
- Every FMU output you care about must be wired to a root Outport;
|
|
34
|
+
unconnected outputs are removed by block reduction and the simulation
|
|
35
|
+
proves nothing about them.
|
|
36
|
+
|
|
37
|
+
Port order follows the FMU's `modelDescription.xml` declaration order and is
|
|
38
|
+
the same for every ThrustLab export regardless of rotor count. The full
|
|
39
|
+
input and output list is in the script header.
|
|
40
|
+
|
|
41
|
+
## Use your own FMU
|
|
42
|
+
|
|
43
|
+
Export an FMU from a completed simulation through **Share → Export FMU** or
|
|
44
|
+
`client.fmu`. Put its folder on the MATLAB path, then set `FMU_FILE` to the
|
|
45
|
+
bare archive name and `N_ROTORS` to its rotor count in
|
|
46
|
+
`run_fmu_simulink.m`.
|
|
47
|
+
|
|
48
|
+
## The bundled FMU
|
|
49
|
+
|
|
50
|
+
This example ships with a quadcopter FMU exported from ThrustLab
|
|
51
|
+
(`../validation-quad.fmu`). The exact powertrain it reproduces is documented
|
|
52
|
+
in the archive's own `resources/README.md`; unzip the FMU to read it. An
|
|
53
|
+
equivalent export will fly this example identically.
|
|
Binary file
|