Skip to content

A new plugin for RC calibration - #1816

Draft
amilcarlucas wants to merge 7 commits into
masterfrom
RC_Calibration
Draft

A new plugin for RC calibration#1816
amilcarlucas wants to merge 7 commits into
masterfrom
RC_Calibration

Conversation

@amilcarlucas

@amilcarlucas amilcarlucas commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Description

RC controller calibration plugin

WIP needs a lot more testing and development

implements #1754

Checklist

  • Run pre-commit checks locally
  • Verified by a human programmer
  • All commits are signed off (use git commit --signoff)
  • Code follows our coding standards
  • Documentation updated if needed
  • No breaking changes or properly documented

Testing

Describe how you tested these changes:

  • Unit tests pass
  • Integration tests pass
  • Manual testing performed
  • Tested on flight controller hardware

Copilot AI review requested due to automatic review settings July 11, 2026 14:21
@amilcarlucas
amilcarlucas marked this pull request as draft July 11, 2026 14:21
@amilcarlucas amilcarlucas added enhancement New feature or request help wanted Extra attention is needed python Pull requests that update Python code needs hardware testing needs testing on a real flight controller hardware labels Jul 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an RC calibration/monitoring plugin to ArduPilot Methodic Configurator, including a new RC data model, a tkinter frontend view, and supporting registration/schema wiring. It also factors out a shared calibration popup base class and refactors the existing compass calibration popup to use it.

Changes:

  • Add RC calibration plugin wiring (plugin constant, plugin registration in __main__.py, schema enum update, and data model factory hookup).
  • Introduce RC calibration UI (frontend_tkinter_rc_calibration.py) and a quadcopter preview renderer stub (renderer_3d_quadcopter.py).
  • Add CalibrationPopupBase and refactor compass calibration popup to inherit from it.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
ardupilot_methodic_configurator/renderer_3d_quadcopter.py New renderer stub used by the RC calibration UI preview.
ardupilot_methodic_configurator/plugin_constants.py Adds PLUGIN_RC_CALIBRATION.
ardupilot_methodic_configurator/frontend_tkinter_rc_calibration.py New RC calibration plugin UI (embedded view + popup + dev window).
ardupilot_methodic_configurator/frontend_tkinter_compass_calibration.py Refactors compass popup to use the new shared popup base.
ardupilot_methodic_configurator/frontend_tkinter_calibration_popup_base.py New shared base class for calibration popups.
ardupilot_methodic_configurator/data_model_rc_calibration.py New RC calibration data model using MAVLink RC telemetry and parameter writes.
ardupilot_methodic_configurator/data_model_parameter_editor.py Wires RC calibration data model creation into plugin model factory.
ardupilot_methodic_configurator/configuration_steps_schema.json Extends plugin name enum to include rc_calibration.
ardupilot_methodic_configurator/__main__.py Registers the RC calibration plugin at startup.
ARCHITECTURE_rc_calibration.md Adds architecture doc for the new plugin.

