mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-25 16:56:25 +08:00
docs(docs): Eithernet and voltage ratings
This commit is contained in:
@@ -14,7 +14,9 @@ inserted into `docs/en/flight_controller/*.md` docs. Sections generated:
|
||||
- `## Radio Control` — RC input protocols and ports
|
||||
- `## GPS & Compass` — GPS/safety connector info
|
||||
- `## Power` — power input ports and monitor type
|
||||
- `## Voltage Ratings` — per-port operating/absolute voltage ranges, servo rail, voltage monitoring note
|
||||
- `## Telemetry Radios` — TELEM port listing
|
||||
- `## Ethernet` — speed, transformerless flag, and link to setup guide (boards with `CONFIG_BOARD_ETHERNET=y`)
|
||||
- `## SD Card` — presence/absence
|
||||
|
||||
## File layout
|
||||
@@ -144,8 +146,22 @@ Tests in `test_generators.py` use the `snapshot` fixture from `conftest.py`.
|
||||
hand-written: it is preserved and the proposed content is appended as a
|
||||
`<!-- section_key-proposed -->` comment, with a console warning.
|
||||
|
||||
- **`parse_power_config()` detects INA228** — `CONFIG_DRIVERS_POWER_MONITOR_INA238=y` takes
|
||||
precedence, then `INA228`, then `INA226`. Old code silently promoted INA228 boards to INA226.
|
||||
|
||||
- **Wizard** — `--new-doc` runs an interactive prompts session and caches
|
||||
answers to `metadata/<stem>_wizard.json` for future re-use.
|
||||
Key wizard fields and their shapes:
|
||||
- `ethernet_wizard`: `{"port_label": str|null, "speed_mbps": str, "transformerless": bool}` — only
|
||||
prompted when `CONFIG_BOARD_ETHERNET=y`; `port_label` is rendered as inline code (backticks) in the
|
||||
generated section. `apply_sections_to_docs()` bootstraps this from the wizard JSON on first run
|
||||
when the doc's `<!-- wizard-data -->` block doesn't yet contain it.
|
||||
- `power_ports_wizard[].normal_min_v` / `normal_max_v` / `absolute_max_v` (str|null) — per-port
|
||||
voltage ranges for the `## Voltage Ratings` section. Old wizard JSONs without these keys are safe;
|
||||
missing values produce `TODO` placeholders in the generated section.
|
||||
- `overview_wizard.servo_rail_absolute_max_v` (str|null) — undamaged absolute maximum for the servo
|
||||
rail (e.g. `"42"` for Pixhawk-standard boards). Distinct from `servo_rail_max_v` (normal operating
|
||||
max used in Specifications). Missing → `TODO` placeholder in Voltage Ratings.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"manufacturer": "CUAV",
|
||||
"product": "x25 Super",
|
||||
"board": "cuav/x25-super",
|
||||
"fmu_version": null,
|
||||
"since_version": "v1.18",
|
||||
"manufacturer_url": "https://store.cuav.net/",
|
||||
"rc_ports_wizard": [
|
||||
{
|
||||
"label": "RC IN",
|
||||
"side": "FMU"
|
||||
},
|
||||
{
|
||||
"label": "PPMNOTFOUND",
|
||||
"side": "FMU",
|
||||
"ppm_only": true
|
||||
}
|
||||
],
|
||||
"gps_ports_wizard": [
|
||||
{
|
||||
"port_key": "GPS1",
|
||||
"label": "GPS&SAFETY",
|
||||
"pixhawk_standard": true,
|
||||
"full_port": true
|
||||
},
|
||||
{
|
||||
"port_key": "GPS2",
|
||||
"label": "GPS2",
|
||||
"pixhawk_standard": true,
|
||||
"full_port": false
|
||||
}
|
||||
],
|
||||
"power_ports_wizard": [
|
||||
{
|
||||
"label": "POWER C1",
|
||||
"connector_type": "8-pin JST GH",
|
||||
"monitor_type": "dronecan",
|
||||
"normal_min_v": "10",
|
||||
"normal_max_v": "18",
|
||||
"absolute_max_v": null
|
||||
},
|
||||
{
|
||||
"label": "POWER C2",
|
||||
"connector_type": "8-pin JST GH",
|
||||
"monitor_type": "dronecan",
|
||||
"normal_min_v": "10",
|
||||
"normal_max_v": "18",
|
||||
"absolute_max_v": null
|
||||
}
|
||||
],
|
||||
"overview_wizard": {
|
||||
"imu": [
|
||||
"SCH16T",
|
||||
"IIM42653",
|
||||
"IIM42652"
|
||||
],
|
||||
"baro": [
|
||||
"BMP581",
|
||||
"ICP-20100"
|
||||
],
|
||||
"mag": [
|
||||
"RM3100"
|
||||
],
|
||||
"osd": null,
|
||||
"width_mm": "45.45",
|
||||
"length_mm": "76.5",
|
||||
"height_mm": "38.7",
|
||||
"weight_g": null,
|
||||
"min_voltage": "10",
|
||||
"max_voltage": "18",
|
||||
"usb_powers_fc": true,
|
||||
"usb_pwr_min_v": "4.75",
|
||||
"usb_pwr_max_v": "5.25",
|
||||
"has_servo_rail": true,
|
||||
"servo_rail_max_v": "9.9",
|
||||
"servo_rail_absolute_max_v": null,
|
||||
"usb_connectors": [
|
||||
"USB-C"
|
||||
],
|
||||
"usb_labels": [
|
||||
"USB"
|
||||
],
|
||||
"num_additional_adc_inputs": null,
|
||||
"sensor_variant_labels": null
|
||||
},
|
||||
"i2c_buses_wizard": [
|
||||
{
|
||||
"bus_num": 1,
|
||||
"label": "I2C1"
|
||||
},
|
||||
{
|
||||
"bus_num": 2,
|
||||
"label": "I2C2"
|
||||
},
|
||||
{
|
||||
"bus_num": 3,
|
||||
"label": "I2C3"
|
||||
}
|
||||
],
|
||||
"ethernet_wizard": {
|
||||
"port_label": "ETH",
|
||||
"speed_mbps": "100",
|
||||
"transformerless": true
|
||||
},
|
||||
"extra_port_labels": [
|
||||
"TELEM1",
|
||||
"TELEM2",
|
||||
"CAN1",
|
||||
"CAN2",
|
||||
"RSSI",
|
||||
"SPI6",
|
||||
"UART4",
|
||||
"ADC3V3",
|
||||
"ADC6V6",
|
||||
"DEBUG (DSU)",
|
||||
"M1-M16"
|
||||
]
|
||||
}
|
||||
@@ -15,7 +15,10 @@
|
||||
"power_ports_wizard": [
|
||||
{
|
||||
"label": "GPS1",
|
||||
"connector_type": "n"
|
||||
"connector_type": "n",
|
||||
"normal_min_v": null,
|
||||
"normal_max_v": null,
|
||||
"absolute_max_v": null
|
||||
}
|
||||
],
|
||||
"overview_wizard": {
|
||||
@@ -34,6 +37,7 @@
|
||||
"usb_pwr_max_v": null,
|
||||
"has_servo_rail": true,
|
||||
"servo_rail_max_v": null,
|
||||
"servo_rail_absolute_max_v": null,
|
||||
"usb_connectors": null,
|
||||
"usb_labels": [
|
||||
"USB"
|
||||
@@ -41,5 +45,7 @@
|
||||
"num_additional_adc_inputs": null,
|
||||
"sensor_variant_labels": null
|
||||
},
|
||||
"i2c_buses_wizard": null
|
||||
"i2c_buses_wizard": null,
|
||||
"ethernet_wizard": null,
|
||||
"extra_port_labels": null
|
||||
}
|
||||
@@ -15,7 +15,10 @@
|
||||
"power_ports_wizard": [
|
||||
{
|
||||
"label": "RC",
|
||||
"connector_type": "n"
|
||||
"connector_type": "n",
|
||||
"normal_min_v": "GPS1",
|
||||
"normal_max_v": "n",
|
||||
"absolute_max_v": null
|
||||
}
|
||||
],
|
||||
"overview_wizard": {
|
||||
@@ -28,7 +31,7 @@
|
||||
"ICP-20100"
|
||||
],
|
||||
"mag": null,
|
||||
"osd": null,
|
||||
"osd": "OSD",
|
||||
"width_mm": null,
|
||||
"length_mm": null,
|
||||
"height_mm": null,
|
||||
@@ -40,6 +43,7 @@
|
||||
"usb_pwr_max_v": null,
|
||||
"has_servo_rail": true,
|
||||
"servo_rail_max_v": null,
|
||||
"servo_rail_absolute_max_v": null,
|
||||
"usb_connectors": null,
|
||||
"usb_labels": [
|
||||
"USB"
|
||||
@@ -56,5 +60,7 @@
|
||||
"bus_num": 2,
|
||||
"label": "n"
|
||||
}
|
||||
]
|
||||
],
|
||||
"ethernet_wizard": null,
|
||||
"extra_port_labels": null
|
||||
}
|
||||
+1
@@ -3,3 +3,4 @@ CONFIG_BOARD_SERIAL_TEL2="/dev/ttyS1"
|
||||
CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS3"
|
||||
CONFIG_BOARD_SERIAL_RC="/dev/ttyS4"
|
||||
CONFIG_DRIVERS_RC_INPUT=y
|
||||
CONFIG_BOARD_ETHERNET=y
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
## Ethernet {#ethernet}
|
||||
|
||||
The `ETH` port provides 100Mbps wired network connectivity (transformerless application), enabling high-speed communication with companion computers and other network-capable devices.
|
||||
|
||||
See [PX4 Ethernet Setup](../advanced_config/ethernet_setup.md) for setup and configuration information.
|
||||
|
||||
<!-- ethernet-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"has_ethernet": true
|
||||
},
|
||||
"ethernet_wizard": {
|
||||
"port_label": "ETH",
|
||||
"speed_mbps": "100",
|
||||
"transformerless": true
|
||||
}
|
||||
}
|
||||
-->
|
||||
@@ -0,0 +1,19 @@
|
||||
## Ethernet {#ethernet}
|
||||
|
||||
The `ETH` port provides 1000Mbps wired network connectivity, enabling high-speed communication with companion computers and other network-capable devices.
|
||||
|
||||
See [PX4 Ethernet Setup](../advanced_config/ethernet_setup.md) for setup and configuration information.
|
||||
|
||||
<!-- ethernet-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"has_ethernet": true
|
||||
},
|
||||
"ethernet_wizard": {
|
||||
"port_label": "ETH",
|
||||
"speed_mbps": "1000",
|
||||
"transformerless": false
|
||||
}
|
||||
}
|
||||
-->
|
||||
@@ -0,0 +1,19 @@
|
||||
## Ethernet {#ethernet}
|
||||
|
||||
The Ethernet port provides 100Mbps wired network connectivity (transformerless application), enabling high-speed communication with companion computers and other network-capable devices.
|
||||
|
||||
See [PX4 Ethernet Setup](../advanced_config/ethernet_setup.md) for setup and configuration information.
|
||||
|
||||
<!-- ethernet-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"has_ethernet": true
|
||||
},
|
||||
"ethernet_wizard": {
|
||||
"port_label": null,
|
||||
"speed_mbps": "100",
|
||||
"transformerless": true
|
||||
}
|
||||
}
|
||||
-->
|
||||
@@ -0,0 +1,15 @@
|
||||
## Ethernet {#ethernet}
|
||||
|
||||
The Ethernet port provides 100Mbps wired network connectivity (transformerless application), enabling high-speed communication with companion computers and other network-capable devices.
|
||||
|
||||
See [PX4 Ethernet Setup](../advanced_config/ethernet_setup.md) for setup and configuration information.
|
||||
|
||||
<!-- ethernet-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"has_ethernet": true
|
||||
},
|
||||
"ethernet_wizard": {}
|
||||
}
|
||||
-->
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
The flight controller can be powered from a power module connected to the **POWER** port.
|
||||
|
||||
The power module must supply a regulated **5V** at a minimum of **3A continuous**.
|
||||
|
||||
Power ports:
|
||||
|
||||
- `POWER`: 6-pin JST GH — DroneCAN battery monitoring.
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
## Voltage Ratings {#voltage_ratings}
|
||||
|
||||
_Durandal_ is 1-way-redundant on the power supply if 1 power sources are supplied.
|
||||
The 1 power rail is: **POWER**.
|
||||
|
||||
**Normal Operation Maximum Ratings**
|
||||
|
||||
Under these conditions all power sources will be used in this order to power the system:
|
||||
|
||||
1. **POWER** input (4.9V to 5.5V)
|
||||
|
||||
**Absolute Maximum Ratings**
|
||||
|
||||
Under these conditions the system will not draw any power (will not be operational), but will remain intact.
|
||||
|
||||
1. **POWER** input (operational range 4.9V to 5.5V, 0V to 6V undamaged)
|
||||
1. **Servo input:** `VDD_SERVO` pin of **FMU PWM OUT** (0V to TODO undamaged)
|
||||
|
||||
**Voltage monitoring**
|
||||
|
||||
Analog battery monitoring via ADC is enabled by default.
|
||||
|
||||
<!-- voltage-ratings-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"num_power_inputs": 1,
|
||||
"has_redundant_power": false,
|
||||
"power_monitor_type": "analog"
|
||||
},
|
||||
"overview_wizard": {
|
||||
"min_voltage": null,
|
||||
"max_voltage": null,
|
||||
"usb_pwr_min_v": null,
|
||||
"usb_pwr_max_v": null,
|
||||
"has_servo_rail": true,
|
||||
"servo_rail_absolute_max_v": null
|
||||
},
|
||||
"power_ports_wizard": [
|
||||
{
|
||||
"label": "POWER",
|
||||
"normal_min_v": "4.9",
|
||||
"normal_max_v": "5.5",
|
||||
"absolute_max_v": "6"
|
||||
}
|
||||
]
|
||||
}
|
||||
-->
|
||||
@@ -0,0 +1,58 @@
|
||||
## Voltage Ratings {#voltage_ratings}
|
||||
|
||||
_X25 Super_ can be triple-redundant on the power supply if 3 power sources are supplied.
|
||||
The 3 power rails are: **POWER C1**, **POWER C2** and **USB**.
|
||||
|
||||
**Normal Operation Maximum Ratings**
|
||||
|
||||
Under these conditions all power sources will be used in this order to power the system:
|
||||
|
||||
1. **POWER C1** input (10V to 18V)
|
||||
1. **POWER C2** input (10V to 18V)
|
||||
1. **USB** input (4.75V to 5.25V)
|
||||
|
||||
**Absolute Maximum Ratings**
|
||||
|
||||
Under these conditions the system will not draw any power (will not be operational), but will remain intact.
|
||||
|
||||
1. **POWER C1** input (operational range 10V to 18V, 0V to TODO undamaged)
|
||||
1. **POWER C2** input (operational range 10V to 18V, 0V to TODO undamaged)
|
||||
1. **USB** input (operational range 4.75V to 5.25V, 0V to 6V undamaged)
|
||||
1. **Servo input:** `VDD_SERVO` pin of **FMU PWM OUT** (0V to TODO undamaged)
|
||||
|
||||
**Voltage monitoring**
|
||||
|
||||
Digital DroneCAN/UAVCAN battery monitoring is enabled by default.
|
||||
|
||||
<!-- voltage-ratings-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"num_power_inputs": 2,
|
||||
"has_redundant_power": true,
|
||||
"power_monitor_type": "dronecan"
|
||||
},
|
||||
"overview_wizard": {
|
||||
"min_voltage": null,
|
||||
"max_voltage": null,
|
||||
"usb_pwr_min_v": "4.75",
|
||||
"usb_pwr_max_v": "5.25",
|
||||
"has_servo_rail": true,
|
||||
"servo_rail_absolute_max_v": null
|
||||
},
|
||||
"power_ports_wizard": [
|
||||
{
|
||||
"label": "POWER C1",
|
||||
"normal_min_v": "10",
|
||||
"normal_max_v": "18",
|
||||
"absolute_max_v": null
|
||||
},
|
||||
{
|
||||
"label": "POWER C2",
|
||||
"normal_min_v": "10",
|
||||
"normal_max_v": "18",
|
||||
"absolute_max_v": null
|
||||
}
|
||||
]
|
||||
}
|
||||
-->
|
||||
@@ -0,0 +1,58 @@
|
||||
## Voltage Ratings {#voltage_ratings}
|
||||
|
||||
_Pixhawk 5X_ can be triple-redundant on the power supply if 3 power sources are supplied.
|
||||
The 3 power rails are: **POWER1**, **POWER2** and **USB**.
|
||||
|
||||
**Normal Operation Maximum Ratings**
|
||||
|
||||
Under these conditions all power sources will be used in this order to power the system:
|
||||
|
||||
1. **POWER1** input (4.9V to 5.5V)
|
||||
1. **POWER2** input (4.9V to 5.5V)
|
||||
1. **USB** input (4.75V to 5.25V)
|
||||
|
||||
**Absolute Maximum Ratings**
|
||||
|
||||
Under these conditions the system will not draw any power (will not be operational), but will remain intact.
|
||||
|
||||
1. **POWER1** input (operational range 4.9V to 5.5V, 0V to 10V undamaged)
|
||||
1. **POWER2** input (operational range 4.9V to 5.5V, 0V to 10V undamaged)
|
||||
1. **USB** input (operational range 4.75V to 5.25V, 0V to 6V undamaged)
|
||||
1. **Servo input:** `VDD_SERVO` pin of **FMU PWM OUT** (0V to 42V undamaged)
|
||||
|
||||
**Voltage monitoring**
|
||||
|
||||
Digital I2C battery monitoring is enabled by default.
|
||||
|
||||
<!-- voltage-ratings-source-data
|
||||
{
|
||||
"board": "test/fixture",
|
||||
"source": {
|
||||
"num_power_inputs": 2,
|
||||
"has_redundant_power": true,
|
||||
"power_monitor_type": "ina226"
|
||||
},
|
||||
"overview_wizard": {
|
||||
"min_voltage": null,
|
||||
"max_voltage": null,
|
||||
"usb_pwr_min_v": "4.75",
|
||||
"usb_pwr_max_v": "5.25",
|
||||
"has_servo_rail": true,
|
||||
"servo_rail_absolute_max_v": "42"
|
||||
},
|
||||
"power_ports_wizard": [
|
||||
{
|
||||
"label": "POWER1",
|
||||
"normal_min_v": "4.9",
|
||||
"normal_max_v": "5.5",
|
||||
"absolute_max_v": "10"
|
||||
},
|
||||
{
|
||||
"label": "POWER2",
|
||||
"normal_min_v": "4.9",
|
||||
"normal_max_v": "5.5",
|
||||
"absolute_max_v": "10"
|
||||
}
|
||||
]
|
||||
}
|
||||
-->
|
||||
@@ -573,6 +573,44 @@ class TestGeneratePowerSection:
|
||||
# Mixed board should not claim "redundant power inputs" in the intro
|
||||
assert "redundant power inputs" not in prose.lower()
|
||||
|
||||
def test_pure_dronecan_omits_5v_requirement(self):
|
||||
"""Pure DroneCAN board must NOT say '5V regulated'."""
|
||||
entry = {
|
||||
**self._entry_single(),
|
||||
"power_ports_wizard": [
|
||||
{"label": "POWER C1", "connector_type": "8-pin JST GH",
|
||||
"monitor_type": "dronecan"},
|
||||
],
|
||||
}
|
||||
prose = fcdg.generate_power_section(BOARD_KEY, entry).split("<!-- power-source-data")[0]
|
||||
assert "5V" not in prose
|
||||
|
||||
def test_analog_retains_5v_requirement(self):
|
||||
"""Analog board must keep the 5V/3A line."""
|
||||
entry = {
|
||||
**self._entry_single(),
|
||||
"power_ports_wizard": [
|
||||
{"label": "POWER", "connector_type": "6-pin JST GH",
|
||||
"monitor_type": "analog"},
|
||||
],
|
||||
}
|
||||
prose = fcdg.generate_power_section(BOARD_KEY, entry).split("<!-- power-source-data")[0]
|
||||
assert "5V" in prose
|
||||
|
||||
def test_mixed_dronecan_analog_retains_5v_requirement(self):
|
||||
"""Mixed board (analog + DroneCAN) must keep the 5V/3A line for the analog port."""
|
||||
entry = {
|
||||
**self._entry_dual(),
|
||||
"power_ports_wizard": [
|
||||
{"label": "POWER 1", "connector_type": "6-pin Molex CLIK-Mate",
|
||||
"monitor_type": "analog"},
|
||||
{"label": "POWER C1", "connector_type": "6-pin JST GH",
|
||||
"monitor_type": "dronecan"},
|
||||
],
|
||||
}
|
||||
prose = fcdg.generate_power_section(BOARD_KEY, entry).split("<!-- power-source-data")[0]
|
||||
assert "5V" in prose
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Telemetry section snapshots and structural tests
|
||||
@@ -1160,3 +1198,259 @@ class TestGenerateSpecSectionVariants:
|
||||
result = fcdg.generate_specifications_section(BOARD_KEY, entry)
|
||||
assert "hardware revision Rev 1.0" in result
|
||||
assert "hardware revision Rev 1.1" in result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ethernet section
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestEthernetSection:
|
||||
def test_ethernet_with_label(self, snapshot):
|
||||
entry = {
|
||||
'has_ethernet': True,
|
||||
'ethernet_wizard': {'port_label': 'ETH', 'speed_mbps': '100', 'transformerless': True},
|
||||
}
|
||||
result = fcdg.generate_ethernet_section(BOARD_KEY, entry)
|
||||
snapshot("ethernet_section.md", result)
|
||||
|
||||
def test_ethernet_no_label(self, snapshot):
|
||||
entry = {
|
||||
'has_ethernet': True,
|
||||
'ethernet_wizard': {'port_label': None, 'speed_mbps': '100', 'transformerless': True},
|
||||
}
|
||||
result = fcdg.generate_ethernet_section(BOARD_KEY, entry)
|
||||
snapshot("ethernet_section_no_label.md", result)
|
||||
|
||||
def test_ethernet_not_transformerless(self, snapshot):
|
||||
entry = {
|
||||
'has_ethernet': True,
|
||||
'ethernet_wizard': {'port_label': 'ETH', 'speed_mbps': '1000', 'transformerless': False},
|
||||
}
|
||||
result = fcdg.generate_ethernet_section(BOARD_KEY, entry)
|
||||
snapshot("ethernet_section_gigabit.md", result)
|
||||
|
||||
def test_ethernet_absent(self):
|
||||
assert fcdg.generate_ethernet_section(BOARD_KEY, {'has_ethernet': False}) == ''
|
||||
|
||||
def test_ethernet_no_wizard(self, snapshot):
|
||||
"""Falls back to defaults when ethernet_wizard is absent."""
|
||||
entry = {'has_ethernet': True}
|
||||
result = fcdg.generate_ethernet_section(BOARD_KEY, entry)
|
||||
snapshot("ethernet_section_no_wizard.md", result)
|
||||
|
||||
def test_label_is_code_style(self):
|
||||
entry = {
|
||||
'has_ethernet': True,
|
||||
'ethernet_wizard': {'port_label': 'ETH', 'speed_mbps': '100', 'transformerless': True},
|
||||
}
|
||||
result = fcdg.generate_ethernet_section(BOARD_KEY, entry)
|
||||
assert '`ETH`' in result
|
||||
assert '**ETH**' not in result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Voltage Ratings section
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_VR_DRONECAN_ENTRY = {
|
||||
'product': 'X25 Super',
|
||||
'num_power_inputs': 2,
|
||||
'has_redundant_power': True,
|
||||
'has_dronecan_power_input': True,
|
||||
'power_monitor_type': 'dronecan',
|
||||
'power_ports_wizard': [
|
||||
{'label': 'POWER C1', 'connector_type': '8-pin JST GH', 'monitor_type': 'dronecan',
|
||||
'normal_min_v': '10', 'normal_max_v': '18', 'absolute_max_v': None},
|
||||
{'label': 'POWER C2', 'connector_type': '8-pin JST GH', 'monitor_type': 'dronecan',
|
||||
'normal_min_v': '10', 'normal_max_v': '18', 'absolute_max_v': None},
|
||||
],
|
||||
'overview_wizard': {
|
||||
'usb_powers_fc': True, 'usb_pwr_min_v': '4.75', 'usb_pwr_max_v': '5.25',
|
||||
'has_servo_rail': True, 'servo_rail_max_v': '9.9',
|
||||
'servo_rail_absolute_max_v': None,
|
||||
},
|
||||
}
|
||||
|
||||
_VR_SMBUS_ENTRY = {
|
||||
'product': 'Pixhawk 5X',
|
||||
'num_power_inputs': 2,
|
||||
'has_redundant_power': True,
|
||||
'has_dronecan_power_input': False,
|
||||
'power_monitor_type': 'ina226',
|
||||
'power_ports_wizard': [
|
||||
{'label': 'POWER1', 'connector_type': '6-pin Molex CLIK-Mate',
|
||||
'normal_min_v': '4.9', 'normal_max_v': '5.5', 'absolute_max_v': '10'},
|
||||
{'label': 'POWER2', 'connector_type': '6-pin Molex CLIK-Mate',
|
||||
'normal_min_v': '4.9', 'normal_max_v': '5.5', 'absolute_max_v': '10'},
|
||||
],
|
||||
'overview_wizard': {
|
||||
'usb_powers_fc': True, 'usb_pwr_min_v': '4.75', 'usb_pwr_max_v': '5.25',
|
||||
'has_servo_rail': True, 'servo_rail_max_v': '10',
|
||||
'servo_rail_absolute_max_v': '42',
|
||||
},
|
||||
}
|
||||
|
||||
_VR_ANALOG_ENTRY = {
|
||||
'product': 'Durandal',
|
||||
'num_power_inputs': 1,
|
||||
'has_redundant_power': False,
|
||||
'has_dronecan_power_input': False,
|
||||
'power_monitor_type': 'analog',
|
||||
'power_ports_wizard': [
|
||||
{'label': 'POWER', 'connector_type': '6-pin JST GH',
|
||||
'normal_min_v': '4.9', 'normal_max_v': '5.5', 'absolute_max_v': '6'},
|
||||
],
|
||||
'overview_wizard': {
|
||||
'usb_powers_fc': False,
|
||||
'has_servo_rail': True, 'servo_rail_max_v': '9.9',
|
||||
'servo_rail_absolute_max_v': None,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestVoltageRatingsSection:
|
||||
def test_dronecan_dual(self, snapshot):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_DRONECAN_ENTRY)
|
||||
snapshot("voltage_ratings_dronecan.md", result)
|
||||
|
||||
def test_smbus_dual(self, snapshot):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
snapshot("voltage_ratings_smbus.md", result)
|
||||
|
||||
def test_analog_single(self, snapshot):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_ANALOG_ENTRY)
|
||||
snapshot("voltage_ratings_analog.md", result)
|
||||
|
||||
def test_heading_present(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '## Voltage Ratings {#voltage_ratings}' in result
|
||||
|
||||
def test_normal_operation_section_present(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '**Normal Operation Maximum Ratings**' in result
|
||||
|
||||
def test_absolute_maximum_section_present(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '**Absolute Maximum Ratings**' in result
|
||||
|
||||
def test_voltage_monitoring_section_present(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '**Voltage monitoring**' in result
|
||||
|
||||
def test_dronecan_monitoring_text(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_DRONECAN_ENTRY)
|
||||
assert 'DroneCAN/UAVCAN battery monitoring' in result
|
||||
|
||||
def test_i2c_monitoring_text(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert 'Digital I2C battery monitoring' in result
|
||||
|
||||
def test_analog_monitoring_text(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_ANALOG_ENTRY)
|
||||
assert 'Analog battery monitoring' in result
|
||||
|
||||
def test_absolute_max_v_shown(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '0V to 10V undamaged' in result
|
||||
|
||||
def test_servo_rail_absolute_max_shown(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '0V to 42V undamaged' in result
|
||||
|
||||
def test_servo_rail_todo_when_absent(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_DRONECAN_ENTRY)
|
||||
assert '0V to TODO undamaged' in result
|
||||
|
||||
def test_source_comment_present(self):
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, _VR_SMBUS_ENTRY)
|
||||
assert '<!-- voltage-ratings-source-data' in result
|
||||
|
||||
def test_no_power_ports_uses_fallback(self):
|
||||
entry = {
|
||||
'product': 'Test Board',
|
||||
'num_power_inputs': 1,
|
||||
'has_redundant_power': False,
|
||||
'power_monitor_type': 'analog',
|
||||
'power_ports_wizard': None,
|
||||
'overview_wizard': {'usb_powers_fc': False, 'has_servo_rail': False},
|
||||
}
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, entry)
|
||||
assert '## Voltage Ratings' in result
|
||||
assert 'TODO' in result
|
||||
|
||||
def test_mixed_dronecan_i2c_monitoring_note(self):
|
||||
"""Mixed DroneCAN + I2C ports produce a combined monitoring note."""
|
||||
entry = {
|
||||
**_VR_SMBUS_ENTRY,
|
||||
'power_ports_wizard': [
|
||||
{'label': 'POWER 1', 'normal_min_v': '4.9', 'normal_max_v': '5.5',
|
||||
'absolute_max_v': '10', 'monitor_type': 'i2c'},
|
||||
{'label': 'POWER C1', 'normal_min_v': '10', 'normal_max_v': '18',
|
||||
'absolute_max_v': None, 'monitor_type': 'dronecan'},
|
||||
],
|
||||
}
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, entry)
|
||||
assert 'DroneCAN/UAVCAN and I2C battery monitoring' in result
|
||||
|
||||
def test_per_port_monitor_type_overrides_board_level(self):
|
||||
"""Per-port monitor_type takes priority over board-level power_monitor_type."""
|
||||
entry = {
|
||||
**_VR_SMBUS_ENTRY,
|
||||
'power_monitor_type': 'ina226', # board-level says I2C
|
||||
'power_ports_wizard': [
|
||||
{'label': 'POWER C1', 'normal_min_v': '10', 'normal_max_v': '18',
|
||||
'absolute_max_v': None, 'monitor_type': 'dronecan'}, # port says DroneCAN
|
||||
],
|
||||
}
|
||||
result = fcdg.generate_voltage_ratings_section(BOARD_KEY, entry)
|
||||
# Per-port dronecan wins over board-level ina226
|
||||
assert 'DroneCAN/UAVCAN battery monitoring' in result
|
||||
assert 'I2C battery monitoring' not in result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _monitoring_note helper
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestMonitoringNote:
|
||||
def test_pure_dronecan(self):
|
||||
ports = [{'monitor_type': 'dronecan'}, {'monitor_type': 'dronecan'}]
|
||||
assert fcdg._monitoring_note(ports, None) == \
|
||||
'Digital DroneCAN/UAVCAN battery monitoring is enabled by default.'
|
||||
|
||||
def test_pure_i2c_ina226(self):
|
||||
ports = [{'monitor_type': 'ina226'}]
|
||||
assert fcdg._monitoring_note(ports, None) == \
|
||||
'Digital I2C battery monitoring is enabled by default.'
|
||||
|
||||
def test_pure_i2c_ltc44xx(self):
|
||||
ports = [{'monitor_type': 'ltc44xx'}]
|
||||
assert fcdg._monitoring_note(ports, None) == \
|
||||
'Digital I2C battery monitoring is enabled by default.'
|
||||
|
||||
def test_pure_analog(self):
|
||||
ports = [{'monitor_type': 'analog'}]
|
||||
assert fcdg._monitoring_note(ports, None) == \
|
||||
'Analog battery monitoring via ADC is enabled by default.'
|
||||
|
||||
def test_mixed_dronecan_i2c(self):
|
||||
ports = [{'monitor_type': 'dronecan'}, {'monitor_type': 'ina226'}]
|
||||
assert fcdg._monitoring_note(ports, None) == \
|
||||
'Digital DroneCAN/UAVCAN and I2C battery monitoring are enabled by default.'
|
||||
|
||||
def test_mixed_dronecan_analog(self):
|
||||
ports = [{'monitor_type': 'dronecan'}, {'monitor_type': 'analog'}]
|
||||
assert fcdg._monitoring_note(ports, None) == \
|
||||
'Digital DroneCAN/UAVCAN and analog ADC battery monitoring are enabled by default.'
|
||||
|
||||
def test_fallback_to_board_level_when_no_port_types(self):
|
||||
ports = [{'label': 'POWER', 'connector_type': '6-pin JST GH'}] # no monitor_type
|
||||
assert fcdg._monitoring_note(ports, 'ina238') == \
|
||||
'Digital I2C battery monitoring is enabled by default.'
|
||||
|
||||
def test_fallback_dronecan_board_level(self):
|
||||
assert fcdg._monitoring_note([], 'dronecan') == \
|
||||
'Digital DroneCAN/UAVCAN battery monitoring is enabled by default.'
|
||||
|
||||
def test_no_data_returns_todo(self):
|
||||
assert 'TODO' in fcdg._monitoring_note([], None)
|
||||
|
||||
@@ -379,6 +379,35 @@ class TestParsePowerConfig:
|
||||
result = fcdg.parse_power_config(board_stm32h7_all_dshot)
|
||||
assert result["has_dronecan_power_input"] is False
|
||||
|
||||
def test_ina228_detected(self, tmp_path):
|
||||
"""INA228-only board must not fall through to the INA226 fallback."""
|
||||
src = tmp_path / "src"
|
||||
src.mkdir()
|
||||
(src / "board_config.h").write_text(
|
||||
"#define BOARD_NUMBER_DIGITAL_BRICKS 1\n"
|
||||
"#define BOARD_HAS_NBAT_V 1d\n"
|
||||
)
|
||||
(tmp_path / "default.px4board").write_text(
|
||||
"CONFIG_DRIVERS_POWER_MONITOR_INA228=y\n"
|
||||
)
|
||||
result = fcdg.parse_power_config(tmp_path)
|
||||
assert result["power_monitor_type"] == "ina228"
|
||||
|
||||
def test_ina238_takes_precedence_over_ina228(self, tmp_path):
|
||||
"""When both INA238 and INA228 are enabled, INA238 wins."""
|
||||
src = tmp_path / "src"
|
||||
src.mkdir()
|
||||
(src / "board_config.h").write_text(
|
||||
"#define BOARD_NUMBER_DIGITAL_BRICKS 1\n"
|
||||
"#define BOARD_HAS_NBAT_V 1d\n"
|
||||
)
|
||||
(tmp_path / "default.px4board").write_text(
|
||||
"CONFIG_DRIVERS_POWER_MONITOR_INA238=y\n"
|
||||
"CONFIG_DRIVERS_POWER_MONITOR_INA228=y\n"
|
||||
)
|
||||
result = fcdg.parse_power_config(tmp_path)
|
||||
assert result["power_monitor_type"] == "ina238"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_sd_card_config
|
||||
|
||||
@@ -16,6 +16,7 @@ def _make_args(**kwargs):
|
||||
"rc_ports_wizard": None,
|
||||
"gps_ports_wizard": None,
|
||||
"power_ports_wizard": None,
|
||||
"extra_port_labels": None,
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
return argparse.Namespace(**defaults)
|
||||
@@ -180,6 +181,59 @@ class TestRunWizard:
|
||||
fcdg._run_wizard(args)
|
||||
assert args.since_version == "v1.19"
|
||||
|
||||
def test_extra_port_labels_stored(self, monkeypatch):
|
||||
"""Extra port labels entered at the final step are stored in args.extra_port_labels."""
|
||||
board_data = {
|
||||
"test/board": {
|
||||
"has_rc_input": True, "has_common_rc": False,
|
||||
"rc_serial_device": "/dev/ttyS4",
|
||||
"has_ppm_pin": False, "ppm_shared_with_rc_serial": False,
|
||||
"has_pps_capture": False, "has_safety_switch": False, "has_safety_led": False,
|
||||
"serial_ports": [
|
||||
{"uart": "USART1", "device": "/dev/ttyS0", "label": "TELEM1"},
|
||||
],
|
||||
"num_power_inputs": 1,
|
||||
}
|
||||
}
|
||||
prompts_responses = {
|
||||
"Manufacturer name": "TestMfr",
|
||||
"Product name": "TestBoard",
|
||||
"Board key (e.g. holybro/kakuteh7 or px4/fmu-v6x)": "test/board",
|
||||
"FMU version suffix (e.g. fmu-v6x, optional)": "",
|
||||
"PX4 version introduced in": "v1.18",
|
||||
"Serial RC port label as printed on board": "RC",
|
||||
" POWER port label as printed on board": "POWER",
|
||||
" Connector type for POWER (e.g. 6-pin JST GH, 6-pin Molex CLIK-Mate)": "6-pin JST GH",
|
||||
"Manufacturer URL": "https://example.com/",
|
||||
}
|
||||
extra_responses = iter(["CAN1", "CAN2", ""]) # two extra labels then stop
|
||||
|
||||
def mock_prompt(label, default="", required=False):
|
||||
if label.startswith(" extra port label"):
|
||||
return next(extra_responses, "")
|
||||
return prompts_responses.get(label, default)
|
||||
|
||||
monkeypatch.setattr(fcdg, "_wizard_prompt", mock_prompt)
|
||||
monkeypatch.setattr(fcdg, "gather_board_data", lambda: board_data)
|
||||
monkeypatch.setattr(fcdg, "_load_wizard_cache", lambda *a, **kw: None)
|
||||
monkeypatch.setattr(fcdg, "_save_wizard_cache", lambda *a, **kw: None)
|
||||
|
||||
args = _make_args()
|
||||
fcdg._run_wizard(args)
|
||||
assert args.extra_port_labels == ["CAN1", "CAN2"]
|
||||
|
||||
def test_extra_port_labels_none_when_no_input(self, monkeypatch):
|
||||
"""extra_port_labels is None when the user enters nothing extra."""
|
||||
self._patch(monkeypatch, [
|
||||
"TestMfr", "TestBoard", "",
|
||||
"",
|
||||
"v1.18",
|
||||
"RC", "n",
|
||||
])
|
||||
args = _make_args()
|
||||
fcdg._run_wizard(args)
|
||||
assert args.extra_port_labels is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Wizard cache: _save_wizard_cache / _load_wizard_cache
|
||||
|
||||
Reference in New Issue
Block a user