Virtual gearing, trainer-speed blend, and cadence decode
Gears are expressed as an offset to the commanded gradient, leaving the physics on the route's true gradient so shifting changes effort, not speed. Neutral gear commands exactly the route gradient, so an un-shifted ride is unchanged. Cadence is not in FTMS on this trainer but is on its Zwift channel, decoded against captured frames. The undeclared FTMS trailing bytes were ruled out: wheel RPM restated at a fixed 73.8x speed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+20
-6
@@ -7,14 +7,17 @@
|
||||
|
||||
pub mod backend;
|
||||
pub mod commands;
|
||||
pub mod controller;
|
||||
pub mod derive;
|
||||
pub mod devices;
|
||||
pub mod events;
|
||||
#[cfg(feature = "mock-ride")]
|
||||
pub mod mock;
|
||||
pub mod profile_view;
|
||||
pub mod samples;
|
||||
pub mod session_backend;
|
||||
pub mod state;
|
||||
pub mod trainer;
|
||||
|
||||
use tauri::{Manager, RunEvent, WindowEvent};
|
||||
|
||||
@@ -68,6 +71,10 @@ pub fn run() {
|
||||
commands::disconnect_device,
|
||||
commands::forget_device,
|
||||
commands::trainer_controllable,
|
||||
// controller
|
||||
commands::controller_status,
|
||||
commands::connect_controller,
|
||||
commands::disconnect_controller,
|
||||
])
|
||||
.setup(|app| {
|
||||
let handle = app.handle().clone();
|
||||
@@ -75,6 +82,7 @@ pub fn run() {
|
||||
handle.state::<AppState>().lock().devices.start_scan();
|
||||
state::spawn_ride_loop(handle.clone());
|
||||
state::spawn_device_loop(handle.clone());
|
||||
state::spawn_controller_loop(handle.clone());
|
||||
// `BIKECONTROL_DEMO=1` opens straight onto a running ride with the
|
||||
// bundled GPX loaded. Purely a development convenience — it makes
|
||||
// the ride screen reviewable without clicking through first.
|
||||
@@ -88,12 +96,18 @@ pub fn run() {
|
||||
.build(tauri::generate_context!())
|
||||
.expect("failed to start BikeControl")
|
||||
.run(|app, event| {
|
||||
// SAF-2 — on any exit path, hand the trainer back at zero load.
|
||||
if let RunEvent::ExitRequested { .. } = &event {
|
||||
state::release_trainer(app);
|
||||
}
|
||||
if let RunEvent::WindowEvent { event: WindowEvent::Destroyed, .. } = &event {
|
||||
state::release_trainer(app);
|
||||
// SAF-2 / SAF-9 — on any exit path, hand the trainer back at zero
|
||||
// load and close every link the app owns. `shutdown_devices` blocks
|
||||
// until the reset sequence has actually been written; a
|
||||
// fire-and-forget send would race the process teardown and leave the
|
||||
// rider on a loaded trainer. It is idempotent, which matters because
|
||||
// one quit delivers several of these events.
|
||||
match &event {
|
||||
RunEvent::ExitRequested { .. } | RunEvent::Exit => state::shutdown_devices(app),
|
||||
RunEvent::WindowEvent { event: WindowEvent::Destroyed, .. } => {
|
||||
state::shutdown_devices(app)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user