Comment on lines +1 to +6
"""
3D Quadcopter Renderer using PyOpenGL.

This module provides a class to render a simple 3D quadcopter model
based on roll, pitch, yaw, and throttle inputs.

Comment on lines +31 to +36
Args:
roll: Roll angle in degrees.
pitch: Pitch angle in degrees.
yaw: Yaw angle in degrees.
throttle: Throttle value (0.0 to 1.0).

Comment on lines +46 to +47
self.flight_mode: str = "No Data"
self.channels: list[dict] = []
Comment on lines +85 to +87
ttk.Label(mode_frame, text=_("Flight Mode:"), font=("TkDefaultFont", 11, "bold")).pack(side="left")
self.mode_label = ttk.Label(mode_frame, text="No Data", font=("TkDefaultFont", 11))
self.mode_label.pack(side="left", padx=10)
Comment on lines +112 to +114
# Update Flight Mode
self.flight_mode = telemetry.get("flight_mode", "No Data")
self.mode_label.configure(text=self.flight_mode)
Comment on lines +37 to +39
if not self.is_connected():
error_msg = _("Not connected to flight controller")
return False, error_msg
Comment on lines +65 to +71
for ch_idx, min_val in self._channel_min.items():
ch_num = ch_idx + 1 # convert to 1-based RC channel number
max_val = self._channel_max.get(ch_idx, _RC_CENTER_PWM * 2 - min_val)
trim_val = (min_val + max_val) // 2
self.flight_controller.set_param(f"RC{ch_num}_MIN", float(min_val))
self.flight_controller.set_param(f"RC{ch_num}_MAX", float(max_val))
self.flight_controller.set_param(f"RC{ch_num}_TRIM", float(trim_val))
Comment on lines +100 to +103
try:
rc_msg = master.recv_match( # pyright: ignore[reportAttributeAccessIssue]
type="RC_CHANNELS", blocking=False
)
Comment on lines +22 to +29
class RCCalibrationDataModel:
"""Data model for RC calibration, backed by MAVLink RC_CHANNELS telemetry."""

def __init__(self, flight_controller: FlightController) -> None:
self.flight_controller = flight_controller
self._is_calibrating = False
self._channel_min: dict[int, int] = {} # 0-based channel index → observed minimum PWM
self._channel_max: dict[int, int] = {} # 0-based channel index → observed maximum PWM
Comment on lines +26 to +27
> **Note:** The data model is currently a stub that returns dummy data.
> MAVLink integration (`RC_CHANNELS` / `HEARTBEAT` messages) is planned for a future commit.
@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
14947 13724 92% 89% 🟢

New Files

File Coverage Status
ardupilot_methodic_configurator/data_model_rc_calibration.py 100% 🟢
ardupilot_methodic_configurator/frontend_tkinter_calibration_popup_base.py 43% 🟢
ardupilot_methodic_configurator/frontend_tkinter_rc_calibration.py 16% 🟢
ardupilot_methodic_configurator/renderer_3d_quadcopter.py 29% 🟢
TOTAL 47% 🟢

Modified Files

File Coverage Status
ardupilot_methodic_configurator/main.py 89% 🟢
ardupilot_methodic_configurator/backend_flightcontroller.py 93% 🟢
ardupilot_methodic_configurator/backend_flightcontroller_commands.py 91% 🟢
ardupilot_methodic_configurator/backend_flightcontroller_protocols.py 100% 🟢
ardupilot_methodic_configurator/data_model_accelerometer_calibration.py 75% 🟢
ardupilot_methodic_configurator/data_model_parameter_editor.py 83% 🟢
ardupilot_methodic_configurator/frontend_tkinter_accelerometer_calibration.py 91% 🟢
ardupilot_methodic_configurator/frontend_tkinter_base_window.py 96% 🟢
ardupilot_methodic_configurator/frontend_tkinter_compass_calibration.py 67% 🟢
ardupilot_methodic_configurator/plugin_constants.py 100% 🟢
TOTAL 89% 🟢

updated for commit: 44aed60 by action🐍

@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Test Results

     4 files  ±  0       4 suites  ±0   45m 29s ⏱️ - 4m 7s
 4 454 tests + 25   4 423 ✅ +1   7 💤 ±0  24 ❌ +24 
17 602 runs  +100  17 461 ✅ +5  45 💤  - 1  96 ❌ +96 

For more details on these failures, see this check.

Results for commit 44aed60. ± Comparison against base commit a4ed796.

♻️ This comment has been updated with latest results.

@theraihanrakibb theraihanrakibb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid foundation for the RC calibration plugin — thanks for the thorough architecture doc and the clean Model-View split. A few things worth addressing:

1. Doc/code contradiction (please fix): ARCHITECTURE_rc_calibration.md states several times "The data model is currently a stub that returns dummy data," but data_model_rc_calibration.py's get_rc_telemetry() actually reads real MAVLink RC_CHANNELS/HEARTBEAT via recv_match. Update the doc to match the code (or vice-versa).

2. grab_set() makes the live monitor modal (UX bug): CalibrationPopupBase.__init__ calls grab_set(), which captures all keyboard/mouse focus. That's correct for the compass calibration popup, but RCCalibrationPopup is meant to be a floating live monitor the user watches while using the rest of AMC. With grab_set() they can't interact with the main window. Override/disable grab_set in the RC subclass (or make the base non-modal and let compass opt in).

3. finish_calibration() trim heuristic: when a channel recorded a min but no max, max is synthesized as 3000 - min (assumes center 1500). Reasonable fallback, but add a comment so it isn't mistaken for a measured value later.

4. WIP vs CI: the status table shows many untested/unimplemented items, and the repo checklist wants BDD pytest — none are added for the new plugin. Either add a smoke test or open this as a draft PR so main-branch gating isn't a surprise.

5. Verify wiring: register_rc_calibration_plugin() and create_plugin_data_model() are wired, but I didn't see RCCalibrationView instantiated in plugin_factory/editor — confirm the embedded view actually mounts.

Nice refactor extracting CalibrationPopupBase (the compass popup drops ~80 duplicated lines).

@iacker

iacker commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Software test suite for RCCalibrationDataModel (offer to help your WIP)

Hi @amilcarlucas — saw #1816 is WIP and flagged as "needs a lot more testing". I focused on the part I can validate 100% without flight controller hardware: data_model_rc_calibration.py is pure logic with all MAVLink I/O behind FlightController, so it's fully mockable.

I added 25 hardware-free unit tests (mocked FC, same BDD Given-When-Then style as test_data_model_accelerometer_calibration.py). Coverage of data_model_rc_calibration.py: 99% (all statements, 25/26 branches — the one uncovered branch is a defensive loop edge).

Branch: iacker:test/rc-calibration-data-model, based on your RC_Calibration branch so it drops straight into the WIP. PR opened against your branch → #1819

What's covered

  • connection state (connected / disconnected)
  • start_calibration refusal when disconnected, stale min/max clearing
  • cancel_calibration writes nothing to the FC
  • finish_calibration: RCn_MIN/MAX/TRIM writes, midpoint trim, 0-based→1-based channel mapping, state cleanup
  • RC_CHANNELS telemetry: axis mapping, invalid-PWM sentinel (65535) handling, chancount bound, swallowed recv_match exceptions, HEARTBEAT flight_mode
  • min/max tracking active only while calibrating

One thing the tests surfaced (not a blocker, your call)

finish_calibration line 67:

max_val = self._channel_max.get(ch_idx, _RC_CENTER_PWM * 2 - min_val)

Because min and max are always updated together in get_rc_telemetry (lines 141-142), a channel present in _channel_min is always present in _channel_max too, so the symmetric fallback never triggers in practice. It's harmless, but if you intend it as a real safety net you may want to cover the "only one extreme seen" case explicitly — my test test_finish_uses_symmetric_fallback_when_max_missing pins the current behaviour either way.

Zero hardware claims here — this is purely the software data model. Manual/HW testing of the actual RC calibration flow is all yours. Happy to adjust naming/structure to your preference.

@OmkarSarkar204

OmkarSarkar204 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

@amilcarlucas, should we introduce the 3d model here? We can use the same model here to check the rc inputs in the 3d popup itself(as discussed), we can have the input bars on the sides along with the drone in the centre and then can be scrolled for additional options?

Also is an invert control option good or should it be done on the TX only?

@volkkov volkkov left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good improvement overall. One edge case: empty string vs null aren't treated the same downstream — might need a normalization step.

amilcarlucas and others added 6 commits July 15, 2026 19:48
Signed-off-by: Dr.-Ing. Amilcar do Carmo Lucas <amilcar.lucas@iav.de>
Adds 25 hardware-free unit tests for data_model_rc_calibration.py using a
mocked FlightController (MagicMock), following the BDD Given-When-Then style
already used in test_data_model_accelerometer_calibration.py.

Coverage of data_model_rc_calibration.py: 99% (all statements, 25/26 branches).

Covered behaviours:
- connection state (connected / disconnected)
- start/cancel calibration and stale-state clearing
- finish_calibration: RCn_MIN/MAX/TRIM writes, midpoint trim, symmetric
  max fallback, 0-based to 1-based channel mapping, state cleanup
- RC_CHANNELS telemetry: axis mapping, invalid-PWM sentinel handling,
  chancount bound, swallowed recv_match exceptions, HEARTBEAT flight_mode
- min/max tracking active only while calibrating

No hardware required: all MAVLink I/O is mocked.

Signed-off-by: Billard <82095453+iacker@users.noreply.github.com>
Removes the duplicated window-centering block (R0801) shared between
CalibrationPopupBase and CompassCalibrationInstructionsPopup. Both now
call a single module-level center_over_parent() helper. Behavior
unchanged; fixes the pylint 3.14 duplicate-code failure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request help wanted Extra attention is needed needs hardware testing needs testing on a real flight controller hardware python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants