Skip to content

Commit 3e7ce29

Browse files
authored
fix(flight-planner): refresh stale home from vehicle (#29)
* fix(flight-planner): refresh home on plan activation * test(flight-planner): cover vehicle home synchronization * docs(porting): record planner home sync handoff
1 parent f02eefd commit 3e7ce29

4 files changed

Lines changed: 173 additions & 1 deletion

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using Avalonia.Headless.XUnit;
2+
using MissionPlanner.Utilities;
3+
using MissionPlanner.ViewModels;
4+
5+
namespace MissionPlanner.Tests;
6+
7+
[CollectionDefinition(Name, DisableParallelization = true)]
8+
public sealed class FlightPlannerHomeSyncCollection {
9+
public const string Name = "Flight Planner home synchronization";
10+
}
11+
12+
[Collection(FlightPlannerHomeSyncCollection.Name)]
13+
public class FlightPlannerHomeSyncTests {
14+
[Fact]
15+
public void Reported_home_replaces_saved_home() {
16+
using var scope = new PlannerScope();
17+
FlightPlannerViewModel vm = scope.ViewModel;
18+
SetSanFranciscoHome(vm);
19+
20+
vm.RefreshHomeFromVehicle(
21+
new PointLatLngAlt(50.344417763, 30.89499303, 132),
22+
new PointLatLngAlt(-35.363261, 149.165230, 584));
23+
24+
Assert.Equal(50.344417763, vm.HomeLat, 8);
25+
Assert.Equal(30.89499303, vm.HomeLng, 8);
26+
Assert.Equal(132, vm.HomeAlt, 6);
27+
}
28+
29+
[Fact]
30+
public void Planner_home_refresh_falls_back_to_planned_home() {
31+
using var scope = new PlannerScope();
32+
FlightPlannerViewModel vm = scope.ViewModel;
33+
34+
vm.RefreshHomeFromVehicle(
35+
new PointLatLngAlt(),
36+
new PointLatLngAlt(50.344417763, 30.89499303, 132));
37+
38+
Assert.Equal(50.344417763, vm.HomeLat, 8);
39+
Assert.Equal(30.89499303, vm.HomeLng, 8);
40+
Assert.Equal(132, vm.HomeAlt, 6);
41+
}
42+
43+
[Fact]
44+
public void Planner_home_refresh_keeps_saved_home_without_vehicle_coordinates() {
45+
using var scope = new PlannerScope();
46+
FlightPlannerViewModel vm = scope.ViewModel;
47+
SetSanFranciscoHome(vm);
48+
49+
vm.RefreshHomeFromVehicle(new PointLatLngAlt(), new PointLatLngAlt());
50+
51+
Assert.Equal(37.619373, vm.HomeLat, 8);
52+
Assert.Equal(-122.376637, vm.HomeLng, 8);
53+
Assert.Equal(5.28, vm.HomeAlt, 6);
54+
}
55+
56+
[AvaloniaFact]
57+
public async Task Navigating_to_plan_refreshes_stale_home_from_vehicle() {
58+
CurrentState state = AppState.comPort.MAV.cs;
59+
PointLatLngAlt previousReportedHome = state.HomeLocation;
60+
PointLatLngAlt previousPlannedHome = state.PlannedHomeLocation;
61+
var shell = new MainWindowViewModel();
62+
var savedPlannerHome = (
63+
shell.FlightPlanner.HomeLat,
64+
shell.FlightPlanner.HomeLng,
65+
shell.FlightPlanner.HomeAlt);
66+
try {
67+
SetSanFranciscoHome(shell.FlightPlanner);
68+
state.HomeLocation = new PointLatLngAlt(50.344417763, 30.89499303, 132);
69+
state.PlannedHomeLocation = new PointLatLngAlt(-35.363261, 149.165230, 584);
70+
71+
await shell.NavigateCommand.ExecuteAsync("PLAN");
72+
73+
Assert.IsType<FlightPlannerViewModel>(shell.CurrentScreen);
74+
Assert.Equal(50.344417763, shell.FlightPlanner.HomeLat, 8);
75+
Assert.Equal(30.89499303, shell.FlightPlanner.HomeLng, 8);
76+
Assert.Equal(132, shell.FlightPlanner.HomeAlt, 6);
77+
78+
state.HomeLocation = new PointLatLngAlt(-35.363261, 149.165230, 584);
79+
80+
await shell.NavigateCommand.ExecuteAsync("PLAN");
81+
82+
Assert.Equal(-35.363261, shell.FlightPlanner.HomeLat, 8);
83+
Assert.Equal(149.165230, shell.FlightPlanner.HomeLng, 8);
84+
Assert.Equal(584, shell.FlightPlanner.HomeAlt, 6);
85+
} finally {
86+
state.HomeLocation = previousReportedHome;
87+
state.PlannedHomeLocation = previousPlannedHome;
88+
shell.FlightPlanner.HomeLat = savedPlannerHome.HomeLat;
89+
shell.FlightPlanner.HomeLng = savedPlannerHome.HomeLng;
90+
shell.FlightPlanner.HomeAlt = savedPlannerHome.HomeAlt;
91+
shell.Dispose();
92+
}
93+
}
94+
95+
private static void SetSanFranciscoHome(FlightPlannerViewModel vm) {
96+
vm.HomeLat = 37.619373;
97+
vm.HomeLng = -122.376637;
98+
vm.HomeAlt = 5.28;
99+
}
100+
101+
private sealed class PlannerScope : IDisposable {
102+
private readonly (double Lat, double Lng, double Alt) _savedHome;
103+
104+
public PlannerScope() {
105+
ViewModel = new FlightPlannerViewModel();
106+
_savedHome = (ViewModel.HomeLat, ViewModel.HomeLng, ViewModel.HomeAlt);
107+
}
108+
109+
public FlightPlannerViewModel ViewModel { get; }
110+
111+
public void Dispose() {
112+
ViewModel.HomeLat = _savedHome.Lat;
113+
ViewModel.HomeLng = _savedHome.Lng;
114+
ViewModel.HomeAlt = _savedHome.Alt;
115+
ViewModel.Dispose();
116+
}
117+
}
118+
}

Porting/STATUS.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
Updated: **2026-08-31**.
44

5+
## Flight Planner vehicle-home synchronization
6+
7+
- Dedicated branch `fix/flight-planner-vehicle-home-sync` starts from merged `master`
8+
`f02eefd87`. Commit `279db6eab` refreshes Flight Planner home state whenever PLAN is selected or
9+
reselected; commit `d7132c4d0` adds the isolated helper and shell-navigation regressions. The
10+
comparison source is official `ArduPilot/MissionPlanner` commit `2b5589f40`.
11+
- The persisted `TXT_homelat`/`TXT_homelng` values could remain at an earlier planning site after a
12+
different vehicle connected. Adding enough local waypoints then correctly drew the official
13+
`last -> home -> first` route, but against that stale home. PLAN activation now prefers the
14+
autopilot-reported `HomeLocation`, falls back to `PlannedHomeLocation`, and retains the saved
15+
planner home only when neither vehicle coordinate is valid. Route construction and its solid or
16+
dashed styling are unchanged.
17+
- The focused `FlightPlannerHomeSyncTests` pass **4/4**, `PlannerPortParityTests` pass **59/59**,
18+
and `dotnet build MissionPlanner.csproj -c Release -m:1 --no-restore` succeeds with **0 warnings /
19+
0 errors**. The complete Avalonia suite ran **1562** cases: **1561 passed** and only the existing
20+
environment-sensitive `VideoSourceResolverTests.NormalizesCommonStreamSources` `/dev/video0`
21+
case failed; neither the resolver nor its test changes on this branch.
22+
- Behavior, integration and test reviewers approve the implementation and test tuple `279db6eab`
23+
+ `d7132c4d0`. The worktree still contains only the user's five pre-existing modifications in
24+
`Drivers/inf2cat.bat`, `Drivers/uninstall_drivers.bat`,
25+
`ExtLibs/Mavlink/regenerate.bat`, `ExtLibs/Mavlink/updatexmls.bat` and `graphs/updatexmls.bat`;
26+
none is staged or included. Claude remains disabled.
27+
- No code or test blocker remains. The screenshot process loaded the older `7de82f8a`, so manual
28+
acceptance requires rebuilding and relaunching this branch: connect a vehicle whose reported
29+
home differs from the saved planner home, select and reselect PLAN, confirm the Home Location
30+
fields follow the vehicle home, then add local waypoints and confirm the closing route remains
31+
local. The separately observed Flight Data bearing-axis zoom behavior remains excluded.
32+
533
## Flight Planner waypoint viewport stability
634

735
- Dedicated branch `fix/flight-planner-waypoint-viewport-stability` starts from merged `master`

ViewModels/FlightPlannerViewModel.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
namespace MissionPlanner.ViewModels;
2323

24-
public partial class FlightPlannerViewModel : ViewModelBase, IDisposable {
24+
public partial class FlightPlannerViewModel : ViewModelBase, IActivationAware, IDisposable {
2525
private MAVLinkInterface _comPort => AppState.comPort;
2626
private bool _recomputing;
2727
private bool _restoringUndo;
@@ -70,6 +70,29 @@ private void OnConnectionChanged() => Dispatcher.UIThread.Post(() => {
7070
OnPropertyChanged(nameof(VehicleFirmware));
7171
});
7272

73+
public void Activate() => RefreshHomeFromVehicle(
74+
_comPort.MAV.cs.HomeLocation, _comPort.MAV.cs.PlannedHomeLocation);
75+
76+
internal void RefreshHomeFromVehicle(PointLatLngAlt? reportedHome,
77+
PointLatLngAlt? plannedHome) {
78+
PointLatLngAlt? home = IsValidHome(reportedHome)
79+
? reportedHome
80+
: IsValidHome(plannedHome) ? plannedHome : null;
81+
if (home == null) {
82+
return;
83+
}
84+
85+
HomeLat = home.Lat;
86+
HomeLng = home.Lng;
87+
HomeAlt = home.Alt;
88+
}
89+
90+
private static bool IsValidHome(PointLatLngAlt? home) => home != null
91+
&& double.IsFinite(home.Lat) && home.Lat is >= -90 and <= 90
92+
&& double.IsFinite(home.Lng) && home.Lng is >= -180 and <= 180
93+
&& double.IsFinite(home.Alt)
94+
&& (home.Lat != 0 || home.Lng != 0);
95+
7396
private void RefreshDisplayView() {
7497
var profile = Services.DisplayViewService.Current;
7598
ShowVerifyHeight = profile.displayCheckHeightBox;

ViewModels/MainWindowViewModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ private async System.Threading.Tasks.Task Navigate(string target) {
212212
}
213213
CurrentScreen = nextScreen;
214214
}
215+
if (nextScreen is IActivationAware activation) {
216+
activation.Activate();
217+
}
215218
}
216219

217220
[RelayCommand]

0 commit comments

Comments
 (0)