fix(track-core): align RSMT switches to line end

Allow repeated speed-mileage time-segmented schemes to use either the original start-boundary timestamp form or the segment-end timestamp form emitted by older clients.

For RSMT schemes whose first time segment starts at zero, keep switching against the next segment boundary. For schemes whose first time is non-zero, treat each timestamp as the current segment's end time so payloads such as 120/240/360 are accepted and scheduled correctly.

Defer eligible time-segment changes until the track mileage reaches the configured line-length boundary after movement, matching the legacy app-track behavior that avoided mid-line speed pattern changes.

Add runtime tests covering line-end switching and non-zero first time segment acceptance.

Verification: cmake --build build && ctest --test-dir build --output-on-failure
This commit is contained in:
2026-05-27 13:12:17 +08:00
parent 7b0f6a523d
commit 5ed07253d8
2 changed files with 103 additions and 22 deletions
+36 -4
View File
@@ -524,11 +524,42 @@ void test_repeated_speed_mileage_time_runtime_ticks() {
require(runtime.has_value(), "RSMT runtime builds");
auto track = track_core::start_scheme_track(std::move(*runtime));
track = track_core::tick_scheme_track(runtime_config(), std::move(track), 3.0F);
track = track_core::tick_scheme_track(runtime_config(), std::move(track), 2.0F);
require_eq_u32(static_cast<std::uint32_t>(track.state.primary_segment_index), 0, "RSMT waits for line alignment");
require_eq_u32(static_cast<std::uint32_t>(track.state.primary_segment_index), 0, "RSMT waits before line end");
track = track_core::tick_scheme_track(runtime_config(), std::move(track), 1.0F);
require_eq_u32(static_cast<std::uint32_t>(track.state.primary_segment_index), 1, "RSMT switches on alignment");
track = track_core::tick_scheme_track(runtime_config(), std::move(track), 2.0F);
require_eq_u32(static_cast<std::uint32_t>(track.state.primary_segment_index), 1, "RSMT switches at line end");
require_near(track.state.speed_m_s, 3.0F, 0.001F, "RSMT switched speed");
}
void test_repeated_speed_mileage_time_accepts_segment_end_times() {
auto runtime = track_core::make_scheme_track_runtime(track_core::make_repeated_speed_mileage_time_scheme(
16,
track_core::Color::white(),
track_core::AccelerationProfile::instant,
{
track_core::RepeatedSMSegment{
.speed_mileage_segments = {
track_core::SMSegment{.speed_m_s = 1.0F, .mileage_from_start_m = 0},
track_core::SMSegment{.speed_m_s = 2.0F, .mileage_from_start_m = 5},
},
.time_since_start_s = 4,
},
track_core::RepeatedSMSegment{
.speed_mileage_segments = {
track_core::SMSegment{.speed_m_s = 3.0F, .mileage_from_start_m = 0},
track_core::SMSegment{.speed_m_s = 1.0F, .mileage_from_start_m = 5},
},
.time_since_start_s = 20,
},
}));
require(runtime.has_value(), "RSMT segment-end-time runtime builds");
auto track = track_core::start_scheme_track(std::move(*runtime));
track = track_core::tick_scheme_track(runtime_config(), std::move(track), 3.0F);
require_eq_u32(static_cast<std::uint32_t>(track.state.primary_segment_index), 0, "RSMT segment-end-time waits before boundary");
track = track_core::tick_scheme_track(runtime_config(), std::move(track), 2.0F);
require_eq_u32(static_cast<std::uint32_t>(track.state.primary_segment_index), 1, "RSMT segment-end-time switches at line end");
require_near(track.state.speed_m_s, 3.0F, 0.001F, "RSMT switched speed");
}
@@ -581,6 +612,7 @@ int main() {
test_mileage_time_runtime_ticks();
test_speed_time_runtime_ticks();
test_repeated_speed_mileage_time_runtime_ticks();
test_repeated_speed_mileage_time_accepts_segment_end_times();
test_scheme_training_runtime_renders();
std::cout << "track-core tests passed\n";
return 0;