diff --git a/.gitignore b/.gitignore index 5b144e0..7dfe1bd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,26 @@ __pycache__/ *.pyc *.pyo +# Python virtual environments (never commit). The local .venv here is 351 MB of +# third-party packages; a single `git add -A` would push the lot. Nothing under +# it has ever been tracked, so this is a guard rather than a cleanup. +# +# It also matters to CI: `.github/workflows/ci.yml` runs `flake8` with an +# explicit --exclude list that does NOT name .venv, so lint only passes today +# because CI checks out a fresh tree with no virtualenv in it. Were one ever +# committed, the lint job would immediately fail on thousands of findings in +# vendored code. (Locally, `flake8 .` does report those — run it over +# `git ls-files '*.py'` instead to see only this repo's own code.) +# +# NOTE for anyone running the tools: this .venv SHADOWS system Python packages +# the repo's tools rely on (manifold3d, sympy, matplotlib are installed +# system-wide). Invoke tools as `/usr/bin/python3 tools/.py`, not through +# the venv. +.venv/ +venv/ +env/ +ENV/ + # Claude Code agent worktrees and session data .claude/ diff --git a/Browncoats-workspace.code-workspace b/Browncoats-workspace.code-workspace index f0df085..3954a5c 100644 --- a/Browncoats-workspace.code-workspace +++ b/Browncoats-workspace.code-workspace @@ -20,6 +20,9 @@ }, { "path": "../Tactical-WX-RX" + }, + { + "path": "../engineering-pe-skills" } ], "settings": { diff --git a/CLAUDE-MEMORY.md b/CLAUDE-MEMORY.md index a513cd2..5b5a606 100644 --- a/CLAUDE-MEMORY.md +++ b/CLAUDE-MEMORY.md @@ -7,7 +7,7 @@ repo root. Source of truth is Claude's memory directory; this file is a copy. **Generated by `tools/mirror_claude_memory.py` — do not hand-edit.** Edit the memory files themselves, then re-run the tool. -Mirrored: 41 memory files plus the index. +Mirrored: 48 memory files plus the index. --- @@ -37,7 +37,8 @@ Mirrored: 41 memory files plus the index. - [Avionics board folder reorg](project_avionics_board_folder_reorg.md) — `avionics/kicad//{kicads,scripts,gerbers}/` layout (template=Kaylee); HERE-relative script paths need an extra .parent after the move; Kaylee's own gen scripts still have this bug - [Port tilt-spar overlay](project_port_tilt_overlay.md) — port_tilt_spar_assembly.scad integration view; nacelle→hull bake transform; pivot=CG 111.5mm (conforms to canon "Engine Pivots 360°" balance-center); nozzle drive = wing-fixed sync gear + geared bellcrank (spar-crank was kinematically dead); servo −inb/wall bug fixed; iris-asm PHI sign FIXED; superseded gear-train/nozzle STLs archived - [Tilt encoder = AKM AK7455](project_tilt_encoder_ak7455.md) — off-axis SPI sensor; MT6701/AS5600 rejected as on-axis; ENC-NACELLE-1/MAL-TILT-ENC-PCB, REF-SENSOR-008 -- [KiCad hand-authoring gotchas](feedback_kicad_hand_authoring.md) — sheet_y = instance_y − lib_y (Y inverted); no ';' comments; verify by net node count; kicad-cli 9.0.2 +- [KiCad hand-authoring gotchas](feedback_kicad_hand_authoring.md) — SCHEMATIC pin xform = instance+Rot(angle)·(x,−y) at ANY angle; grid-align instance centers; kicad-cli ver must ≥ file's generator_version (use KiCad AppImage's bundled kicad-cli if not) +- [KiCad PCB footprint rotation](feedback_kicad_pcb_footprint_rotation.md) — PCB pad xform is OPPOSITE sign from schematic: board=instance+Rot(−angle)·(x,y'), y'=−y only on B.Cu; verify via Gerber %TO.P% flashes not derivation alone; inner/outer angle desync after GUI moves - [Ground clearance requirement feedback](feedback_ground_clearance_requirement.md) — clearance is aircraft-safety-only, NOT sized to a cargo box's height (aircraft lands over box, winches it in); don't back-derive geometry from payload dimensions without confirming ops concept - [Landing gear Rev R6 leg variants](project_landing_gear_r6_variants.md) — canonical_leg_r6_1_5in.scad (default) / _3_0in.scad (extended, kept) share one bay/foot/wire BOM; same wire hardware reused at different R_h levers → 1.5in runs hotter (81/162g) with tight clearance margin, 3.0in gentler (52/104g); tools/landing_gear_r6_sizing.py - [Fleet trust module rollout](project_fleet_trust_module.md) — 2026-07-26: MCU+SLB9670 TPM+ISOW1044+ISOW1412 stack on new stackable CAN-PERIPH-GW-1 board + Jayne/Kaylee/Emma/Wash/Zoë; ADM2795E→ISOW1412 fleet swap; AK7455 now bus-published not direct-read @@ -49,7 +50,7 @@ Mirrored: 41 memory files plus the index. - [venv hides system Python pkgs](env_venv_hides_system_python.md) — manifold3d/sympy/matplotlib exist but are hidden by .venv; use /usr/bin/python3, never pip (not permitted); no rtree/embree either - [Nozzle flap shingle (Rev T3)](project_nozzle_flap_shingle.md) — 8×50° flaps in ONE radial band interpenetrated (17/12 non-manifold edges, 0 open edges, split()=0 bodies); alternate flaps now seal-lapped 0.2mm outboard; masters unchanged so bore targets hold; print = 4 master + 4 seal; -5deg variant discarded - [Generated index conflicts](project_generated_index_conflicts.md) — PROJECT_INDEX/ARCHIVE_INDEX/index_tags.json conflict on EVERY base merge; resolve by REGENERATING, never by picking a side. PR #183 made the generator deterministic (git-tracked only), so regenerating anywhere is safe and --no-verify is no longer needed -- [LG sponson wells (Rev R6)](project_lg_sponson_wells.md) — gear bays mount on the sponson 25° panel (0.901,0.015,-0.433), NOT the cargo flank; wells at canonical Y -7/+107 need no extension (-26.6 g); 3 silent-bug traps +- [LG sponson wells (Rev R6)](project_lg_sponson_wells.md) — gear bays mount on the sponson 25° panel (0.901,0.015,-0.433), NOT the cargo flank; LG-10 CLOSED — seat is a shared per-station DATUM (4.91/1.85), not a conforming patch; bowed_wire concat bug; zero-area slits - [OPTIGA Trust M swap (ESC + servo)](project_optiga_trust_m_swap.md) — 2026-08-10 SLB9672 TPM → OPTIGA Trust M on both boards; secure element ≠ TPM; 5s protected-op budget keeps it out of the hot path; LibreServo_v2-Sec → LibreServo_v4 - [S32K144 pinout source found](project_s32k144_pinout_source.md) — the pin map is in XLSX files EMBEDDED in S32K-RM.pdf (pdfdetach); unblocks the placeholder pin map; LPI2C0 = PTA2/PTA3 pins 48/47 - [kiutils missing → inject, don't regenerate](env_kiutils_missing.md) — ESC generator scripts can't run; use targeted S-expression injection + kicad-cli validation; paren-walk, don't regex, for lib_symbol ends @@ -57,6 +58,12 @@ Mirrored: 41 memory files plus the index. - [ESC MCU swap → MSPM0G3518-Q1 (PENDING)](project_esc_mcu_swap_mspm0.md) — for AES-256 over CSEc's AES-128; VCORE needs a DEDICATED cap, not a VDDA slot-swap; start from docs/HANDOFF-mcu-swap-s32k144-to-mspm0g3518.md - [Wing spar Rev S1b (35% chord)](project_wing_spar_rev_s1b.md) — spar → 45.15mm/35% root chord; WING_SPAR_Z (68.42, camber midline) ≠ WING_ROOT_Z (62.5, mortise); nacelle servo mount is SPAR-RELATIVE; thickness-only section scaling preserves camber 8.12% vs 11.75% - [OpenFOAM airfoil meshing dead ends](env_openfoam_airfoil_meshing.md) — v1912 installed, source /usr/share/openfoam/etc/bashrc; hand-rolled blockMesh O-grid on S1223 floors at 2-4 inverted cells; six approaches recorded, use elliptic/C-grid/snappy instead +- [Board call-sign rename](project_board_callsign_rename.md) — 2026-08-01 Wash→Pilot, Zoë→XO, Jayne→Observer, Kaylee→FlightEngineer, Emma→Commo; TACCO/Emma variant REJECTED; older crew-name memories are stale +- [project-overseer skill rewrite](skill_project_overseer_rewrite.md) — 2026-08-20 de-Sinicized + rebuilt; dir renamed to `project-overseer`; dual-mode governed/greenfield so it won't fork the WBS federation; external-source vetting gate +- [OPTIGA Trust M firmware layer](project_optiga_trust_m_firmware.md) — 2026-08-23 LibreServo_v4 `firmware/` tree; V3 ships a DEFAULT world-readable 0xE140 so pairing is mandatory; KEYSTORE can't hold the PBS; Shielded Connection needs host AES-CCM; PA8 I2C0_SDA is PF4 not PF3 +- [Infineon docs via GitHub](reference_infineon_github_docs.md) — infineon.com PDFs are anti-bot blocked; `github.com/Infineon/-overview` serves the same files (MD5-identical) and clones fine +- [Engineering PE skills repo](project_engineering_pe_skills.md) — 10 NCEES-aligned skills; aero has NO PE exam, statics is FE-level, NCEES never names sponsoring societies +- [Wing Rev T1/T1b fixed CF spar](project_wing_rev_t1_fixed_spar.md) — spar is a BONDED WING MEMBER not a rotating shaft; stub duct-bounded to 15mm; root joint splits shear(socket)/moment(80x60 flange) so the bay stays clear; drive is a REDUCTION (>1 rev, 14T/50T) so the servo-range question is void; section is S1223/t17.7—t26.7, NOT an S1223 --- @@ -340,40 +347,159 @@ rather than physical pass-through. --- name: feedback_kicad_hand_authoring -description: "Hand-authoring .kicad_sch — pin sheet_y = instance_y − lib_y; no ';' comments; verify by node count" +description: "Hand-authoring .kicad_sch — pin transform is instance_pos + Rot(angle)·(lib_x,−lib_y) at ANY angle; no ';' comments; verify by node count; kicad-cli version must match generator_version" metadata: node_type: memory type: feedback originSessionId: 0b104538-4478-49ff-91a6-9f2a40e2e90a - modified: 2026-07-20T16:48:14.424Z + modified: 2026-08-24T12:54:34.839Z --- -When hand-authoring KiCad schematics (`.kicad_sch` S-expr) in this repo, a placed -symbol's pins land at **sheet_x = instance_x + lib_x** but **sheet_y = instance_y − -lib_y** — the symbol library is Y-**up**, the schematic sheet is Y-**down**, so the Y -offset is **negated** (at rotation 0). X is not negated. +When hand-authoring KiCad schematics (`.kicad_sch` S-expr), a placed symbol's pins land +at **sheet = instance_pos + Rot(instance_angle) · (lib_x, −lib_y)** — the Y offset is +**always negated before the rotation is applied**, at every rotation angle, not just 0°. +X is never negated on its own. (Previously recorded as "sheet_y = instance_y − lib_y"; +that was only the angle-0 special case of this general rule. Confirmed again on the +osc-sg90-v006 ECC204 addition: an `angle 0` GND pin still needed the Y-negation — I +assumed angle-0 meant no transform at all and got it backwards, caught by +`pin_not_connected`/`power_pin_not_driven` on ERC/DRC after fixing the same bug I'd +already recorded here once.) **Why:** I wired instance pins using `+lib_y` and silently **cross-wired every multi-pin -part**. Symmetric parts (a 4-pin pigtail) still passed `kicad-cli` ERC with 0 errors while -being wrong; only the AK7455's asymmetric pins exposed it (dangling-label / no-connect -errors). ERC error count alone does NOT prove correct connectivity. +part**. Symmetric parts (a 4-pin pigtail, a 2-terminal R/C) still pass `kicad-cli` ERC +with 0 errors while being wrong, because the wrong pin still lands on a valid net by +symmetry — only asymmetric parts (the AK7455, or a mis-netted GND/power pin) expose it. +ERC error count alone does NOT prove correct connectivity; a power pin landing on the +wrong coordinate self-reports as its own isolated net (`pin_not_connected` + +`power_pin_not_driven`), which is the actual tell. **How to apply:** -- Compute each pin's sheet coord as `(instance_x + lib_x, instance_y − lib_y)`; place - wires / power symbols / global labels / no_connects at those coords. +- Compute each pin's sheet coord as `instance_pos + Rot(angle) · (lib_x, −lib_y)`; for + angle 0 that's `(instance_x + lib_x, instance_y − lib_y)`, for angle 180 it's + `(instance_x − lib_x, instance_y + lib_y)` — verified against this repo's own already- + wired symbols (SWDIO label sitting exactly on a pin) before trusting it on new work. +- Pick instance placement coordinates that are themselves multiples of the pin-offset + grid (1.27 mm in this repo) — every pin offset is already an N×1.27 multiple, so a + grid-aligned instance center guarantees every derived pin lands on-grid too, avoiding a + wall of `endpoint_off_grid` warnings. - Verify with `kicad-cli sch export netlist` and check **each net's node count** (e.g. a point-to-point signal must be 2 nodes), not just the ERC error total. - KiCad's parser **rejects `;` semicolon comments** — do not put them in `.kicad_sch` (they cause "Failed to load schematic"). Use `(text …)` notes instead. - Add `PWR_FLAG` on externally-supplied rails (pigtail-fed +3V3/GND) to clear - `power_pin_not_driven`. Round-mm placement gives benign `endpoint_off_grid` warnings - (matches existing repo schematics); inline symbols give benign `lib_symbol_issues`. -- Installed toolchain: **kicad-cli 9.0.2** (loads hand-written v7 `version 20231120` files). + `power_pin_not_driven`. +- **`kicad-cli`'s version must be ≥ the file's `generator_version`, or it silently fails + to load the file at all** ("Failed to load schematic"/"Failed to load board", no other + diagnostic) — this reproduces even on an untouched, just-committed file, so don't read + it as a sign your edit broke something. Check the file's own + `(generator_version "X.Y")` header first. open-servo-core-secure's boards are KiCad-10 + files (`"10.0"`); the system `kicad-cli` here is 9.0.2 and cannot load them at all. Fix: + run the newer `kicad-cli` bundled in a KiCad 10 AppImage instead of installing a second + system copy — `/path/to/kicad-10.0.3-x86_64.AppImage kicad-cli ` (it's a + `sharun`-runtime AppImage: the bare AppImage prints `sharun` usage, but it dispatches + any bundled binary name given as the first argument, so `kicad-cli` works as a + sub-invocation without extracting or installing anything). +- Run both `sch erc` and `pcb drc` **in place**, in the real project directory, so + `fp-lib-table`/`sym-lib-table`/`.kicad_pro` resolve — running against a copy elsewhere + (even with those files copied alongside) inflates unrelated violations (footprint + resolution differences) and produces a false diff. To isolate what your own edit + introduced, `git stash` the edit, run the check in place for the baseline count, `git + stash pop`, run it again, and diff by (violation type, item description, position) — + do not just eyeball the totals, since even a clean baseline on a real board can carry + dozens of pre-existing violations having nothing to do with your change. See [[project_tilt_encoder_ak7455]], [[project_avionics_board_folder_reorg]]. --- +## `feedback_kicad_pcb_footprint_rotation.md` + +--- + +--- +name: feedback_kicad_pcb_footprint_rotation +description: "Hand-authoring .kicad_pcb — pad transform is instance_pos + Rot(−angle)·(local, Y-negated if B.Cu); verify against live Gerber %TO.P% comments, not guesswork" +metadata: + node_type: memory + type: feedback + originSessionId: 0b104538-4478-49ff-91a6-9f2a40e2e90a + modified: 2026-08-25T12:13:43.646Z +--- + +When hand-authoring KiCad **PCB** files (`.kicad_pcb` S-expr — this is a +*different* rule from schematic symbol pins, see [[feedback_kicad_hand_authoring]]), +a footprint's pad lands at **board_pos = instance_pos + Rot(−instance_angle) · +(local_x, local_y')**, where `Rot` is the standard CCW rotation matrix and +`local_y' = −local_y` only when the footprint is on `B.Cu` (front-layer +footprints use `local_y' = local_y` unchanged). **The stored angle is negated +before rotating** — I assumed the stored angle was applied directly (standard +CCW, no negation) and got it backwards on both layers. + +**Why:** I spent a full extra round of a session unable to reconcile +DRC-reported pad positions for a real, unmodified component (`J4`) against my +own hand-computed positions, and wrongly concluded DRC was reporting stale +zone-fill data or that the board had a duplicate-reference bug confusing the +tool — neither was true. The only actual bug was my own rotation formula. +Confirmed by cross-referencing a live-exported Gerber's `%TO.P,,%` +component-attribute comments (emitted immediately before each pad's `D03` +flash) against the coordinates DRC reported — direct, unambiguous ground +truth, since it's KiCad's own plotter stating "this exact flash belongs to +this exact pin." Solving for the rotation that reconciles local pin +coordinates with those confirmed absolute positions gave `Rot(−θ)`, not +`Rot(+θ)`. + +**How to apply:** +- For a `B.Cu` footprint at instance angle θ: `board = instance + Rot(−θ) · + (local_x, −local_y)`. For `F.Cu`: `board = instance + Rot(−θ) · + (local_x, local_y)`. +- **Do not trust your own derivation without independent verification** — + export a Gerber for the affected layer (`kicad-cli pcb export gerbers + --layers -o .kicad_pcb`) and grep for + `%TO.P,,%` immediately followed by an `X..Y..D03*` + flash. That coordinate is ground truth (note: Gerber Y is the *negative* + of board Y — `%FSLAX46Y46%` + `board_y = -gerber_y`). Cross-check at least + one asymmetric or known-net pad before trusting the formula on new work, + the same way [[feedback_kicad_hand_authoring]] says to check schematic + pins against an already-wired label. +- A footprint's declared pad `size` is not necessarily its real copper + extent — a through-hole pad can declare a tiny `size` (e.g. `0.1mm`) with + a much larger `drill` (e.g. `0.9mm`); KiCad enforces a minimum annular + ring, so the effective copper is close to `drill + 2×ring`, not the + declared `size`. Don't use the bare `size` field for clearance/placement + math on any pad whose `drill` is close to or larger than its `size` + — verify the real extent via the same Gerber-flash method (the aperture + selected for the `D03` flash states the actual plotted diameter). +- **A hand-edit to only the outer `(at X Y angle)` line of a footprint is + not sufficient if that footprint's per-pad/per-property `(at ...)` lines + carry their own angle suffix** — KiCad stores the same rotation angle + redundantly on the outer footprint line *and* on every pad/property + `(at x y angle)` line inside it, and a GUI "move" (as opposed to + "rotate") can desync them (flatten the inner ones to 0 while leaving the + outer line correct). The result parses fine and looks fine but produces + wrong absolute pad positions and cascading, hard-to-trace DRC findings + against unrelated nearby components. After any edit to a rotated + footprint, diff its *entire* block against a known-good copy (e.g. + `git show HEAD:` for the same UUID), not just the position line — + or replace the whole block wholesale rather than patching one line. +- Whether `kicad-cli pcb drc`/`pcb export gerbers` read a zone's *live + recomputed* fill or its *stored* `filled_polygon` data (which goes stale + the moment new copper is added without a GUI "Fill All Zones") could + **not** be determined from Gerber inspection alone — a Gerber export may + itself just plot the same stored data. Treat any zone-clearance DRC + finding against newly-added copper as *likely real* but only fully + resolvable by a live "Fill All Zones" (`B`) + re-DRC in the actual KiCad + GUI. +- DRC exclusions in `.kicad_pro`'s `drc_exclusions` use a violation-specific + hash format (`padstack|X|Y|uuid|uuid` for padstack findings — other + finding types almost certainly use a different encoding). Do not + hand-author a new exclusion entry by guessing the format; add it via the + GUI's right-click "Exclude this violation" and let KiCad compute the + hash, or leave the finding un-excluded and documented instead. + +See [[feedback_kicad_hand_authoring]], [[feedback_generator_drift_and_freerouting]]. + +--- + ## `feedback_todo_stale_items.md` --- @@ -631,6 +757,54 @@ Output: files-hollowed-18in/s_eng_{left,right}_stator_shell24_50mm.stl --- +## `project_board_callsign_rename.md` + +--- + +--- +name: project_board_callsign_rename +description: "Avionics/GCS boards were renamed off Firefly character names on 2026-08-01 — Wash→Pilot, Zoë→XO, Jayne→Observer, Kaylee→FlightEngineer, Emma→Commo; older memories using the crew names are stale" +metadata: + node_type: memory + type: project + originSessionId: 8ad51954-93f9-409c-9d97-d766b7f14caf + modified: 2026-08-18T13:17:26.714Z +--- + +Commit `9bc107b` (2026-08-01, "Rename avionics/GCS boards off Firefly +character names", TODO.md §0.9 item 8) renamed every board directory under +`avionics/kicad/`: + +| Old (crew name) | Canonical call-sign | +| --- | --- | +| Wash | **Pilot** | +| Zoë | **XO** | +| Jayne | **Observer** | +| Kaylee | **FlightEngineer** | +| Emma | **Commo** | + +`avionics/kicad/Jayne/` still exists on main but holds only +`PCBNEW_SWIG_BUG.md`; everything else moved. + +**A competing rename was rejected.** On 2026-08-15 the stale `legs` branch +(119 commits behind main) hand-redid the same rename without the 08-01 work in +view, landing on **TACCO** for Zoë and leaving **Emma** unchanged, and it +*copied* rather than moved — `FlightEngineer/` kept `Kaylee.*`, `Pilot/` kept +`Wash.*`, `Observer/` kept `Jayne.*`. The owner reviewed both on 2026-08-18 and +kept main's (XO, Commo); PR #185 was closed as superseded rather than merged. +So **TACCO and Emma are not the names** — do not reintroduce them. + +**Why this matters for recall:** several older memories predate the rename and +still use the crew names throughout — [[project_jayne_cape_design]], +[[project_jayne_som_carrier]], [[project_jayne_som_endstate]], +[[project_jayne_vera_rename]], [[project_emma_zoe_schematic_migration]], +[[project_wash_zoe_rebuild]], [[project_fleet_trust_module]] and +[[project_trust_module_g351x_retarget]]. Their engineering content still +holds; translate the board name through the table above before acting on +them, and never write a new path or symbol under a crew name. + +--- + ## `project_emma_zoe_schematic_migration.md` --- @@ -676,6 +850,64 @@ board (renamed from "Vera" to "Jayne", see [[project_jayne_vera_rename]]) and th --- +## `project_engineering_pe_skills.md` + +--- + +--- +name: project-engineering-pe-skills +description: New publishable repo of 10 NCEES-aligned engineering skills; key alignment findings that must not be re-derived +metadata: + type: project +--- + +2026-08-29: Started `github.com/Griffing-Technology-LLC/engineering-pe-skills` +(cloned to `designs/engineering-pe-skills`, MIT). Ten discipline skills; PR #1 +has the scaffold + `aeronautical-engineering` as the reference pattern. + +Three findings that cost real fetches to establish — do not re-derive: + +1. **NCEES discipline pages do NOT name sponsoring societies.** The "Society of + Petroleum Engineers" text on those pages is a page-footer artifact. Cite + sponsorship to the society's own site, never to NCEES. +2. **No NCEES PE exam exists for aeronautical/aerospace engineering** (23 + disciplines, aeronautical absent). Statics & dynamics is **FE-level**, not a + PE discipline. Both are documented as explicit exceptions in README.md. +3. **PE Electrical & Computer module is "Electronics, Controls, and + Communications"** — not "Electrical and Electronics" as the top-level + discipline list implies. + +Verified from own sites: SFPE ("Licensing & PE Exam"), SNAME (PE Review Course). +**ISA role RESOLVED** at `isa.org/certification`: ISA "supports the Control +Systems Engineer (CSE) License" and offers review materials — it does NOT author +or administer the exam. Never upgrade that to "sponsors"/"develops". + +ASTM/AIChE/AIAA return HTTP 403 to curl (anti-bot, links are real) → recorded +`VERIFIED (BLOCKED)`, not `VERIFIED`. + +**Part 23 RESOLVED** — ecfr.gov WebFetch is blocked (302 to an unblock page); +use the eCFR API instead: `ecfr.gov/api/versioner/v1/full//title-14.xml?part=23` +(date must be <= the title's issue date). Findings: **§23.2230(b) DOES state the +1.5 FOS** — it is current text, my earlier caution was wrong. But **+3.8/-1.52 +appear NOWHERE** in current Part 23 (§23.2200(b) is performance-based) — that +caution was right. §23.2265 "special factors of safety" covers articles with +"appreciable variability because of uncertainties in manufacturing processes" = +a real citation for printed-polymer allowables. See [[feedback-todo-stale-items]]. + +4 of 10 skills drafted + installed via symlink `~/.claude/skills/` -> +repo `skills/`: aeronautical, mechanical, statics-and-dynamics, +control-systems. CI enforces the mandatory qualified-review notice is present +and is the FIRST `##` section of every SKILL.md. + +markdownlint MD051 flags TOC link fragments in these files that an isolated +repro can NOT reproduce — reference files are all <300 lines, so the fix is +plain (unlinked) Contents lists, not chasing the rule. + +markdownlint: MD013 must exempt tables/code/headings — a SKILL.md frontmatter +`description` is necessarily one long line. + +--- + ## `project_esc_mcu_swap_mspm0.md` --- @@ -1164,70 +1396,85 @@ leg-length choice), LG-10 (bay flank-conforming bake), LG-18 --- name: project-lg-sponson-wells -description: "Rev R6 gear bays mount on the sponson's 25° angled panel (NOT the cargo flank); wells cut at canonical Y -7/+107 need no sponson extension; branch feat/lg-r6-bay-integration" +description: "Rev R6 gear bays mount on the sponson's 25° angled panel (NOT the cargo flank); LG-10 CLOSED 2026-08-17 — bay seat is a shared per-station DATUM, not a conforming patch" metadata: node_type: memory type: project - originSessionId: 41ad9868-1d22-4161-8ca1-2dea4ad112a0 - modified: 2026-08-09T23:27:02.192Z + modified: 2026-08-17T00:00:00.000Z --- -2026-08-09. Landing-gear/hull integration, branch `feat/lg-r6-bay-integration` -(worktree `.worktrees/lg-r6-bay-integration`, merged to local `main` through -`ae7ad5f`; later commits `abf88b5`, `f687c9b`, `1211cd6`, `cac91cf` are on the -branch). **The canonical `cargo_sect_shell24_2mm_repaired.stl` is UNTOUCHED** — -every merge went to a scratch `CARGO_MERGE_OUT`. +Landing-gear/hull integration. **LG-10 closed 2026-08-17**, all eight sub-items. +The canonical `cargo_sect_shell24_2mm_repaired.stl` IS now published (an older +version of this note said it was untouched — that was already stale). **The mounting face is the sponson's 25° angled panel, not the cargo flank.** -This was wrong for the whole of Rev R6 and cost a lot of rework: +Wrong for the whole of Rev R6; it caused every defect found. -- The plate back face floated 14–17 mm outboard of the flank; `BAY_CANT = +22` - leaned it the wrong way (the real flank leans OUTBOARD going up). -- Vertical walls squared to port/stbd (Y −7: Z 10–40, half-width 56.04) are the - hull sides **forward of** the sponson — not a mounting face. - Measured panel normal **(0.901, 0.015, −0.433) port**, mirrored stbd; tilt - 25.1–26.0°, mirror-verified to 1.4 mm. Use this constant directly — - a per-station frame search lands on door-frame/joint surfaces and returns - junk (Y-dominated fore, −Z-dominated aft). -- Panel runs roughly Y 8 → 128, tiled by ~12 coplanar sub-patches. - `tools/landing_gear_opening_fit.py` measures it. - -**Y −7 is where the sponson meets the wall** (owner). So the wells need NO -sponson extension: each cutter removes 6.0–6.7 cm³ of existing shell at the -canonical stations. A convex-hull extension was tried and reverted — it added -**+61.7 g** of bulge. Wells-only gives **−26.6 g** (298,576 → 273,270 mm³), -and that delta matches 4 × ~6.3 cm³, which is the check that the cuts are -right. Face drop 1.05M → 949k is removed wall area, not lost detail. - -**Three bugs that cost real time — watch for these:** - -1. An `Edit`/replace can **silently not apply** if the file changed on disk. - It produced a byte-identical no-op merge that still printed `RESULT: PASS` - and `CI_valid=True`. Assert your replacements; check the volume actually - moved. -2. Anchoring "most outboard vertex" in a cylinder finds the hull's **widest - point (Z ≈ 78)**, not the bay station (Z 38). Constrain height too. -3. Selecting `nx > 0.7` for "port outward" also catches the **starboard INNER - wall** (its normal points toward +X). Always add an X-vs-centreline - constraint. Without it a convex hull spanned the whole hull. - -Closed: LG-13 (nylon-tipped M2 drag screw — the seat SLIDES 1.62 mm per stroke, -a clamp would fight the fuse), LG-17 (**4 ft**, Ø3.81, −16.6 g), LG-19 -(trapezoidal cowl on the bay plate, narrow end at the mouth, interference- -verified clear 0–30.9°). - -Open: visual review then publish the shell; relocate the 16 bay bolt bosses -onto the well collars and re-enable `LG_BAY_ENABLED`; hull patches + -`BAY_CONFORM`; FCStd purge of `leg_4_scaled24`/`nacelle_port_revq`; foot -Z-levelling. Also carried: the **4× stroke-formula error** in + 25.1–26.0°. Use this constant directly — a per-station frame search lands on + door-frame/joint surfaces and returns junk normals. Since LG-10.3 it only + LOCATES the opening; it never orients the mount (bolt axis vs panel normal + disagree by a 21–24° yaw no `BAY_CANT` can remove). +- **Y −7 is where the sponson meets the wall**, so the wells need NO sponson + extension. A convex-hull extension was tried and reverted: +61.7 g for + nothing. + +**How the bay actually seats (LG-10.6 — the big one).** The conforming hull +patch and `BAY_CONFORM` were **retired, not built**. The hull-side flange +rebate (flat trapezoidal pocket, 5 mm deep, 12 mm into the skin) already shaves +the whole footprint, so the hull presents a FLAT seat normal to the bolt axis; +cutting the printed part to a curve would put the mismatch back and split the +bay into four unshared geometries. The real defect was the **datum**: +`BAY_STANDOFF` was 12.6/5.4 (measured along the panel NORMAL) against a pocket +cut at −7.69/−3.55 in the plate frame — a **3.5–7.7 mm air gap under every M3**. +Now **4.91 fore / 1.85 aft**, ONE datum per station (the deeper corner), which +is what keeps the §11.4 shared-BOM claim true. Single-sourced as +`merge_cargo_interior.station_seat_data()`; gated by +`tools/landing_gear_bay_seat_fit.py` — re-run after ANY hull re-merge. + +**LG-10.8 was not about the feet.** All four are level to 0.0000 mm. The +assembly's lowest point was not the sole: a hardcoded spigot cube drove the +ankle spigot **2.9 mm through the foot** on the 1.5in leg (743.9 mm³ of leg +inside the foot — unassemblable). The rule, stated in `FOOT_HUB_H`'s own +comment: `ANKLE[2] = GROUND_Z + ANKLE_DISC_D/2 + FOOT_HUB_H` (= +18). The 3.0in +leg satisfied it; the 1.5in was 6.4 mm short from the variant split. Fixed by +moving ANKLE (−64.5 → −58.1), NOT `GROUND_Z` (pinned by the 1.5 in spec) and +NOT `R_H` (which is what keeps the LG-17 load schedule valid). In-SCAD +`assert()` guards it now; `tools/landing_gear_foot_stance.py` gates it. + +**Two latent bugs found on the way:** + +1. **`bowed_wire` in `canonical_leg_r6_1_5in.scad`** built its side wall as a + list of face PAIRS inside a **no-op `concat()`** (the 3.0in file always had + the correct `each`). Every wire on the DEFAULT leg exported as 20 cap faces + of zero volume; nobody noticed because the shared wire STLs came from the + other file. Moral: compare shared parts GEOMETRICALLY across variants — + `tools/export_landing_gear_stls.py` now does. +2. **Zero-area collinear slits.** A 0.02 mm collar nudge created a coplanar + boolean seam and a 3-edge boundary loop 0.25 mm long that failed CI. + `trimesh.repair.fill_holes` CANNOT close one (the fill triangle has zero + area and is rejected); collapsing the loop can. + `merge_cargo_interior.close_zero_area_slits()` does it, only for loops + flatter than 1 µm, so real holes still fail loudly. + +**Traps that cost real time:** an edit can silently not apply and still print +`RESULT: PASS` / `CI_valid=True` — assert replacements and confirm the volume +moved. "Most outboard vertex" finds the hull's widest point (Z ≈ 78), not the +bay station (Z 38). `nx > 0.7` also catches the starboard INNER wall — add an +X-vs-centreline constraint. And **measuring the seat on the published shell is +circular** (the rebate is already cut there) — measure on `BLENDER_SRC`. + +Still open: procurement/test only — LG-15/LG-16 block leg fab, LG-13 blocks +first flight. Carried: the **4× stroke-formula error** in `wire_stroke_available()` (`H_DEF_DUCT` should be 9.9 not 19.2; ductile stock -could drop 75 → ~40 mm, ~33 g) — correcting it re-opens LG-15, and the §11.4 -"shared BOM" claim needs amending since a conforming back face makes the bay -two mirrored geometries. +75 → ~40 mm, ~33 g) — correcting it re-opens LG-15 and shrinks the bay. +Known noise: `landing_gear_cowl_clearance.py --variant 1_5in` reports 5.468 mm³ +at 26°, which is 4° PAST the 22° stop; pre-existing, clear through the real +stroke. -See [[env_venv_hides_system_python]] — all of this tooling needs +See [[env_venv_hides_system_python]] — all this tooling needs `/usr/bin/python3` for manifold3d. Related: [[cargo_interior_merge_pipeline]], -[[project_landing_gear_r6_variants]], [[feedback_generator_drift_and_freerouting]]. +[[project_landing_gear_r6_variants]], [[project_wing_spar_rev_s1b]]. --- @@ -1564,6 +1811,50 @@ gear_option_compare.scad / gear_shell_compare.scad still `use<>` archived gears. --- +## `project_optiga_trust_m_firmware.md` + +--- + +--- +name: project_optiga_trust_m_firmware +description: 2026-08-23 LibreServo_v4 firmware/ tree delivering the OPTIGA Trust M driver layer; the V3 default-PBS finding, the KEYSTORE correction, and the PA8 PF4 fix +metadata: + type: project +--- + +2026-08-23, `LibreServo_v4`: built the OPTIGA™ Trust M driver layer as a new +**`firmware/`** tree (config + PAL for MSPM0G3518-Q1 + trust layer + host +tests). Uncommitted at session end. Extends [[project_optiga_trust_m_swap]]. + +Four findings that are easy to get wrong again: + +1. **An OPTIGA Trust M *V3* part ships 0xE140 (platform binding secret) with a + *Default* value and read AC *ALW*** (Configuration Guide §2 Table 1). So the + Shielded Connection protects nothing until the manufacturing pairing step + runs. Pairing is mandatory, not hardening. (Express/MTR ship pre-paired with + chip-unique secrets — different variants, don't generalise.) +2. **The MSPM0 KEYSTORE cannot hold the platform binding secret.** It is a + deposit-then-use-by-AESADV store with no software readback, for 128/256-bit + keys; the host library must read the up-to-64-byte secret back through + `pal_os_datastore_read`. KEYSTORE *is* right for the derived session CMAC key. + Two different secrets, two different lifetimes. +3. **Enabling `OPTIGA_COMMS_SHIELDED_CONNECTION` obliges the host to supply + AES-128-CCM and the TLS-1.2 PRF** (`pal_crypt_*`), because the IFX I²C + presentation layer protects each APDU on the MCU side. Not visible in the + datasheet; it is a real cost of the decision. +4. **`PA8`'s `I2C0_SDA` is IOMUX `PF4`, not `PF3`** — `PF3` on `PA8` is + `SPI0_CS0`. The repo had `PF3` in two places. Read TI pin tables with + `pdftotext -layout`; the plain extraction loses the column structure and the + adjacent pin's row bleeds in. + +**Infineon docs workaround, reusable:** infineon.com PDF links return an +anti-bot interstitial, but `github.com/Infineon/-overview` serves the +same documents and `git clone` works. The Trust M datasheet there was MD5- +identical to the repo's existing copy, which retroactively corroborated it. +See [[reference_infineon_github_docs]]. + +--- + ## `project_optiga_trust_m_swap.md` --- @@ -1593,8 +1884,17 @@ machinery. Only the designator was reused. device identity (ECDSA, fab-provisioned key + X.509) and ephemeral session-key agreement (ECDHE) once at boot; the MCU's symmetric engine (S32K144 CSEc on the ESC) does per-frame AES-128 CMAC. The Trust M **must never be in a control hot -path** — REFERENCES.md [45] p.28 §7.2 permits only **one protected operation -per 5 s `t_max` period**, and any use of the identity private key counts. +path**. + +**CORRECTED 2026-08-23** (see [[project_optiga_trust_m_firmware]]): the reason +above used to be written as "[45] p.28 §7.2 permits only one protected operation +per 5 s `t_max`". That is the *permitted sustained usage profile*, not an +instantaneous gate. Per the Solution Reference Manual §4.6, the throttle only +starts at SEC=128 and reaches `t_max` at SEC=255; credit accrues while idle; and +**session-context temporary keys are carved out of every key-use security +event**. A boot-time burst is affordable and ECDHE is cheap. The hot-path +conclusion still stands — per-frame use at servo rates would pin SEC at its +ceiling — but do not repeat the "flat one-per-5s" framing. The folder was renamed `LibreServo_v2-Sec` → **`LibreServo_v4`** to match the GitHub repo rename (`Stab-Rabbit-coding/LibreServo_v4`); `origin` retargeted, @@ -2114,6 +2414,111 @@ See [[feedback_kicad_hand_authoring]], [[project_fleet_trust_module]], --- +## `project_wing_rev_t1_fixed_spar.md` + +--- + +--- +name: project-wing-rev-t1-fixed-spar +description: "Wing Rev T1 built 2026-08-29 — spar is now a FIXED bonded CF wing member, not a rotating shaft; root moment path, bore layout, and the joints published to fuselage/nacelle" +metadata: + node_type: memory + type: project + originSessionId: bb0768a0-1c5c-4dcf-a8d7-5d9a932f4816 + modified: 2026-08-29T22:04:16.179Z +--- + +2026-08-29: `airframe/openscad/wings/wings_s1223_revo.scad` **Rev T1** built. +The 20 mm spar stopped being a rotating drive shaft and became a **fixed +20 × 16.3 CF tube bonded into the wing over its full span** — the wing's primary +bending member. That single change is what re-routes everything else. + +**Why it matters structurally:** a bearing transmits shear, not moment, so the +Rev R2 rotating spar was *incapable* of reacting the root moment. The enlarged +tenon (FOS 0.49) and the two-rod couple (FOS 4.14) both existed only to work +around that. A fixed bonded spar carries it directly — couple arm 48 mm +(chordwise rods) → 86.7 mm (span). `TENON_LOAD_PATH = "spar_carrythrough"`; +tenon locates only; **tie rods retired** (fwd rod at 9.9–18.1 now intersects the +Ø20.4 bore at 17.80–38.20; aft rod's only remaining job was torsion = FOS 306). + +**Built numbers:** station 45.15→**28.0**, bore 8.3→**20.4**, `THICKNESS_SCALE` +1.00→**1.46**, `THICKNESS_SCALE_TIP` 1.56→**2.20** (solved 1.456/2.190, rounded +up per the file's own "not sitting on its own limit" convention), `SPAR_Z` +**66.85**. Root t/c 12.14→17.72 %, tip 18.93→26.70 %. + +**Bore layout (all constant-mm so taper can't erode webs):** 8.0 Ø3.2 nav · +28.0 Ø20.4 spar (carries the 4×10 AWG on the tilt axis) · 44.5 Ø6.5 AK7455 · +54.0 Ø4.4 tilt drive shaft. The 2ר7 EDF double-D is **retired** — it never +could carry two 10 AWG conductors, and 27.5 now falls inside the spar bore. + +**Four corrections made, not worked around:** + +1. Plan 004 KTD4's shaft station 43 ⇒ 15 mm gear centre distance is + **kinematically impossible** — the ring gear is concentric with the spar, so + the algebra returns ring PD 10.5 mm, smaller than the spar. Built C = 26.0, + station 54. Servo range is now **blocking**: 180° needs station 58.5, which + hits the root tenon. +2. `wing_tip_spar_through_bore()` carried `rotate([0,90,0])` — cutting a + *chordwise* hole. Harmless at Ø8.3; a gash at Ø20.4. +3. `wing_airfoil_integrity.py` validated only the `t_scale = 1.0` table and + never read either thickness scale — it would have passed identically at 2.20. + Now validates the sections `wing_solid()` actually lofts (closes plan RISK-2). +4. Plan 003's wingtip pinch collar and garage both moved — a Ø30 collar cannot + exist in a 22.83 mm section, and the tip has no volume for 10 AWG + disconnects (section falls 17.50→3.43 mm aft of the spar). + +**Rev T1b (same day) — three owner directives, two invalidated built geometry:** + +1. **Spar must not penetrate the thrust duct.** Stub was 32 mm and reached + |X| = 9.7 against a duct wall at r=25 — 15 mm INSIDE it. The spar runs along + local X at Y=0 so it clears only if it TERMINATES at |X| ≥ 26; tip face is at + 41.7, so **max stub 15.7, built 15.0**. The nacelle's bearing pair, ring gear + and ring magnet must now ALL fit in 15 mm (2× 6804 = 14.0 fits). +2. **Cargo bay centre must stay clear.** Kills the 55 mm socket — only 18.67 mm + exists before the bay (wall −81.33, bay edge −100), where the socket gives + FOS 0.51 on moment. **Joint splits by load type:** socket 18.5 mm for SHEAR + (FOS 16) + bonded **80×60 flange** on the inner sidewall for MOMENT + (FOS 29.2). Better than the socket it replaced (4.02) — a flange trades a + 1/L² depth term for a linear area term. Bay untouched; owner decision closed. +3. **Shaft turns >1 revolution.** Inverts the stage to a REDUCTION, which + dissolves the "impossible ring gear" finding (that was true only under the + step-up reading). Built module 0.8, **14T pinion / 50T ring, i 3.571, 1.389 + rev, C 25.6 → station 53.6**. Actuator is now a continuous-rotation gearmotor + or stepper closed on the AK7455 — the 180/270° question is VOID, and the + encoder becomes load-bearing for CONTROL, not telemetry. + +Encoder followed: `HALL_SENS_R` 17→**16.8**, ring **ID 26 / OD 41.2**, board 9×8. + +**Joints published, not built:** `docs/WING_ATTACH_INTERFACE.md` is the single +spec for both ends, with a requirement register (WA-R1..R15). + +**Airfoil re-derived from the tabulated UIUC coords (REF-CAD-006), NOT quoted.** +The built sections are **S1223/t17.7** and **S1223/t26.7** — S1223's camber line +with the thickness envelope scaled. max t/c 17.72/26.71 % **at 19.8 % chord**; +camber **8.67 % at 49.0 %**, unscaled and exact. The old header's "22.6 %" and +"39.4 %" locations were WRONG. LE radius goes as t_scale² (0.025c→0.053c→0.121c). +Camber figures survive exactly; zero-lift angle and lift-curve slope partially +survive at the ROOT only (thin-airfoil theory depends on camber alone, but 26.7 % +is far outside "thin"); CL_max/L/D/cruise-lift do NOT survive. + +`tools/wing_root_deconflict.py` **FAILS by design** until the fuselage moves — +3 findings, all one cause: fuselage still on `WING_SPAR_Y = +38.15`, +`WING_SPAR_BORE_D = 8.3`. + +**Citation defects found and fixed:** the repo cited **14 CFR Part 23.303**, +removed by the 2017 Amdt. 23-64 restructure → now **§23.2230** (REF-FAA-004, +verified verbatim via GovInfo CFR XML; eCFR bot-blocks fetches). And the FOS 4.0 +joint target, described as bare judgment, has a real basis: **§23.2265** requires +a *special* factor for parts with manufacturing-process variability — which is +exactly FDM. Also recorded: +3.8/−1.52 appear NOWHERE in current Part 23. + +Two figures everything else hangs off are **unverified**: the 5.5 mm 10 AWG wire +OD (no BOM entry) and the 300 MPa CF allowable (no certificate). See +[[project_nacelle_1_1_3_completion]], [[project_wing_spar_rev_s1b]] (superseded +by this), [[project_tilt_encoder_ak7455]]. + +--- + ## `project_wing_spar_rev_s1b.md` --- @@ -2174,6 +2579,96 @@ because the OpenFOAM study is blocked on meshing — see --- +## `reference_infineon_github_docs.md` + +--- + +--- +name: reference_infineon_github_docs +description: Infineon publishes its product PDFs in its own GitHub org, which bypasses the infineon.com anti-bot interstitial that blocks direct datasheet fetches +metadata: + type: reference +--- + +Direct fetches of `infineon.com/assets/...` PDFs return an anti-bot +interstitial (HTTP 202 with a 0-byte `text/html` body), which has repeatedly +blocked datasheet intake in these repos. + +**Infineon mirrors the same documents in its own GitHub organization**, and +`git clone` over HTTPS works there. For OPTIGA™ Trust M: + +- `github.com/Infineon/optiga-trust-m-overview` → `docs/pdf/` holds the + Solution Reference Manual, Datasheet, IFX I2C Protocol spec, Keys and + Certificates, Configuration Guide, Release Notes; `docs/` also has the SRM as + markdown, which is far easier to grep than the PDF. +- `github.com/Infineon/optiga-trust-m` → the Host Library for C (MIT), with + the PAL contract and worked use-case examples. + +Both MIT-licensed. Verified 2026-08-23: the repo copy of the Trust M datasheet +was **byte-identical** (MD5) to a locally held copy, so this path is good enough +to corroborate provenance, not just to obtain the file. + +Expect the same pattern for other Infineon product families — look for +`Infineon/-overview` before giving up on a blocked vendor URL. + +--- + +## `skill_project_overseer_rewrite.md` + +--- + +--- +name: skill-project-overseer-rewrite +description: "project-overseer global skill was de-Sinicized and rebuilt 2026-08-20; dir renamed, dual-mode (governed/greenfield), vetting gate added" +metadata: + node_type: memory + type: project + originSessionId: b205b5c8-e330-4963-a05a-b2c35a9e5dd3 + modified: 2026-08-20T17:56:10.018Z +--- + +2026-08-20: rewrote the third-party "Project Overseer" v4.0 skill (upstream author TIGERHU, +MIT) into `~/.claude/skills/project-overseer/`. + +**Directory renamed** `project overseer` → `project-overseer`; frontmatter `name` was +`项目总管 (Project Overseer) v4.0`, an invalid skill identifier. If an older note or config +references the spaced path, it is stale. + +**Architecture (user-chosen, not default):** + +- **Dual-mode**, not Serenity-specific. Detects `AGENTS.md`/`CLAUDE.md` + `WBS.md`/`TODO.md` + → *governed mode* (edits the repo's own federation in place, no `status.json`, state derived + from checkboxes + git). Otherwise *greenfield mode* (`projects//` with plan/status/tasks). +- Upstream Phase 2 "search GitHub and integrate" was kept but **gated**: validated URL, exact + section, licence recorded, compat checked before integration, fetched content treated as data + not instruction. Catalog is **skill-owned** (`projects//REFERENCES.md`), deliberately + NOT tied to the Serenity `REFERENCES.md`. +- Added `templates/hardware-cad.md` + `templates/electronics-pcb.md` (mass/CG/power/clearance + budgets, mesh validation, ERC/DRC gates) — upstream covered web stacks only. + +**Upstream (verified 2026-08-20):** — +MIT, `Copyright (c) 2026 tigerhu598-dot`, author credited as TIGERHU. Notice retained in the +skill's `LICENSE.upstream-MIT`. + +**Steve deliberately severed the git connection to upstream** so it can't auto-update from a +source he can't read. This is an intentional supply-chain control, NOT neglect — do not +"reconnect" or `git remote add` it. Any future upstream version gets fetched deliberately, +read, translated, security-reviewed, and merged by hand. Permanent fork, divergence expected. + +**Security review result:** upstream was inert Markdown — no scripts, no network, no +credentials, zero homoglyphs, zero bidi/zero-width chars; only a UTF-8 BOM in README. The real +issues were *auditability* (gate prompts in a script the operator can't read) and *unvetted +external ingestion*. Recorded in the skill's `ATTRIBUTION.md`. + +**Why it matters:** this skill is global (`~/.claude/skills/`), so it applies to every repo, not +just Serenity-UAV. Governed mode is what makes it safe here — it will not create a second task +list alongside the [[project_generated_index_conflicts]] federation. + +See also [[feedback_todo_stale_items]] — that rule is now written into +`references/governed-mode.md` as a standing governed-mode requirement. + +--- + ## `skill_secure_controller_assurance.md` --- @@ -2236,3 +2731,84 @@ Related: [[project_fleet_trust_module]], [[project_optiga_trust_m_swap]], [[project_esc_mcu_swap_mspm0]], [[feedback_kicad_hand_authoring]] --- + +--- + +--- +name: project_fuselage_rev_t1c_tilt_drive +description: Rev T1c fuselage wing-root joint + multi-turn tilt actuator; the tenon forces the gear plane and costs an 18mm actuator standoff +metadata: + type: project +--- + +2026-08-30, commit 16ebb59. Fuselage side of [[project_wing_rev_t1_fixed_spar]] +built in `merge_cargo_interior.py`: socket Y +21.00 / Z +66.85 / Ø20.4 / 18.5 mm, +boss Ø30.1, F688ZZ **and both tie rods deleted**, mortise 30.8→12.8, conduits +nav Ø4.2 @ Y+1.0 / AK7455 Ø7.5 @ Y+37.5 / shaft Ø4.4 @ Y+46.6. + +**Three non-obvious forcings, in the order they bite:** + +1. **Coaxial actuator does not fit.** DS3225 on the drive-shaft axis overlaps the + Ø30.1 socket boss by 13.5 mm in Y at *either* output-shaft orientation. + → fuselage spur pair m0.8 38T/38T, C 30.40, **1:1**. +2. **C is set by the landing-gear bay tops** (Z +82.39 + 3.0 budget ⇒ C ≥ 29.80). + 36T/37T fall under budget; 38T clears at 3.60 mm. +3. **The root TENON forces the gear plane, and this is the expensive one.** Tenon + spans X −100…−108, Y +51.50…+63.50; its forward face is 4.90 mm from the shaft + axis, so clearing it *in Y* needs a tip radius < 3.90 mm — no gear that can + transmit at C = 30.40 qualifies. It must be cleared **axially**: mesh 11 mm + inboard of X −100/−240, which costs an **18 mm actuator standoff** and + 18 mm/side of cargo-bay roof reach. + +**Stage is 1:1 on purpose.** A step-up would trade the 48× torque surplus for +slew rate but pulls the actuator to 260° — under one revolution — re-opening the +180-vs-270 question Rev T1 exists to close. + +**Root flange is a bonded PART, not printed hull.** Ray-cast 13×17 grid: the +sidewall skin moves **34.3 mm (port) / 37.0 mm (stbd)** across the 80×60 +footprint. Filling to a plane ⇒ 34 mm thick; bounding with a plane ⇒ tangent to +the skin (0.46 mm non-manifold edge, 4 faces, at −86.33/+8.6/+52.2). Subtracting +a translated envelope from the full 900k-face shell was **worse** (4 boundary, 2 +non-manifold, 4 bodies). Done **locally** on a cropped envelope it is clean: +`airframe/stls/fuselage/generate_wing_root_flange.py`, 12.7 g/side at 40 %. + +Mass: **+77.0 g, +1.97 % AUW**; hover T/W was ~1.19 vs a 1.2 minimum → WA-R18 +blocker. + +Actuator = DS3225 + LibreServo_v4, pin removed, **multi-turn**, closed on the +AK7455 — same pattern as the cargo winch. Loop spec: +`docs/TILT_DRIVE_CONTROL_SPEC.md`. **The train is NOT self-locking and has no +holding provision (TILT-CTL-01/WA-R16) — flight-release blocker.** + +--- + +--- +name: feedback_gates_mask_tool_bugs +description: When a gate is red for a known geometry reason, its OTHER checks are unvalidated — fixing the geometry exposes tool bugs, so budget for them +metadata: + type: feedback +--- + +Serenity-UAV, 2026-08-30. `wing_root_deconflict.py` had been red "by design" +since Rev T1 for one known cause. Fixing that cause exposed **four** genuine +defects in the checkers that the red state had been hiding: + +- `tenon_params()` matched only `"two_rod"`, so the Rev T1 default path + `"spar_carrythrough"` fell through to the **enlarged 30 mm tenon** — reporting + an 8.60 mm/side mortise foul against a correctly-sized mortise. The SCAD writes + the rule as a *negative* (`!= "enlarged_tenon"`) precisely so new paths inherit + the safe size; the tool mirrored it as a positive whitelist and inverted it. +- The spar corridor is **stepped** (Ø20.4 outboard, Ø16.3 inboard) and was swept + at one diameter, reading the intended annulus as "wall not cut". +- Every route was probed 12 mm past the wall bracket — right for a rigid shaft, + wrong for a wire that turns. +- `cargo_bay_envelope.py` derived spar OD with the retired 0.15 mm/side rotating + clearance and printed "rotating tilt-spar" for a fixed bonded tube. + +**Why:** a failing gate is not a *partially* passing gate. Everything downstream +of the first failure is unexercised, and stale assumptions accumulate there +undisturbed. + +**How to apply:** when clearing a long-red gate, expect to fix the gate too; +read each check's assumptions against the *current* design before trusting a new +green. Related: [[project_fuselage_rev_t1c_tilt_drive]]. diff --git a/PROJECT_INDEX.md b/PROJECT_INDEX.md index 6a17d05..0b90083 100644 --- a/PROJECT_INDEX.md +++ b/PROJECT_INDEX.md @@ -6,7 +6,7 @@ look, change the generator (tools/precommit_index.py), not this file. Machine-readable form: tools/index_tags.json --> - + ## Tag Index @@ -15,12 +15,12 @@ Grep a tag name below to get every matching file in one line, without reading th - `avionics-hardware` (396): avionics/AGENTS.md, avionics/LICENSE, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/datasheets/6391731564544371956530548.pdf, avionics/datasheets/749010012A.pdf, avionics/datasheets/987651-1223.pdf, avionics/datasheets/DS3218 datasheet.pdf, avionics/datasheets/DS3225 datasheet.pdf, avionics/datasheets/FOCSineESCCANCommunicationProtocolV30.pdf, avionics/datasheets/KSZ9477S-Data-Sheet-DS00002392C.pdf, avionics/datasheets/L-1038e.A5_phyCORE-AM62x_HW Manual.pdf, avionics/datasheets/MT6701_Datasheet_Rev.1.9.pdf, avionics/datasheets/PCA9555.pdf, avionics/datasheets/S32K-RM.pdf, avionics/datasheets/S32K1xx.pdf, avionics/datasheets/SAM-M10Q_DataSheet_UBX-22013293.pdf, avionics/datasheets/SLB_9670VQ20_Infineon.pdf, avionics/datasheets/SLB_9672XU20_Infineon.pdf, avionics/datasheets/SM04B-SRSS-TB.pdf, avionics/datasheets/SM1553-Series_HiRel-Data-Bus-Pulse-Transformer_RevD.pdf, avionics/datasheets/SRF2012A.pdf, avionics/datasheets/Vishay_45273MDSK.pdf, avionics/datasheets/X2YDatasheet.pdf, avionics/datasheets/X2Y_15-2237598.pdf, avionics/datasheets/adin1300.pdf, avionics/datasheets/adm2795e.pdf, avionics/datasheets/ak7455-en-datasheet-myakm.pdf, avionics/datasheets/bsh-bth.pdf, avionics/datasheets/bst-bmp388-ds001.pdf, avionics/datasheets/bth-xxx-xx-x-d-xx-footprint.pdf, avionics/datasheets/ds-000347-icm-42688-p-v1.6.pdf, avionics/datasheets/ds26lv31qml.pdf, avionics/datasheets/ds26lv32at.pdf, avionics/datasheets/eGH.pdf, avionics/datasheets/infineon-optiga-trust-m-datasheet-en.pdf, avionics/datasheets/iso6442.pdf, avionics/datasheets/isow1044.pdf, avionics/datasheets/isow1412.pdf, avionics/datasheets/mechanics-of-advanced-composite-structures.pdf, avionics/datasheets/mspm0g3507-q1.pdf, avionics/datasheets/mspm0g3507.pdf, avionics/datasheets/mspm0g3518-q1.pdf, avionics/datasheets/slaae29a.pdf, avionics/datasheets/slaae76e.pdf, avionics/datasheets/slaaet8a.pdf, avionics/datasheets/slau846e.pdf, avionics/datasheets/slaz742g.pdf, avionics/datasheets/slb9672.pdf, avionics/emi-hardening/TODO.md, avionics/emi-hardening/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/CMakeLists.txt, avionics/firmware/README.md, avionics/firmware/TODO.md, avionics/firmware/WBS.md, avionics/firmware/cn/CMakeLists.txt, avionics/firmware/cn/src/main.c, avionics/firmware/cn/src/si5351.c, avionics/firmware/cn/src/si5351.h, avionics/firmware/cn/src/xcvr_kiss.c, avionics/firmware/cn/src/xcvr_kiss.h, avionics/firmware/common/CMakeLists.txt, avionics/firmware/common/include/ax25_types.h, avionics/firmware/common/include/failsafe_config.h, avionics/firmware/common/include/kiss_types.h, avionics/firmware/common/include/sbus_input.h, avionics/firmware/common/src/sbus_input.c, avionics/firmware/dts/Makefile, avionics/firmware/dts/README.md, avionics/firmware/dts/cape-a/k3-am6254-pocketbeagle2-serenity-cape-a2.dts, avionics/firmware/dts/cape-b/k3-am6254-pocketbeagle2-serenity-cape-b2.dts, avionics/firmware/fc/CMakeLists.txt, avionics/firmware/fc/src/bmon_ina2xx.c, avionics/firmware/fc/src/bmon_ina2xx.h, avionics/firmware/fc/src/cell_mon_bq769x0.c, avionics/firmware/fc/src/cell_mon_bq769x0.h, avionics/firmware/fc/src/governor_config.h, avionics/firmware/fc/src/mag_mmc5983ma.c, avionics/firmware/fc/src/mag_mmc5983ma.h, avionics/firmware/fc/src/mag_qmc5883l.c, avionics/firmware/fc/src/mag_qmc5883l.h, avionics/firmware/fc/src/main.c, avionics/firmware/fc/src/pwr_fault.c, avionics/firmware/fc/src/pwr_fault.h, avionics/firmware/fc/tools/.gitignore, avionics/firmware/fc/tools/governor_cal.py, avionics/firmware/fc/tools/requirements.txt, avionics/kicad/.gitignore, avionics/kicad/4_Run KiCad ERC_DRC validator (changed files only).txt, avionics/kicad/CAN-PERIPH-GW-1/CAN-PERIPH-GW-1.md, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Adhesive.gba, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Courtyard.gbr, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Cu.gbl, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Fab.gbr, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Mask.gbs, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Paste.gbp, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-B_Silkscreen.gbo, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-Edge_Cuts.gm1, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Adhesive.gta, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Courtyard.gbr, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Cu.gtl, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Fab.gbr, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Mask.gts, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Paste.gtp, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-F_Silkscreen.gto, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-Margin.gbr, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1-job.gbrjob, avionics/kicad/CAN-PERIPH-GW-1/gerbers/CAN-PERIPH-GW-1.drl, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.dsn, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.kicad_pcb, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.kicad_pcb.pre-g351x, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.kicad_prl, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.kicad_pro, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.kicad_sch, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.kicad_sch.pre-g351x, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.net, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1.ses, avionics/kicad/CAN-PERIPH-GW-1/kicads/CAN-PERIPH-GW-1_v2.ses, avionics/kicad/CAN-PERIPH-GW-1/scripts/gen_can_periph_gw_pcb.py, avionics/kicad/CAN-PERIPH-GW-1/scripts/gen_can_periph_gw_sch.py, avionics/kicad/CAN-PERIPH-GW-1/scripts/route_can_periph_gw_pcb.py, avionics/kicad/Commo/Commo.md, avionics/kicad/Commo/gerbers/Commo-B_Courtyard.gbr, avionics/kicad/Commo/gerbers/Commo-B_Cu.gbl, avionics/kicad/Commo/gerbers/Commo-B_Fab.gbr, avionics/kicad/Commo/gerbers/Commo-B_Mask.gbs, avionics/kicad/Commo/gerbers/Commo-B_Paste.gbp, avionics/kicad/Commo/gerbers/Commo-B_Silkscreen.gbo, avionics/kicad/Commo/gerbers/Commo-Edge_Cuts.gm1, avionics/kicad/Commo/gerbers/Commo-F_Courtyard.gbr, avionics/kicad/Commo/gerbers/Commo-F_Cu.gtl, avionics/kicad/Commo/gerbers/Commo-F_Fab.gbr, avionics/kicad/Commo/gerbers/Commo-F_Mask.gts, avionics/kicad/Commo/gerbers/Commo-F_Paste.gtp, avionics/kicad/Commo/gerbers/Commo-F_Silkscreen.gto, avionics/kicad/Commo/gerbers/Commo-In1_Cu.g1, avionics/kicad/Commo/gerbers/Commo-In2_Cu.g2, avionics/kicad/Commo/gerbers/Commo-Margin.gbr, avionics/kicad/Commo/gerbers/Commo-in1-back.drl, avionics/kicad/Commo/gerbers/Commo-job.gbrjob, avionics/kicad/Commo/gerbers/Commo.drl, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Mask.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Paste.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Silkscreen.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-Edge_Cuts.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Mask.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Paste.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Silkscreen.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-In1_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-In2_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-job.gbrjob, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2.drl, avionics/kicad/Commo/kicads/Commo.kicad_pcb, avionics/kicad/Commo/kicads/Commo.kicad_prl, avionics/kicad/Commo/kicads/Commo.kicad_pro, avionics/kicad/Commo/kicads/Commo.kicad_sch, avionics/kicad/Commo/scripts/cleanup_commo_drc.py, avionics/kicad/Commo/scripts/gen_commo_sch.py, avionics/kicad/Commo/scripts/inject_commo_tpm.py, avionics/kicad/Commo/scripts/mod_commo_pcb.py, avionics/kicad/Commo/scripts/route_commo_rssi.py, avionics/kicad/ENC-NACELLE-1.kicad_pcb, avionics/kicad/ENC-NACELLE-1.kicad_prl, avionics/kicad/ENC-NACELLE-1.kicad_pro, avionics/kicad/ENC-NACELLE-1.kicad_sch, avionics/kicad/ENC-NACELLE-1.md, avionics/kicad/ENC-NACELLE-1.net, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/GENERATOR_DRIFT_ANALYSIS.md, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Adhesive.gba, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Courtyard.gbr, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Cu.gbl, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Fab.gbr, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Mask.gbs, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Paste.gbp, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-B_Silkscreen.gbo, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-Edge_Cuts.gm1, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Adhesive.gta, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Courtyard.gbr, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Cu.gtl, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Fab.gbr, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Mask.gts, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Paste.gtp, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-F_Silkscreen.gto, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-In1_Cu.g1, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-Margin.gbr, avionics/kicad/FlightEngineer/gerbers/FlightEngineer-job.gbrjob, avionics/kicad/FlightEngineer/gerbers/FlightEngineer.drl, avionics/kicad/FlightEngineer/kicads/FlightEngineer.kicad_pcb, avionics/kicad/FlightEngineer/kicads/FlightEngineer.kicad_prl, avionics/kicad/FlightEngineer/kicads/FlightEngineer.kicad_pro, avionics/kicad/FlightEngineer/kicads/FlightEngineer.kicad_sch, avionics/kicad/FlightEngineer/kicads/Kaylee.kicad_sch.pre-g351x, avionics/kicad/FlightEngineer/scripts/fix_flight_engineer_pin_snap.py, avionics/kicad/FlightEngineer/scripts/fix_flight_engineer_revs1_cleanup.py, avionics/kicad/FlightEngineer/scripts/fix_flight_engineer_yinv.py, avionics/kicad/FlightEngineer/scripts/gen_flight_engineer.py, avionics/kicad/FlightEngineer/scripts/gen_flight_engineer_pcb.py, avionics/kicad/FlightEngineer/scripts/gen_flight_engineer_revs1.py, avionics/kicad/FlightEngineer/scripts/inject_flight_engineer_trust_module.py, avionics/kicad/FlightEngineer/scripts/mod_flight_engineer_pcb_revs1.py, avionics/kicad/Observer/OBSERVER_SOM_NETMAP.md, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/Observer.pretty/DS_Camera_9P.kicad_mod, avionics/kicad/Observer/Observer.pretty/DS_Laser_2P.kicad_mod, avionics/kicad/Observer/Observer.pretty/DS_ToF_4P.kicad_mod, avionics/kicad/Observer/Observer.pretty/TQFP-128-1EP_KSZ9477_14x14mm_P0.4mm_EP10x10.kicad_mod, avionics/kicad/Observer/Observer.pretty/Wurth_749010012A_10-100BASE-TX.kicad_mod, avionics/kicad/Observer/Observer.pretty/phyCORE-AM62x_PCM071_2xBTH-060.kicad_mod, avionics/kicad/Observer/Observer.pretty/phyCORE-AM62x_PCM071_placement.kicad_mod, avionics/kicad/Observer/Observer_som_pinmap.csv, avionics/kicad/Observer/PCBNEW_SWIG_BUG.md, avionics/kicad/Observer/VERA_NOSE_TRAPEZOID.md, avionics/kicad/Observer/Vera.pretty/DS_Camera_9P.kicad_mod, avionics/kicad/Observer/Vera.pretty/DS_Laser_2P.kicad_mod, avionics/kicad/Observer/Vera.pretty/DS_ToF_4P.kicad_mod, avionics/kicad/Observer/Vera.pretty/phyCORE-AM62x_PCM071_2xBTH-060.kicad_mod, avionics/kicad/Observer/Vera.pretty/phyCORE-AM62x_PCM071_placement.kicad_mod, avionics/kicad/Observer/gen_vera_bth060_footprint.py, avionics/kicad/Observer/gen_vera_ds_footprints.py, avionics/kicad/Observer/gen_vera_som_pcm071.py, avionics/kicad/Observer/kicads/Jayne.kicad_sch.pre-g351x, avionics/kicad/Observer/kicads/Observer.kicad_dru, avionics/kicad/Observer/kicads/Observer.kicad_pcb, avionics/kicad/Observer/kicads/Observer.kicad_pcb.pre-g351x, avionics/kicad/Observer/kicads/Observer.kicad_prl, avionics/kicad/Observer/kicads/Observer.kicad_pro, avionics/kicad/Observer/kicads/Observer.kicad_sch, avionics/kicad/Observer/kicads/Observer.net, avionics/kicad/Observer/kicads/_autosave-Observer.kicad_pcb, avionics/kicad/Observer/kicads/fp-lib-table, avionics/kicad/Observer/mod_vera_corners.py, avionics/kicad/Observer/mod_vera_ds_pcb.py, avionics/kicad/Observer/mod_vera_som_place.py, avionics/kicad/Observer/mod_vera_trapezoid.py, avionics/kicad/Observer/scripts/gen_observer.py, avionics/kicad/Observer/scripts/gen_observer_bth060_footprint.py, avionics/kicad/Observer/scripts/gen_observer_carrier_pcb.py, avionics/kicad/Observer/scripts/gen_observer_carrier_sch.py, avionics/kicad/Observer/scripts/gen_observer_ds_footprints.py, avionics/kicad/Observer/scripts/gen_observer_ic_symbols.py, avionics/kicad/Observer/scripts/gen_observer_ksz_symbol.py, avionics/kicad/Observer/scripts/gen_observer_pcb.py, avionics/kicad/Observer/scripts/gen_observer_som_pcm071.py, avionics/kicad/Observer/scripts/gen_observer_som_symbol.py, avionics/kicad/Observer/scripts/mod_observer_corners.py, avionics/kicad/Observer/scripts/mod_observer_ds_pcb.py, avionics/kicad/Observer/scripts/mod_observer_som_place.py, avionics/kicad/Observer/scripts/mod_observer_trapezoid.py, avionics/kicad/Pilot/PILOT_FOOTPRINT_VERIFICATION.md, avionics/kicad/Pilot/Pilot.md, avionics/kicad/Pilot/gerbers/Pilot-B_Cu.gbr, avionics/kicad/Pilot/gerbers/Pilot-B_Mask.gbr, avionics/kicad/Pilot/gerbers/Pilot-B_Paste.gbr, avionics/kicad/Pilot/gerbers/Pilot-B_Silkscreen.gbr, avionics/kicad/Pilot/gerbers/Pilot-Edge_Cuts.gbr, avionics/kicad/Pilot/gerbers/Pilot-F_Cu.gbr, avionics/kicad/Pilot/gerbers/Pilot-F_Mask.gbr, avionics/kicad/Pilot/gerbers/Pilot-F_Paste.gbr, avionics/kicad/Pilot/gerbers/Pilot-F_Silkscreen.gbr, avionics/kicad/Pilot/gerbers/Pilot-In1_Cu.gbr, avionics/kicad/Pilot/gerbers/Pilot-In2_Cu.gbr, avionics/kicad/Pilot/gerbers/Pilot-job.gbrjob, avionics/kicad/Pilot/gerbers/Pilot.drl, avionics/kicad/Pilot/kicads/Pilot.kicad_pcb, avionics/kicad/Pilot/kicads/Pilot.kicad_prl, avionics/kicad/Pilot/kicads/Pilot.kicad_pro, avionics/kicad/Pilot/kicads/Pilot.kicad_sch, avionics/kicad/Pilot/kicads/Pilot_rebuild.kicad_sch, avionics/kicad/Pilot/kicads/sym-lib-table, avionics/kicad/Pilot/scripts/gen_pilot_sch.py, avionics/kicad/Pilot/scripts/mod_pilot_pcb_reconcile.py, avionics/kicad/README.md, avionics/kicad/Serenity-Custom.pretty/MountingHole_2.2mm_M2_SelfTap_Compact.kicad_mod, avionics/kicad/Serenity-Custom.pretty/Pigtail_7W_DirectSolder.kicad_mod, avionics/kicad/Serenity-Custom.pretty/TI_WL1837MOD.kicad_mod, avionics/kicad/Serenity-Custom.pretty/uBlox_SAM-M10Q-00B.kicad_mod, avionics/kicad/TODO-1.2b-CHECKLIST.md, avionics/kicad/TODO-1.2b-KICAD-READY.md, avionics/kicad/TODO-1.2b-STATUS-REPORT.md, avionics/kicad/XO/XO.md, avionics/kicad/XO/gerbers/XO-B_Cu.gbr, avionics/kicad/XO/gerbers/XO-B_Mask.gbr, avionics/kicad/XO/gerbers/XO-B_Paste.gbr, avionics/kicad/XO/gerbers/XO-B_Silkscreen.gbr, avionics/kicad/XO/gerbers/XO-Edge_Cuts.gbr, avionics/kicad/XO/gerbers/XO-F_Cu.gbr, avionics/kicad/XO/gerbers/XO-F_Mask.gbr, avionics/kicad/XO/gerbers/XO-F_Paste.gbr, avionics/kicad/XO/gerbers/XO-F_Silkscreen.gbr, avionics/kicad/XO/gerbers/XO-In1_Cu.gbr, avionics/kicad/XO/gerbers/XO-In2_Cu.gbr, avionics/kicad/XO/gerbers/XO-job.gbrjob, avionics/kicad/XO/gerbers/XO.drl, avionics/kicad/XO/kicads/XO.kicad_pcb, avionics/kicad/XO/kicads/XO.kicad_prl, avionics/kicad/XO/kicads/XO.kicad_pro, avionics/kicad/XO/kicads/XO.kicad_sch, avionics/kicad/XO/ref_remap_2026-07-18.json, avionics/kicad/add_eth_phy.py, avionics/kicad/add_sensors_sbus.py, avionics/kicad/apply_netlist.py, avionics/kicad/check_impedance.py, avionics/kicad/complete_1_2b.py, avionics/kicad/complete_xcvr_49mhz2.py, avionics/kicad/drc_report.txt, avionics/kicad/fix_pilot_xo_isolators.py, avionics/kicad/fix_starved_thermal_pads.py, avionics/kicad/fix_xcvr_labels.py, avionics/kicad/fp-lib-table, avionics/kicad/gen_enc_nacelle_pcb.py, avionics/kicad/generate_gerbers.py, avionics/kicad/generate_gerbers_rev_s1.py, avionics/kicad/gerbers/CAPE-B-2-S1/XO-B_Courtyard.gbr, avionics/kicad/gerbers/CAPE-B-2-S1/XO-B_Cu.gbl, avionics/kicad/gerbers/CAPE-B-2-S1/XO-B_Fab.gbr, avionics/kicad/gerbers/CAPE-B-2-S1/XO-B_Mask.gbs, avionics/kicad/gerbers/CAPE-B-2-S1/XO-B_Paste.gbp, avionics/kicad/gerbers/CAPE-B-2-S1/XO-B_Silkscreen.gbo, avionics/kicad/gerbers/CAPE-B-2-S1/XO-Edge_Cuts.gm1, avionics/kicad/gerbers/CAPE-B-2-S1/XO-F_Courtyard.gbr, avionics/kicad/gerbers/CAPE-B-2-S1/XO-F_Cu.gtl, avionics/kicad/gerbers/CAPE-B-2-S1/XO-F_Fab.gbr, avionics/kicad/gerbers/CAPE-B-2-S1/XO-F_Mask.gts, avionics/kicad/gerbers/CAPE-B-2-S1/XO-F_Paste.gtp, avionics/kicad/gerbers/CAPE-B-2-S1/XO-F_Silkscreen.gto, avionics/kicad/gerbers/CAPE-B-2-S1/XO-In1_Cu.g1, avionics/kicad/gerbers/CAPE-B-2-S1/XO-In2_Cu.g2, avionics/kicad/gerbers/CAPE-B-2-S1/XO-Margin.gbr, avionics/kicad/gerbers/CAPE-B-2-S1/XO-job.gbrjob, avionics/kicad/gerbers/CAPE-B-2-S1/XO.drl/XO.drl, avionics/kicad/gerbers/Commo-S1/Commo-B_Courtyard.gbr, avionics/kicad/gerbers/Commo-S1/Commo-B_Cu.gbl, avionics/kicad/gerbers/Commo-S1/Commo-B_Fab.gbr, avionics/kicad/gerbers/Commo-S1/Commo-B_Mask.gbs, avionics/kicad/gerbers/Commo-S1/Commo-B_Paste.gbp, avionics/kicad/gerbers/Commo-S1/Commo-B_Silkscreen.gbo, avionics/kicad/gerbers/Commo-S1/Commo-Edge_Cuts.gm1, avionics/kicad/gerbers/Commo-S1/Commo-F_Courtyard.gbr, avionics/kicad/gerbers/Commo-S1/Commo-F_Cu.gtl, avionics/kicad/gerbers/Commo-S1/Commo-F_Fab.gbr, avionics/kicad/gerbers/Commo-S1/Commo-F_Mask.gts, avionics/kicad/gerbers/Commo-S1/Commo-F_Paste.gtp, avionics/kicad/gerbers/Commo-S1/Commo-F_Silkscreen.gto, avionics/kicad/gerbers/Commo-S1/Commo-In1_Cu.g1, avionics/kicad/gerbers/Commo-S1/Commo-In2_Cu.g2, avionics/kicad/gerbers/Commo-S1/Commo-Margin.gbr, avionics/kicad/gerbers/Commo-S1/Commo-job.gbrjob, avionics/kicad/gerbers/Commo-S1/Commo.drl/Commo.drl, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Adhesive.gba, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Courtyard.gbr, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Cu.gbl, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Fab.gbr, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Mask.gbs, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Paste.gbp, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-B_Silkscreen.gbo, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-Edge_Cuts.gm1, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Adhesive.gta, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Courtyard.gbr, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Cu.gtl, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Fab.gbr, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Mask.gts, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Paste.gtp, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-F_Silkscreen.gto, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-In1_Cu.g1, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-Margin.gbr, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer-job.gbrjob, avionics/kicad/gerbers/FlightEngineer-S1/FlightEngineer.drl/FlightEngineer.drl, avionics/kicad/replace_footprints.py, avionics/kicad/retarget_mspm0g351x_slb9672.py, avionics/kicad/retarget_pcb_footprints.py, avionics/kicad/symbols/ISOW1044BDFMR_pinmap.csv, avionics/kicad/symbols/KSZ9477STXI_pinmap.csv, avionics/kicad/symbols/MSPM0G3507SRGZR_pinmap.csv, avionics/kicad/symbols/MSPM0G3518_Q1_RHB.kicad_sym, avionics/kicad/symbols/MSPM0G3519_Q1_RGZ.kicad_sym, avionics/kicad/symbols/Observer_ISOW1044BDFMR.kicad_sym, avionics/kicad/symbols/Observer_KSZ9477.kicad_sym, avionics/kicad/symbols/Observer_MSPM0G3507_RGZ.kicad_sym, avionics/kicad/symbols/Observer_SoM.kicad_sym, avionics/kicad/symbols/Observer_SoM_PCM071.kicad_sym, avionics/kicad/symbols/SLB9672XU2_0_pinmap.csv, avionics/kicad/symbols/SLB9672_TPM.kicad_sym, avionics/kicad/symbols/Vera_SoM_PCM071.kicad_sym, avionics/kicad/symbols/footprints/PHYCORE-AM62AX-DSC-FOOTPRINT.kicad_mod, avionics/kicad/symbols/phyCORE_AM62x_PCM071_pinmap.csv, avionics/mosfet-search.txt, avionics/observer/TODO.md, avionics/observer/WBS.md, avionics/rev-s1/TODO.md, avionics/rev-s1/WBS.md - `bom` (13): airframe/diagrams/ring_frames/ring_cargo_Y30_inner.csv, airframe/diagrams/ring_frames/ring_rear_Y290_inner.csv, avionics/kicad/Observer/Observer_som_pinmap.csv, avionics/kicad/symbols/ISOW1044BDFMR_pinmap.csv, avionics/kicad/symbols/KSZ9477STXI_pinmap.csv, avionics/kicad/symbols/MSPM0G3507SRGZR_pinmap.csv, avionics/kicad/symbols/SLB9672XU2_0_pinmap.csv, avionics/kicad/symbols/phyCORE_AM62x_PCM071_pinmap.csv, code-scanning-files-extracted.csv, current-specification/bom_revS.csv, current-specification/bom_revS.json, docs/bom_revR.json, tools/compact_bom_entries.py - `build-guide` (151): graphical-build-guide/AGENTS.md, graphical-build-guide/BUILD_GUIDE_TEMPLATE.md, graphical-build-guide/LICENSE, graphical-build-guide/README.md, graphical-build-guide/REVN_BUILD_GUIDE_24IN.md, graphical-build-guide/TODO-old.md, graphical-build-guide/TODO.md, graphical-build-guide/WBS.md, graphical-build-guide/build_guide_00_cover.svg, graphical-build-guide/build_guide_01_print_prep.svg, graphical-build-guide/build_guide_02_print_hull.svg, graphical-build-guide/build_guide_03_print_nacelle.svg, graphical-build-guide/build_guide_04_cut_cf.svg, graphical-build-guide/build_guide_05_cf_skeleton.svg, graphical-build-guide/build_guide_06_nacelle_pivot.svg, graphical-build-guide/build_guide_07_edf_install.svg, graphical-build-guide/build_guide_08_nozzle_gear.svg, graphical-build-guide/build_guide_09_avionics.svg, graphical-build-guide/build_guide_10_power_wiring.svg, graphical-build-guide/build_guide_11_inter_board.svg, graphical-build-guide/build_guide_12_security_hw.svg, graphical-build-guide/build_guide_13_nav_lights.svg, graphical-build-guide/build_guide_14_antennas.svg, graphical-build-guide/build_guide_15_software.svg, graphical-build-guide/build_guide_16_calibration.svg, graphical-build-guide/build_guide_17_ground_test.svg, graphical-build-guide/build_guide_18_first_flight.svg, graphical-build-guide/build_guide_19_decal_placement.svg, graphical-build-guide/build_guide_20_node_placement.svg, graphical-build-guide/build_guide_21_node_install.svg, graphical-build-guide/build_guide_22_void_formers.svg, graphical-build-guide/build_guide_23_foam_fill.svg, graphical-build-guide/build_guide_24_access_panels.svg, graphical-build-guide/build_guide_25_obstacle_sensors.svg, graphical-build-guide/build_guide_26_cargo_bay_winch.svg, graphical-build-guide/build_plan.svg, graphical-build-guide/components_overview.svg, graphical-build-guide/decal_sheet.svg, graphical-build-guide/flight-phases/TODO.md, graphical-build-guide/flight-phases/WBS.md, graphical-build-guide/gen_hull_outlines.py, graphical-build-guide/hull_bottom.svg, graphical-build-guide/hull_front.svg, graphical-build-guide/hull_side.svg, graphical-build-guide/hull_top.svg, graphical-build-guide/overview_bottom.svg, graphical-build-guide/overview_front.svg, graphical-build-guide/overview_side.svg, graphical-build-guide/overview_svgs/serenity_bottom.svg, graphical-build-guide/overview_svgs/serenity_bow.svg, graphical-build-guide/overview_svgs/serenity_iso_port_bow.svg, graphical-build-guide/overview_svgs/serenity_iso_port_quarter.svg, graphical-build-guide/overview_svgs/serenity_iso_starboard_bow.svg, graphical-build-guide/overview_svgs/serenity_iso_stbd_quarter.svg, graphical-build-guide/overview_svgs/serenity_port.svg, graphical-build-guide/overview_svgs/serenity_starboard.svg, graphical-build-guide/overview_svgs/serenity_stern.svg, graphical-build-guide/overview_svgs/serenity_top.svg, graphical-build-guide/overview_top.svg, graphical-build-guide/pngs/01_port.png, graphical-build-guide/pngs/02_stbd.png, graphical-build-guide/pngs/03_top.png, graphical-build-guide/pngs/04_bottom.png, graphical-build-guide/pngs/05_bow.png, graphical-build-guide/pngs/06_stern.png, graphical-build-guide/pngs/07_iso_port_bow_dorsal.png, graphical-build-guide/pngs/08_iso_stbd_bow_dorsal.png, graphical-build-guide/pngs/09_iso_port_stern_dorsal.png, graphical-build-guide/pngs/10_iso_stbd_stern_dorsal.png, graphical-build-guide/pngs/11_iso_port_bow_ventral.png, graphical-build-guide/pngs/12_iso_stbd_bow_ventral.png, graphical-build-guide/pngs/13_iso_port_stern_ventral.png, graphical-build-guide/pngs/14_iso_stbd_stern_ventral.png, graphical-build-guide/pngs/15_closeup_nose.png, graphical-build-guide/pngs/16_closeup_gear.png, graphical-build-guide/pngs/17_closeup_nacelle.png, graphical-build-guide/probe_stl.py, graphical-build-guide/shellview/cargo_port.png, graphical-build-guide/shellview/cargo_stbd.png, graphical-build-guide/shellview/cargoax_front.png, graphical-build-guide/shellview/cargoax_iso.png, graphical-build-guide/shellview/cargoax_stbd.png, graphical-build-guide/shellview/cargoax_top.png, graphical-build-guide/shellview/fine2_starb.png, graphical-build-guide/shellview/fine_starb.png, graphical-build-guide/shellview/fine_top.png, graphical-build-guide/shellview/grid_starb.png, graphical-build-guide/shellview/hd_s_fwdtop.png, graphical-build-guide/shellview/hd_sf_hi.png, graphical-build-guide/shellview/hd_sf_lo.png, graphical-build-guide/shellview/hd_sf_mid.png, graphical-build-guide/shellview/head_front_Yneg.png, graphical-build-guide/shellview/head_front_Yneg_rg.png, graphical-build-guide/shellview/head_iso.png, graphical-build-guide/shellview/head_port_Xpos.png, graphical-build-guide/shellview/head_stbd_Xneg.png, graphical-build-guide/shellview/head_top_Zpos.png, graphical-build-guide/shellview/head_xsec.png, graphical-build-guide/shellview/head_xsec_zoom.png, graphical-build-guide/shellview/headax_front.png, graphical-build-guide/shellview/headax_iso.png, graphical-build-guide/shellview/headax_stbd.png, graphical-build-guide/shellview/headax_top.png, graphical-build-guide/shellview/headfin_s_fwdtop.png, graphical-build-guide/shellview/headfin_sf_hi.png, graphical-build-guide/shellview/headfin_sf_lo.png, graphical-build-guide/shellview/headfin_sf_mid.png, graphical-build-guide/shellview/headfin_sf_mid_rg.png, graphical-build-guide/shellview/inara_engrave.png, graphical-build-guide/shellview/inner_op4_front_Yneg.png, graphical-build-guide/shellview/inner_op4_iso.png, graphical-build-guide/shellview/inner_op4_port_Xpos.png, graphical-build-guide/shellview/inner_op4_stbd_Xneg.png, graphical-build-guide/shellview/inner_op4_top_Zpos.png, graphical-build-guide/shellview/inner_orig_front_Yneg.png, graphical-build-guide/shellview/inner_orig_iso.png, graphical-build-guide/shellview/inner_orig_port_Xpos.png, graphical-build-guide/shellview/inner_orig_stbd_Xneg.png, graphical-build-guide/shellview/inner_orig_top_Zpos.png, graphical-build-guide/shellview/inner_vop_front_Yneg.png, graphical-build-guide/shellview/inner_vop_iso.png, graphical-build-guide/shellview/inner_vop_port_Xpos.png, graphical-build-guide/shellview/inner_vop_stbd_Xneg.png, graphical-build-guide/shellview/inner_vop_top_Zpos.png, graphical-build-guide/shellview/mk_fwdtop.png, graphical-build-guide/shellview/mk_stbd.png, graphical-build-guide/shellview/port_wall.stl, graphical-build-guide/shellview/rear_front_Yneg.png, graphical-build-guide/shellview/rear_iso.png, graphical-build-guide/shellview/rear_port_Xpos.png, graphical-build-guide/shellview/rear_stbd_Xneg.png, graphical-build-guide/shellview/rear_top_Zpos.png, graphical-build-guide/shellview/rinner_op_s_fwdtop.png, graphical-build-guide/shellview/rinner_op_sf_hi.png, graphical-build-guide/shellview/rinner_op_sf_lo.png, graphical-build-guide/shellview/rinner_op_sf_mid.png, graphical-build-guide/shellview/rinner_orig_s_fwdtop.png, graphical-build-guide/shellview/rinner_orig_sf_hi.png, graphical-build-guide/shellview/rinner_orig_sf_lo.png, graphical-build-guide/shellview/rinner_orig_sf_mid.png, graphical-build-guide/shellview/rio_front_Yneg.png, graphical-build-guide/shellview/rio_iso.png, graphical-build-guide/shellview/rio_port_Xpos.png, graphical-build-guide/shellview/rio_stbd_Xneg.png, graphical-build-guide/shellview/rio_top_Zpos.png, graphical-build-guide/shellview/rop_front_Yneg.png, graphical-build-guide/shellview/rop_iso.png, graphical-build-guide/shellview/rop_port_Xpos.png, graphical-build-guide/shellview/rop_stbd_Xneg.png, graphical-build-guide/shellview/rop_top_Zpos.png, graphical-build-guide/update_overview_paths.py -- `build-tooling` (41): tools/AGENTS.md, tools/LICENSE, tools/README.md, tools/TODO.md, tools/TOOL_REFERENCE.md, tools/add_landing_gear_bosses.py, tools/bake_hull_frame.py, tools/build_head_shell.py, tools/build_landing_gear_views.py, tools/cargo_bay_envelope.py, tools/compact_bom_entries.py, tools/export-specctra-dsn.py, tools/export_landing_gear_stls.py, tools/import-specctra-ses.py, tools/landing_gear_bay_pad_fit.py, tools/landing_gear_bay_seat_fit.py, tools/landing_gear_bay_station_fit.py, tools/landing_gear_cowl_clearance.py, tools/landing_gear_foot_stance.py, tools/landing_gear_opening_fit.py, tools/landing_gear_r6_sizing.py, tools/landing_gear_wing_clearance.py, tools/mirror_claude_memory.py, tools/nacelle_esc_service.py, tools/open_mating_faces.py, tools/precommit_index.py, tools/precommit_kicad_load.py, tools/precommit_sanitize.py, tools/purge_stale_fcstd_objects.py, tools/tests/test_nacelle_esc_service.py, tools/validate_kicad.py, tools/validate_stls.py, tools/verify_bow_pod.py, tools/wing_airfoil_integrity.py, tools/wing_airfoil_variants.py, tools/wing_cfd_openfoam.py, tools/wing_internal_clearance.py, tools/wing_root_deconflict.py, tools/wing_section_figure.py, tools/wing_spar_carrythrough.py, tools/wing_spar_station_fit.py -- `cad-mesh` (277): airframe/FreeCAD-scripts/Makefile, airframe/FreeCAD-scripts/faraday-enclosure.py, airframe/FreeCAD-scripts/freecad-stl2part.py, airframe/FreeCAD-scripts/make_flat_pattern.py, airframe/FreeCAD-scripts/serenity_assembly.py, airframe/FreeCAD-scripts/serenity_placeholders_assembly.py, airframe/blender-scripts/add_structural_features.py, airframe/blender-scripts/blender_edf_bore_and_petals.py, airframe/blender-scripts/blender_hollow_shells.py, airframe/blender-scripts/blender_intake_cut.py, airframe/blender-scripts/blender_middle_intake_cut.py, airframe/blender-scripts/blender_nacelle_revo.py, airframe/blender-scripts/blender_nozzle_gen.py, airframe/blender-scripts/blender_shells_2mm_solidify.py, airframe/blender-scripts/blender_shells_v3.py, airframe/blender-scripts/blender_stator_gen.py, airframe/blender-scripts/check_nacelle_alignment.py, airframe/blender-scripts/engrave_plaques.py, airframe/blender-scripts/engrave_shuttles.py, airframe/blender-scripts/files-hollowed-24in/cargo_sect_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/head_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/middle_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_flight_engineer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_inara.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_river.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_shepherd.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_simon.stl, airframe/blender-scripts/files-hollowed-24in/rear_shell24_2mm_repaired.stl, airframe/blender-scripts/fill_thin_details.py, airframe/blender-scripts/finalize_cargo_middle.py, airframe/blender-scripts/finalize_head_rear.py, airframe/blender-scripts/generate_overview_svgs.py, airframe/blender-scripts/hollow_manifold.py, airframe/blender-scripts/inspect_shell_center.py, airframe/blender-scripts/make_bay_text.py, airframe/blender-scripts/make_shuttle_text.py, airframe/blender-scripts/merge_cargo_interior.py, airframe/blender-scripts/merge_head_interior.py, airframe/blender-scripts/morph_open_voxel.py, airframe/blender-scripts/regen_rear_interior.py, airframe/blender-scripts/repair_shells_for_scad.py, airframe/blender-scripts/serenity_render_views.py, airframe/blender-scripts/verify_shells.py, airframe/freecad/assembly/SerenityAssembly.FCStd.bak2, airframe/freecad/assembly/leg_2_scaled-oriented.stl, airframe/openscad/fuselage/access_panels_24in.scad, airframe/openscad/fuselage/battery_tray.scad, airframe/openscad/fuselage/belly_panel.scad, airframe/openscad/fuselage/bow_sensor_faceplate.scad, airframe/openscad/fuselage/bow_sensor_pod.scad, airframe/openscad/fuselage/canonical_leg_r6_1_5in.scad, airframe/openscad/fuselage/canonical_leg_r6_3_0in.scad, airframe/openscad/fuselage/cargo/_export_cargo_port_root_chunk.scad, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/cargo/cargo_spar_drive.scad, airframe/openscad/fuselage/cargo/cargo_vera_faraday.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/landing_leg_assy.scad, airframe/openscad/fuselage/middle_canonical_shell24.scad, airframe/openscad/fuselage/rcrs49_wire_post.scad, airframe/openscad/fuselage/rear_shell24.scad, airframe/openscad/fuselage/wire_brace_leg.scad, airframe/openscad/fuselage/wire_loop_fuse.scad, airframe/openscad/nacelles/_export_inboard_cut.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/edf_aft_spider_sleeve.scad, airframe/openscad/nacelles/edf_stator_sleeve.scad, airframe/openscad/nacelles/gear_option_compare.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/nacelles/nacelle_nozzle_pushrod.scad, airframe/openscad/nacelles/nacelle_pod_50mm_tandem.scad, airframe/openscad/nacelles/nacelle_servo_bracket.scad, airframe/openscad/port_tilt_spar_assembly.scad, airframe/openscad/wings/wings_s1223_revo.scad, airframe/openscad/wings/wings_s1223_revo.scad.backup-2026-07-18, airframe/placeholders/avionics/Cape_A2_PCB_55x35mm.stl, airframe/placeholders/avionics/Cape_B2_PCB_55x35mm.stl, airframe/placeholders/avionics/FlightEngineer_PDB_90x65mm.stl, airframe/placeholders/avionics/PocketBeagle2_Industrial_56x35mm.stl, airframe/placeholders/avionics/XCVR_49MHZ2_PCB_55x35mm.stl, airframe/placeholders/avionics/microSD_64GB.stl, airframe/placeholders/bearings/B6804_20x32x7mm_GCS.stl, airframe/placeholders/bearings/MF104ZZ_4x10x4mm.stl, airframe/placeholders/bearings/MR63ZZ_3x6x2p5mm.stl, airframe/placeholders/cargo/DRV8833_Hbridge_breakout.stl, airframe/placeholders/cargo/Dyneema_SK75_0p5mm_coil.stl, airframe/placeholders/cargo/HX711_loadcell_ADC_breakout.stl, airframe/placeholders/cargo/N20_motor_300RPM_6V.stl, airframe/placeholders/faraday/Far_EMI_vent_40x40x6mm.stl, airframe/placeholders/faraday/Far_FT_panel_55x35mm.stl, airframe/placeholders/faraday/Far_bond_strap_100mm.stl, airframe/placeholders/faraday/Far_cage_AV_70x50x82mm.stl, airframe/placeholders/faraday/Far_fan_40mm_5V.stl, airframe/placeholders/faraday/Far_ferrite_4mm_ID.stl, airframe/placeholders/faraday/Far_gasket_AV_250x6x1mm.stl, airframe/placeholders/faraday/Mal_far_fan_40mm_5V.stl, airframe/placeholders/faraday/Mal_far_gasket_470x8x1p5mm.stl, airframe/placeholders/foam/Foam_fill_cargo_190x200x159mm.stl, airframe/placeholders/foam/Foam_fill_head_125x231x136mm.stl, airframe/placeholders/foam/Foam_fill_middle_horseshoe_173x69x161mm.stl, airframe/placeholders/foam/Foam_fill_rear_136x177x154mm.stl, airframe/placeholders/foam/Void_avionics_bay_62x42x75mm.stl, airframe/placeholders/foam/Void_cargo_bay_120x150x80mm.stl, airframe/placeholders/foam/Void_far_cage_76x56x88mm.stl, airframe/placeholders/foam/Void_far_fan_spur_44x44x50mm.stl, airframe/placeholders/foam/Void_nacelle_pylon_20x80x20mm.stl, airframe/placeholders/foam/Void_power_bus_25x500x25mm.stl, airframe/placeholders/foam/Void_vent_exhaust_20x300x20mm.stl, airframe/placeholders/foam/Void_vent_intake_20x250x20mm.stl, airframe/placeholders/foam/Void_wiring_trunk_30x700x20mm.stl, airframe/placeholders/gcs/AS5600_encoder_breakout_15x15mm.stl, airframe/placeholders/gcs/Antenna_2p4GHz_zigbee_rubber_duck.stl, airframe/placeholders/gcs/Antenna_49MHz_whip_940mm.stl, airframe/placeholders/gcs/Antenna_5GHz_panel_14dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_Yagi_9dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_omni_5dBi.stl, airframe/placeholders/gcs/Antenna_GNSS_patch_ANN_MB00.stl, airframe/placeholders/gcs/B6804_20x32x7mm_gimbal_pan.stl, airframe/placeholders/gcs/GPS_module_M10Q_SparkFun.stl, airframe/placeholders/gcs/N42_disc_magnet_6x2mm.stl, airframe/placeholders/gcs/Pololu_D24V22F6_6V2A_BEC.stl, airframe/placeholders/gcs/Pololu_D24V50F5_5V5A_BEC.stl, airframe/placeholders/gcs/RF_splitter_915MHz_2way_ZFSC.stl, airframe/placeholders/gcs/Skipper_enclosure_IP65_145x90x65mm.stl, airframe/placeholders/gcs/Skipper_tripod_antenna_mast.stl, airframe/placeholders/gcs/TCA9548A_I2C_mux_breakout.stl, airframe/placeholders/gears/Bevel_M1_14T_pair.stl, airframe/placeholders/gears/Bevel_housing_CF_PETG.stl, airframe/placeholders/gears/Pinion_M1_12T_R6mm.stl, airframe/placeholders/gears/Sector_gear_M1_R22mm.stl, airframe/placeholders/hardware/Batt_strap_silicone_16mm_CAM.stl, airframe/placeholders/hardware/Insert_M25_brass_L5.stl, airframe/placeholders/hardware/Insert_M3_brass_L5.stl, airframe/placeholders/hardware/Pin_SS_3x5mm_hinge.stl, airframe/placeholders/hardware/Screw_M3x8mm_button_ISO7380.stl, airframe/placeholders/lighting/WS2812C_2020_SMD.stl, airframe/placeholders/power/Fuse_MAXI_150A.stl, airframe/placeholders/power/Fuse_mini_40A.stl, airframe/placeholders/power/LiPo_4S_10000mAh_175x64x38mm_GCS.stl, airframe/placeholders/power/LiPo_6S_2800mAh_115x35x35mm.stl, airframe/placeholders/power/LiPo_6S_4000mAh_138x44x36mm.stl, airframe/placeholders/power/Shunt_CSS2H_2512K_1mohm.stl, airframe/placeholders/propulsion/EDF_120mm_6S_deferred.stl, airframe/placeholders/propulsion/EDF_50mm_6S.stl, airframe/placeholders/propulsion/ESC_40A_6S_BLHeli32.stl, airframe/placeholders/propulsion/ESC_80A_6S_BLHeli32_deferred.stl, airframe/placeholders/servos/DS3218MG_25kgcm.stl, airframe/placeholders/servos/SG90_micro.stl, airframe/placeholders/structural/CF_bar_6x3mm_620mm_keel.stl, airframe/placeholders/structural/CF_plate_2mm_200x300mm.stl, airframe/placeholders/structural/CF_rod_3mm_300mm_stock.stl, airframe/placeholders/structural/CF_rod_4mm_300mm_stock.stl, airframe/placeholders/structural/CF_tube_12mm_OD_1p5w_350mm_spar.stl, airframe/placeholders/structural/PTFE_sleeve_4mm_OD_3mm_ID_52mm.stl, airframe/placeholders/wiring/PTFE_conduit_4mm_OD_3mm_ID_700mm.stl, airframe/placeholders/wiring/Post_49MHz_base_load_coil.stl, airframe/placeholders/wiring/Wire_10AWG_silicone_400mm.stl, airframe/placeholders/wiring/Wire_28AWG_STP_bundle_500mm.stl, airframe/placeholders/wiring/Wire_49MHz_antenna_0p3mm_470mm.stl, airframe/placeholders/wiring/Wire_4AWG_silicone_200mm_pair.stl, airframe/stls/fuselage/battery_tray.stl, airframe/stls/fuselage/belly_panel.stl, airframe/stls/fuselage/bow_sensor_faceplate.stl, airframe/stls/fuselage/cargo/cargo_cradle_autolatch.stl, airframe/stls/fuselage/cargo/cargo_door_port.stl, airframe/stls/fuselage/cargo/cargo_door_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_door_stbd.stl, airframe/stls/fuselage/cargo/cargo_drv8833_tray.stl, airframe/stls/fuselage/cargo/cargo_fpv_bezel.stl, airframe/stls/fuselage/cargo/cargo_gps_retention_ring.stl, airframe/stls/fuselage/cargo/cargo_hinge_retention.stl, airframe/stls/fuselage/cargo/cargo_release_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_bossed.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_repaired.stl, airframe/stls/fuselage/cargo/cargo_vera_faraday.stl, airframe/stls/fuselage/cargo/cargo_winch_motor_mount.stl, airframe/stls/fuselage/cargo/cargo_winch_spool.stl, airframe/stls/fuselage/cargo_middle_splice_collar.stl, airframe/stls/fuselage/dorsal_antenna_fin.stl, airframe/stls/fuselage/head_cargo_splice_collar.stl, airframe/stls/fuselage/head_shell24.stl, airframe/stls/fuselage/head_shell24_2mm_repaired.stl, airframe/stls/fuselage/inara_access_cover.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_bay.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_nominal.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_foot.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_nominal.stl, airframe/stls/fuselage/middle_canonical_edf_intake.stl, airframe/stls/fuselage/middle_canonical_shell24.stl, airframe/stls/fuselage/middle_rear_splice_collar.stl, airframe/stls/fuselage/middle_shell24_2mm_repaired.stl, airframe/stls/fuselage/rear_shell24_2mm_repaired.stl, airframe/stls/fuselage/river_access_cover.stl, airframe/stls/nacelles/edf_aft_spider_sleeve.stl, airframe/stls/nacelles/edf_stator_sleeve.stl, airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl, airframe/stls/nacelles/eng_right_shell24_50mm_repaired.stl, airframe/stls/nacelles/nacelle_port_revs.stl, airframe/stls/nacelles/nacelle_servo_bracket.stl, airframe/stls/nacelles/nacelle_stbd_revs.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap_seal.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-closed.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-open.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_ring.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_throat.stl, airframe/stls/wings/wing_port_s1223_revo.stl, airframe/stls/wings/wing_stbd_s1223_revo.stl, deferred/aft-edf/openscad/aft_edf_plenum.scad, deferred/aft-edf/openscad/edf_120_motor_mount.scad, deferred/aft-edf/openscad/edf_120_thrust_tube.scad, deferred/aft-edf/openscad/neck_intake_frame.scad, deferred/aft-edf/openscad/rear_neck_intake_shell24.scad, deferred/aft-edf/stls/aft_edf_plenum.stl, deferred/aft-edf/stls/edf_120_motor_mount.stl, deferred/aft-edf/stls/edf_120_thrust_tube.stl, deferred/aft-edf/stls/neck_intake_frame.stl, deferred/aft-edf/stls/rear_nozzle_closed_asm.stl, deferred/aft-edf/stls/rear_nozzle_frame.stl, deferred/aft-edf/stls/rear_nozzle_petal.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_0.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_1.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_2.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_3.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_4.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_5.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_6.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_7.stl, deferred/aft-edf/stls/rear_shell24_2mm_edf_bored.stl, docs/references/thingverse-serenity/files/s_cargo_door.stl, docs/references/thingverse-serenity/files/s_cargo_door_strutts.stl, docs/references/thingverse-serenity/files/s_cargo_sect.stl, docs/references/thingverse-serenity/files/s_eng_left.stl, docs/references/thingverse-serenity/files/s_eng_pistons.stl, docs/references/thingverse-serenity/files/s_eng_piv_outer.stl, docs/references/thingverse-serenity/files/s_eng_piv_pins.stl, docs/references/thingverse-serenity/files/s_eng_right.stl, docs/references/thingverse-serenity/files/s_feet_x_4.stl, docs/references/thingverse-serenity/files/s_head.stl, docs/references/thingverse-serenity/files/s_legs.stl, docs/references/thingverse-serenity/files/s_middle.stl, docs/references/thingverse-serenity/files/s_pivot_arm_a.stl, docs/references/thingverse-serenity/files/s_rear.stl, docs/references/thingverse-serenity/files/s_wings_both.stl, docs/references/vimdrones_can_periph_pico_v1.0.stl, docs/references/vimdrones_esc_s50_v1.0.stl, gcs/skipper/hardware/enclosure/openscad/skipper_field_enclosure.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_mount.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_pan.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_tilt.scad, graphical-build-guide/shellview/port_wall.stl, tools/purge_stale_fcstd_objects.py +- `build-tooling` (42): tools/AGENTS.md, tools/LICENSE, tools/README.md, tools/TODO.md, tools/TOOL_REFERENCE.md, tools/add_landing_gear_bosses.py, tools/bake_hull_frame.py, tools/build_head_shell.py, tools/build_landing_gear_views.py, tools/cargo_bay_envelope.py, tools/compact_bom_entries.py, tools/export-specctra-dsn.py, tools/export_landing_gear_stls.py, tools/import-specctra-ses.py, tools/landing_gear_bay_pad_fit.py, tools/landing_gear_bay_seat_fit.py, tools/landing_gear_bay_station_fit.py, tools/landing_gear_cowl_clearance.py, tools/landing_gear_foot_stance.py, tools/landing_gear_opening_fit.py, tools/landing_gear_r6_sizing.py, tools/landing_gear_wing_clearance.py, tools/mirror_claude_memory.py, tools/nacelle_esc_service.py, tools/open_mating_faces.py, tools/precommit_index.py, tools/precommit_kicad_load.py, tools/precommit_sanitize.py, tools/purge_stale_fcstd_objects.py, tools/spar_bundle_fit.py, tools/tests/test_nacelle_esc_service.py, tools/validate_kicad.py, tools/validate_stls.py, tools/verify_bow_pod.py, tools/wing_airfoil_integrity.py, tools/wing_airfoil_variants.py, tools/wing_cfd_openfoam.py, tools/wing_internal_clearance.py, tools/wing_root_deconflict.py, tools/wing_section_figure.py, tools/wing_spar_carrythrough.py, tools/wing_spar_station_fit.py +- `cad-mesh` (279): airframe/FreeCAD-scripts/Makefile, airframe/FreeCAD-scripts/faraday-enclosure.py, airframe/FreeCAD-scripts/freecad-stl2part.py, airframe/FreeCAD-scripts/make_flat_pattern.py, airframe/FreeCAD-scripts/serenity_assembly.py, airframe/FreeCAD-scripts/serenity_placeholders_assembly.py, airframe/blender-scripts/add_structural_features.py, airframe/blender-scripts/blender_edf_bore_and_petals.py, airframe/blender-scripts/blender_hollow_shells.py, airframe/blender-scripts/blender_intake_cut.py, airframe/blender-scripts/blender_middle_intake_cut.py, airframe/blender-scripts/blender_nacelle_revo.py, airframe/blender-scripts/blender_nozzle_gen.py, airframe/blender-scripts/blender_shells_2mm_solidify.py, airframe/blender-scripts/blender_shells_v3.py, airframe/blender-scripts/blender_stator_gen.py, airframe/blender-scripts/check_nacelle_alignment.py, airframe/blender-scripts/engrave_plaques.py, airframe/blender-scripts/engrave_shuttles.py, airframe/blender-scripts/files-hollowed-24in/cargo_sect_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/head_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/middle_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_flight_engineer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_inara.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_river.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_shepherd.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_simon.stl, airframe/blender-scripts/files-hollowed-24in/rear_shell24_2mm_repaired.stl, airframe/blender-scripts/fill_thin_details.py, airframe/blender-scripts/finalize_cargo_middle.py, airframe/blender-scripts/finalize_head_rear.py, airframe/blender-scripts/generate_overview_svgs.py, airframe/blender-scripts/hollow_manifold.py, airframe/blender-scripts/inspect_shell_center.py, airframe/blender-scripts/make_bay_text.py, airframe/blender-scripts/make_shuttle_text.py, airframe/blender-scripts/merge_cargo_interior.py, airframe/blender-scripts/merge_head_interior.py, airframe/blender-scripts/morph_open_voxel.py, airframe/blender-scripts/regen_rear_interior.py, airframe/blender-scripts/repair_shells_for_scad.py, airframe/blender-scripts/serenity_render_views.py, airframe/blender-scripts/verify_shells.py, airframe/freecad/assembly/SerenityAssembly.FCStd.bak2, airframe/freecad/assembly/leg_2_scaled-oriented.stl, airframe/openscad/fuselage/access_panels_24in.scad, airframe/openscad/fuselage/battery_tray.scad, airframe/openscad/fuselage/belly_panel.scad, airframe/openscad/fuselage/bow_sensor_faceplate.scad, airframe/openscad/fuselage/bow_sensor_pod.scad, airframe/openscad/fuselage/canonical_leg_r6_1_5in.scad, airframe/openscad/fuselage/canonical_leg_r6_3_0in.scad, airframe/openscad/fuselage/cargo/_export_cargo_port_root_chunk.scad, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/cargo/cargo_spar_drive.scad, airframe/openscad/fuselage/cargo/cargo_vera_faraday.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/landing_leg_assy.scad, airframe/openscad/fuselage/middle_canonical_shell24.scad, airframe/openscad/fuselage/rcrs49_wire_post.scad, airframe/openscad/fuselage/rear_shell24.scad, airframe/openscad/fuselage/wire_brace_leg.scad, airframe/openscad/fuselage/wire_loop_fuse.scad, airframe/openscad/nacelles/_export_inboard_cut.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/edf_aft_spider_sleeve.scad, airframe/openscad/nacelles/edf_stator_sleeve.scad, airframe/openscad/nacelles/gear_option_compare.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/nacelles/nacelle_nozzle_pushrod.scad, airframe/openscad/nacelles/nacelle_pod_50mm_tandem.scad, airframe/openscad/nacelles/nacelle_servo_bracket.scad, airframe/openscad/port_tilt_spar_assembly.scad, airframe/openscad/wings/wings_s1223_revo.scad, airframe/openscad/wings/wings_s1223_revo.scad.backup-2026-07-18, airframe/placeholders/avionics/Cape_A2_PCB_55x35mm.stl, airframe/placeholders/avionics/Cape_B2_PCB_55x35mm.stl, airframe/placeholders/avionics/FlightEngineer_PDB_90x65mm.stl, airframe/placeholders/avionics/PocketBeagle2_Industrial_56x35mm.stl, airframe/placeholders/avionics/XCVR_49MHZ2_PCB_55x35mm.stl, airframe/placeholders/avionics/microSD_64GB.stl, airframe/placeholders/bearings/B6804_20x32x7mm_GCS.stl, airframe/placeholders/bearings/MF104ZZ_4x10x4mm.stl, airframe/placeholders/bearings/MR63ZZ_3x6x2p5mm.stl, airframe/placeholders/cargo/DRV8833_Hbridge_breakout.stl, airframe/placeholders/cargo/Dyneema_SK75_0p5mm_coil.stl, airframe/placeholders/cargo/HX711_loadcell_ADC_breakout.stl, airframe/placeholders/cargo/N20_motor_300RPM_6V.stl, airframe/placeholders/faraday/Far_EMI_vent_40x40x6mm.stl, airframe/placeholders/faraday/Far_FT_panel_55x35mm.stl, airframe/placeholders/faraday/Far_bond_strap_100mm.stl, airframe/placeholders/faraday/Far_cage_AV_70x50x82mm.stl, airframe/placeholders/faraday/Far_fan_40mm_5V.stl, airframe/placeholders/faraday/Far_ferrite_4mm_ID.stl, airframe/placeholders/faraday/Far_gasket_AV_250x6x1mm.stl, airframe/placeholders/faraday/Mal_far_fan_40mm_5V.stl, airframe/placeholders/faraday/Mal_far_gasket_470x8x1p5mm.stl, airframe/placeholders/foam/Foam_fill_cargo_190x200x159mm.stl, airframe/placeholders/foam/Foam_fill_head_125x231x136mm.stl, airframe/placeholders/foam/Foam_fill_middle_horseshoe_173x69x161mm.stl, airframe/placeholders/foam/Foam_fill_rear_136x177x154mm.stl, airframe/placeholders/foam/Void_avionics_bay_62x42x75mm.stl, airframe/placeholders/foam/Void_cargo_bay_120x150x80mm.stl, airframe/placeholders/foam/Void_far_cage_76x56x88mm.stl, airframe/placeholders/foam/Void_far_fan_spur_44x44x50mm.stl, airframe/placeholders/foam/Void_nacelle_pylon_20x80x20mm.stl, airframe/placeholders/foam/Void_power_bus_25x500x25mm.stl, airframe/placeholders/foam/Void_vent_exhaust_20x300x20mm.stl, airframe/placeholders/foam/Void_vent_intake_20x250x20mm.stl, airframe/placeholders/foam/Void_wiring_trunk_30x700x20mm.stl, airframe/placeholders/gcs/AS5600_encoder_breakout_15x15mm.stl, airframe/placeholders/gcs/Antenna_2p4GHz_zigbee_rubber_duck.stl, airframe/placeholders/gcs/Antenna_49MHz_whip_940mm.stl, airframe/placeholders/gcs/Antenna_5GHz_panel_14dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_Yagi_9dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_omni_5dBi.stl, airframe/placeholders/gcs/Antenna_GNSS_patch_ANN_MB00.stl, airframe/placeholders/gcs/B6804_20x32x7mm_gimbal_pan.stl, airframe/placeholders/gcs/GPS_module_M10Q_SparkFun.stl, airframe/placeholders/gcs/N42_disc_magnet_6x2mm.stl, airframe/placeholders/gcs/Pololu_D24V22F6_6V2A_BEC.stl, airframe/placeholders/gcs/Pololu_D24V50F5_5V5A_BEC.stl, airframe/placeholders/gcs/RF_splitter_915MHz_2way_ZFSC.stl, airframe/placeholders/gcs/Skipper_enclosure_IP65_145x90x65mm.stl, airframe/placeholders/gcs/Skipper_tripod_antenna_mast.stl, airframe/placeholders/gcs/TCA9548A_I2C_mux_breakout.stl, airframe/placeholders/gears/Bevel_M1_14T_pair.stl, airframe/placeholders/gears/Bevel_housing_CF_PETG.stl, airframe/placeholders/gears/Pinion_M1_12T_R6mm.stl, airframe/placeholders/gears/Sector_gear_M1_R22mm.stl, airframe/placeholders/hardware/Batt_strap_silicone_16mm_CAM.stl, airframe/placeholders/hardware/Insert_M25_brass_L5.stl, airframe/placeholders/hardware/Insert_M3_brass_L5.stl, airframe/placeholders/hardware/Pin_SS_3x5mm_hinge.stl, airframe/placeholders/hardware/Screw_M3x8mm_button_ISO7380.stl, airframe/placeholders/lighting/WS2812C_2020_SMD.stl, airframe/placeholders/power/Fuse_MAXI_150A.stl, airframe/placeholders/power/Fuse_mini_40A.stl, airframe/placeholders/power/LiPo_4S_10000mAh_175x64x38mm_GCS.stl, airframe/placeholders/power/LiPo_6S_2800mAh_115x35x35mm.stl, airframe/placeholders/power/LiPo_6S_4000mAh_138x44x36mm.stl, airframe/placeholders/power/Shunt_CSS2H_2512K_1mohm.stl, airframe/placeholders/propulsion/EDF_120mm_6S_deferred.stl, airframe/placeholders/propulsion/EDF_50mm_6S.stl, airframe/placeholders/propulsion/ESC_40A_6S_BLHeli32.stl, airframe/placeholders/propulsion/ESC_80A_6S_BLHeli32_deferred.stl, airframe/placeholders/servos/DS3218MG_25kgcm.stl, airframe/placeholders/servos/SG90_micro.stl, airframe/placeholders/structural/CF_bar_6x3mm_620mm_keel.stl, airframe/placeholders/structural/CF_plate_2mm_200x300mm.stl, airframe/placeholders/structural/CF_rod_3mm_300mm_stock.stl, airframe/placeholders/structural/CF_rod_4mm_300mm_stock.stl, airframe/placeholders/structural/CF_tube_12mm_OD_1p5w_350mm_spar.stl, airframe/placeholders/structural/PTFE_sleeve_4mm_OD_3mm_ID_52mm.stl, airframe/placeholders/wiring/PTFE_conduit_4mm_OD_3mm_ID_700mm.stl, airframe/placeholders/wiring/Post_49MHz_base_load_coil.stl, airframe/placeholders/wiring/Wire_10AWG_silicone_400mm.stl, airframe/placeholders/wiring/Wire_28AWG_STP_bundle_500mm.stl, airframe/placeholders/wiring/Wire_49MHz_antenna_0p3mm_470mm.stl, airframe/placeholders/wiring/Wire_4AWG_silicone_200mm_pair.stl, airframe/stls/fuselage/battery_tray.stl, airframe/stls/fuselage/belly_panel.stl, airframe/stls/fuselage/bow_sensor_faceplate.stl, airframe/stls/fuselage/cargo/cargo_cradle_autolatch.stl, airframe/stls/fuselage/cargo/cargo_door_port.stl, airframe/stls/fuselage/cargo/cargo_door_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_door_stbd.stl, airframe/stls/fuselage/cargo/cargo_drv8833_tray.stl, airframe/stls/fuselage/cargo/cargo_fpv_bezel.stl, airframe/stls/fuselage/cargo/cargo_gps_retention_ring.stl, airframe/stls/fuselage/cargo/cargo_hinge_retention.stl, airframe/stls/fuselage/cargo/cargo_release_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_bossed.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_repaired.stl, airframe/stls/fuselage/cargo/cargo_vera_faraday.stl, airframe/stls/fuselage/cargo/cargo_winch_motor_mount.stl, airframe/stls/fuselage/cargo/cargo_winch_spool.stl, airframe/stls/fuselage/cargo_middle_splice_collar.stl, airframe/stls/fuselage/dorsal_antenna_fin.stl, airframe/stls/fuselage/head_cargo_splice_collar.stl, airframe/stls/fuselage/head_shell24.stl, airframe/stls/fuselage/head_shell24_2mm_repaired.stl, airframe/stls/fuselage/inara_access_cover.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_bay.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_nominal.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_foot.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_nominal.stl, airframe/stls/fuselage/middle_canonical_edf_intake.stl, airframe/stls/fuselage/middle_canonical_shell24.stl, airframe/stls/fuselage/middle_rear_splice_collar.stl, airframe/stls/fuselage/middle_shell24_2mm_repaired.stl, airframe/stls/fuselage/rear_shell24_2mm_repaired.stl, airframe/stls/fuselage/river_access_cover.stl, airframe/stls/fuselage/wing_root_flange_port.stl, airframe/stls/fuselage/wing_root_flange_stbd.stl, airframe/stls/nacelles/edf_aft_spider_sleeve.stl, airframe/stls/nacelles/edf_stator_sleeve.stl, airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl, airframe/stls/nacelles/eng_right_shell24_50mm_repaired.stl, airframe/stls/nacelles/nacelle_port_revs.stl, airframe/stls/nacelles/nacelle_servo_bracket.stl, airframe/stls/nacelles/nacelle_stbd_revs.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap_seal.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-closed.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-open.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_ring.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_throat.stl, airframe/stls/wings/wing_port_s1223_revo.stl, airframe/stls/wings/wing_stbd_s1223_revo.stl, deferred/aft-edf/openscad/aft_edf_plenum.scad, deferred/aft-edf/openscad/edf_120_motor_mount.scad, deferred/aft-edf/openscad/edf_120_thrust_tube.scad, deferred/aft-edf/openscad/neck_intake_frame.scad, deferred/aft-edf/openscad/rear_neck_intake_shell24.scad, deferred/aft-edf/stls/aft_edf_plenum.stl, deferred/aft-edf/stls/edf_120_motor_mount.stl, deferred/aft-edf/stls/edf_120_thrust_tube.stl, deferred/aft-edf/stls/neck_intake_frame.stl, deferred/aft-edf/stls/rear_nozzle_closed_asm.stl, deferred/aft-edf/stls/rear_nozzle_frame.stl, deferred/aft-edf/stls/rear_nozzle_petal.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_0.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_1.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_2.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_3.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_4.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_5.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_6.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_7.stl, deferred/aft-edf/stls/rear_shell24_2mm_edf_bored.stl, docs/references/thingverse-serenity/files/s_cargo_door.stl, docs/references/thingverse-serenity/files/s_cargo_door_strutts.stl, docs/references/thingverse-serenity/files/s_cargo_sect.stl, docs/references/thingverse-serenity/files/s_eng_left.stl, docs/references/thingverse-serenity/files/s_eng_pistons.stl, docs/references/thingverse-serenity/files/s_eng_piv_outer.stl, docs/references/thingverse-serenity/files/s_eng_piv_pins.stl, docs/references/thingverse-serenity/files/s_eng_right.stl, docs/references/thingverse-serenity/files/s_feet_x_4.stl, docs/references/thingverse-serenity/files/s_head.stl, docs/references/thingverse-serenity/files/s_legs.stl, docs/references/thingverse-serenity/files/s_middle.stl, docs/references/thingverse-serenity/files/s_pivot_arm_a.stl, docs/references/thingverse-serenity/files/s_rear.stl, docs/references/thingverse-serenity/files/s_wings_both.stl, docs/references/vimdrones_can_periph_pico_v1.0.stl, docs/references/vimdrones_esc_s50_v1.0.stl, gcs/skipper/hardware/enclosure/openscad/skipper_field_enclosure.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_mount.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_pan.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_tilt.scad, graphical-build-guide/shellview/port_wall.stl, tools/purge_stale_fcstd_objects.py - `comms-protocol` (25): airframe/placeholders/avionics/XCVR_49MHZ2_PCB_55x35mm.stl, airframe/placeholders/gcs/Antenna_2p4GHz_zigbee_rubber_duck.stl, airframe/placeholders/gcs/Antenna_49MHz_whip_940mm.stl, airframe/placeholders/wiring/Post_49MHz_base_load_coil.stl, airframe/placeholders/wiring/Wire_49MHz_antenna_0p3mm_470mm.stl, avionics/firmware/cn/src/xcvr_kiss.c, avionics/firmware/cn/src/xcvr_kiss.h, avionics/firmware/common/include/ax25_types.h, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Mask.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Paste.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-B_Silkscreen.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-Edge_Cuts.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Mask.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Paste.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-F_Silkscreen.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-In1_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-In2_Cu.gbr, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2-job.gbrjob, avionics/kicad/Commo/gerbers/XCVR-49MHZ-2.drl, avionics/kicad/complete_xcvr_49mhz2.py, avionics/kicad/fix_xcvr_labels.py, gcs/skipper/software/config/mavlink_router.conf, gcs/skipper/software/install/install_mavlink_router.sh - `deferred-future` (28): deferred/AGENTS.md, deferred/DEFERRED_ITEM_TEMPLATE.md, deferred/LICENSE, deferred/README.md, deferred/TODO.md, deferred/WBS.md, deferred/aft-edf/README.md, deferred/aft-edf/openscad/aft_edf_plenum.scad, deferred/aft-edf/openscad/edf_120_motor_mount.scad, deferred/aft-edf/openscad/edf_120_thrust_tube.scad, deferred/aft-edf/openscad/neck_intake_frame.scad, deferred/aft-edf/openscad/rear_neck_intake_shell24.scad, deferred/aft-edf/stls/aft_edf_plenum.stl, deferred/aft-edf/stls/edf_120_motor_mount.stl, deferred/aft-edf/stls/edf_120_thrust_tube.stl, deferred/aft-edf/stls/neck_intake_frame.stl, deferred/aft-edf/stls/rear_nozzle_closed_asm.stl, deferred/aft-edf/stls/rear_nozzle_frame.stl, deferred/aft-edf/stls/rear_nozzle_petal.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_0.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_1.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_2.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_3.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_4.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_5.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_6.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_7.stl, deferred/aft-edf/stls/rear_shell24_2mm_edf_bored.stl -- `documentation` (268): AGENTS.md, CLAUDE-MEMORY.md, CLAUDE.md, README.md, REFERENCES.md, REPO_ENFORCEMENT.md, SECURITY.md, TODO.md, WBS.md, airframe/AGENTS.md, airframe/HULL_FRAME_REFERENCE.md, airframe/README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/TODO.md, airframe/VERIFY_PLACEMENT_CHECKLIST.md, airframe/WBS.md, airframe/fuselage-covers/TODO.md, airframe/fuselage-covers/WBS.md, airframe/fuselage-joints/TODO.md, airframe/fuselage-joints/WBS.md, airframe/fuselage-mid/TODO.md, airframe/fuselage-mid/WBS.md, airframe/landing-gear/HANDOFF.md, airframe/landing-gear/TODO.md, airframe/landing-gear/WBS.md, airframe/wings-nacelles/TODO.md, airframe/wings-nacelles/WBS.md, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/emi-hardening/TODO.md, avionics/emi-hardening/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/README.md, avionics/firmware/TODO.md, avionics/firmware/WBS.md, avionics/firmware/dts/README.md, avionics/kicad/CAN-PERIPH-GW-1/CAN-PERIPH-GW-1.md, avionics/kicad/Commo/Commo.md, avionics/kicad/ENC-NACELLE-1.md, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/GENERATOR_DRIFT_ANALYSIS.md, avionics/kicad/Observer/OBSERVER_SOM_NETMAP.md, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/PCBNEW_SWIG_BUG.md, avionics/kicad/Observer/VERA_NOSE_TRAPEZOID.md, avionics/kicad/Pilot/PILOT_FOOTPRINT_VERIFICATION.md, avionics/kicad/Pilot/Pilot.md, avionics/kicad/README.md, avionics/kicad/TODO-1.2b-CHECKLIST.md, avionics/kicad/TODO-1.2b-KICAD-READY.md, avionics/kicad/TODO-1.2b-STATUS-REPORT.md, avionics/kicad/XO/XO.md, avionics/observer/TODO.md, avionics/observer/WBS.md, avionics/rev-s1/TODO.md, avionics/rev-s1/WBS.md, current-specification/AGENTS.md, current-specification/LICENSE_AND_ATTRIBUTION.md, current-specification/README.md, current-specification/SPEC_TEMPLATE.md, current-specification/TODO.md, deferred/AGENTS.md, deferred/DEFERRED_ITEM_TEMPLATE.md, deferred/README.md, deferred/TODO.md, deferred/WBS.md, deferred/aft-edf/README.md, docs/AGENTS.md, docs/AVIONICS_PB2_REDESIGN.md, docs/BATTERY_MOUNT.md, docs/CARGO_WINCH_SPECIFICATION.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/DOC_TEMPLATES.md, docs/DOC_VERIFICATION_0.6.2.md, docs/ETHERNET_PHY_TRADE.md, docs/FIRST_FLIGHT_READINESS.md, docs/LANDING_GEAR_ANALYSIS.md, docs/LICENSE, docs/MANIFEST.json, docs/NOZZLE_DRIVE_TRADE.md, docs/OBSERVER_LASER_ANALYSIS.md, docs/OBSERVER_MANUFACTURING_READINESS.md, docs/OSHW_CERTIFICATION.md, docs/PHASED_BUILD_GUIDE.md, docs/POWER_DISTRIBUTION.md, docs/PROTO_PRINT_DAVINCI_JR.md, docs/PYLON_INTEGRATION_2026-07-18.md, docs/README.md, docs/REVN_BUILD_GUIDE_24IN.md, docs/TILT_ENCODER_WIRING_EMI_SPEC.md, docs/TILT_SPAR_ANALYSIS.md, docs/TODO.md, docs/TODO_1_1_0_COMPLETION_SUMMARY.md, docs/Tilt-Rotor 10AWG Wire Clearance Analysis.md, docs/VERIFY_PLACEMENT_WORKFLOW.md, docs/WBS.md, docs/WBS_FEDERATION.md, docs/attribution_and_licensing.md, docs/bom_revR.json, docs/electrical_fault_margins.md, docs/failsafe_thresholds.md, docs/flight_envelope.md, docs/img/assembled-iso.png, docs/img/leg.png, docs/img/nozzle_drive_trade.png, docs/img/port-nacelle-horizontal.png, docs/img/splice-collar.png, docs/img/starboard-nacelle-vert.png, docs/img/transparent-nacelle.png, docs/img/wing_rev_r1a_sections.png, docs/img/wing_rev_s1c_sections.png, docs/plans/2026-08-24-001-fix-wing-repair-root-joint-plan.md, docs/plans/2026-08-25-001-finish-avionics-plan.md, docs/plans/2026-08-25-002-finish-airframe-components-plan.md, docs/plans/2026-08-25-003-feat-emi-rf-design-test-plan-plan.md, docs/plans/2026-08-26-001-nacelle-esc-intake-integration-plan.md, docs/plans/2026-08-27-nacelle-wiring-plan.md, docs/plans/2026-08-29-001-nacelle-nav-wire-hall-reconciliation-plan.md, docs/plans/2026-08-29-002-nacelle-esc-power-routing-plan.md, docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md, docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md, docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md, docs/references/108090023_STS3215-C001_Datasheet.pdf, docs/references/Bus_servo_control_circuit.pdf, docs/references/CERN_OHL_rationale.pdf, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model.html, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/1.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/2.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37110-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37111-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37112-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-large_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37114-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37115-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/76a8e5b0fe9140cf057b953efbc49a1f.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/EDF50mm-02.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/analytics.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css_002.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades-3s4.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_003.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_004.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_005.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_006.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_007.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/ga.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/mollie.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_114_a4444adaf1056832dd63b28b928b74e6.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_03a718a060cf48c0e5393bbea5cb1aa7_all.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_c1bb31e2f03fadfdc659940cc14955dc_print.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-64mm-edf-glider-nacelle-for-swift-2100.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-edf-glider-nacelle-for-swift-2100-827-50mm.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-model-logo-16966716258.jpg, docs/references/POCKETBEAGLE2 V10_241220.DSN, docs/references/ST3215 memory register map-EN.xls, docs/references/ST3215-2D.zip, docs/references/ST3215-3D.zip, docs/references/Screenshot 2026-08-18 at 09-50-31 EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades 6S Motor 3200KV - Xfly-Model.png, docs/references/Servo_Driver_with_ESP32_3D.zip, docs/references/The_Official_Serenity_Blueprints_Reference_Pack.pdf, docs/references/nick-henning/Re: Contact got a new submission - Nick Henning - 2026-07-06 1421.txt, docs/references/nick-henning/nick-henning-close-back-combine.jpg, docs/references/nick-henning/nick-henning-close-bridge-combine.jpg, docs/references/nick-henning/nick-henning-close-gear-combine.jpg, docs/references/nick-henning/nick-henning-final-backside-combine.jpg, docs/references/nick-henning/nick-henning-final-front-combine.jpg, docs/references/nick-henning/nick-henning-final-top-combine.jpg, docs/references/nick-henning/nick-henning-uvdisplay-engine.jpg, docs/references/nick-henning/nick-henning-uvdisplay-gear.jpg, docs/references/nick-henning/nick-henning-uvdisplay-wing.jpg, docs/references/nick-henning/port-bow-full-henning.jpg, docs/references/nick-henning/port-bow-upper-bridge-with-wireframe-henning.jpg, docs/references/nick-henning/port-bow-view-with-wireframe-henning.jpg, docs/references/nick-henning/port-quarter-with-wireframe-henning.jpg, docs/references/nick-henning/top-view-with-wireframe-henning.jpg, docs/references/open-source-hardware-checklist.pdf, docs/references/pocketbeagle2 revA_241205.brd, docs/references/scservo.zip.zip, docs/references/thingverse-serenity/LICENSE.txt, docs/references/thingverse-serenity/README.txt, docs/references/thingverse-serenity/files/s_cargo_door.stl, docs/references/thingverse-serenity/files/s_cargo_door_strutts.stl, docs/references/thingverse-serenity/files/s_cargo_sect.stl, docs/references/thingverse-serenity/files/s_eng_left.stl, docs/references/thingverse-serenity/files/s_eng_pistons.stl, docs/references/thingverse-serenity/files/s_eng_piv_outer.stl, docs/references/thingverse-serenity/files/s_eng_piv_pins.stl, docs/references/thingverse-serenity/files/s_eng_right.stl, docs/references/thingverse-serenity/files/s_feet_x_4.stl, docs/references/thingverse-serenity/files/s_head.stl, docs/references/thingverse-serenity/files/s_legs.stl, docs/references/thingverse-serenity/files/s_middle.stl, docs/references/thingverse-serenity/files/s_pivot_arm_a.stl, docs/references/thingverse-serenity/files/s_rear.stl, docs/references/thingverse-serenity/files/s_wings_both.stl, docs/references/thingverse-serenity/images/front-starboard.png, docs/references/thingverse-serenity/images/s_01.jpg, docs/references/thingverse-serenity/images/s_02.jpg, docs/references/thingverse-serenity/images/s_03.jpg, docs/references/thingverse-serenity/images/s_04.jpg, docs/references/thingverse-serenity/images/s_05.jpg, docs/references/thingverse-serenity/images/s_06.jpg, docs/references/thingverse-serenity/images/s_07.jpg, docs/references/thingverse-serenity/images/s_08.jpg, docs/references/thingverse-serenity/images/s_09.jpg, docs/references/thingverse-serenity/images/s_10.jpg, docs/references/thingverse-serenity/images/s_11.jpg, docs/references/thingverse-serenity/images/s_cargo_door.png, docs/references/thingverse-serenity/images/s_cargo_door_strutts.png, docs/references/thingverse-serenity/images/s_cargo_sect.png, docs/references/thingverse-serenity/images/s_eng_left.png, docs/references/thingverse-serenity/images/s_eng_pistons.png, docs/references/thingverse-serenity/images/s_eng_piv_outer.png, docs/references/thingverse-serenity/images/s_eng_piv_pins.png, docs/references/thingverse-serenity/images/s_eng_right.png, docs/references/thingverse-serenity/images/s_feet_x_4.png, docs/references/thingverse-serenity/images/s_head.png, docs/references/thingverse-serenity/images/s_legs.png, docs/references/thingverse-serenity/images/s_middle.png, docs/references/thingverse-serenity/images/s_pivot_arm_a.png, docs/references/thingverse-serenity/images/s_rear.png, docs/references/thingverse-serenity/images/s_wings_both.png, docs/references/vimdrones_can_periph_pico_v1.0.stl, docs/references/vimdrones_esc_s50_v1.0.step, docs/references/vimdrones_esc_s50_v1.0.stl, docs/references/vimdrones_esc_s50_wiring.png, docs/solutions/design-patterns/reducing-token-cost-of-federated-agents-md.md, docs/structural_analysis.md, gcs/AGENTS.md, gcs/README.md, gcs/SKIPPER_SPEC.md, gcs/TODO.md, gcs/WBS.md, gcs/skipper/README.md, gcs/skipper/hardware/docs/skipper_antenna_spec.md, gcs/skipper/hardware/docs/skipper_power_budget.md, gcs/skipper/hardware/docs/skipper_wiring.md, graphical-build-guide/AGENTS.md, graphical-build-guide/BUILD_GUIDE_TEMPLATE.md, graphical-build-guide/README.md, graphical-build-guide/REVN_BUILD_GUIDE_24IN.md, graphical-build-guide/TODO-old.md, graphical-build-guide/TODO.md, graphical-build-guide/WBS.md, graphical-build-guide/flight-phases/TODO.md, graphical-build-guide/flight-phases/WBS.md, tools/AGENTS.md, tools/README.md, tools/TODO.md, tools/TOOL_REFERENCE.md -- `emi-hardening` (65): AGENTS.md, README.md, WBS.md, airframe/FreeCAD-scripts/serenity_placeholders_assembly.py, airframe/README.md, airframe/fuselage-joints/WBS.md, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/cargo/cargo_vera_faraday.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/rcrs49_wire_post.scad, airframe/placeholders/faraday/Far_EMI_vent_40x40x6mm.stl, airframe/placeholders/generate_placeholders.py, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/emi-hardening/TODO.md, avionics/emi-hardening/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/README.md, avionics/firmware/dts/cape-a/k3-am6254-pocketbeagle2-serenity-cape-a2.dts, avionics/firmware/dts/cape-b/k3-am6254-pocketbeagle2-serenity-cape-b2.dts, avionics/firmware/fc/src/mag_mmc5983ma.h, avionics/kicad/Commo/Commo.md, avionics/kicad/Commo/scripts/gen_commo_sch.py, avionics/kicad/ENC-NACELLE-1.md, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/scripts/gen_flight_engineer_pcb.py, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/VERA_NOSE_TRAPEZOID.md, avionics/kicad/Observer/scripts/gen_observer.py, avionics/kicad/Observer/scripts/gen_observer_carrier_sch.py, avionics/kicad/Observer/scripts/gen_observer_pcb.py, avionics/kicad/Pilot/Pilot.md, avionics/kicad/XO/XO.md, avionics/kicad/complete_xcvr_49mhz2.py, avionics/kicad/fix_xcvr_labels.py, avionics/observer/WBS.md, avionics/rev-s1/WBS.md, current-specification/serenity-rev-s.jsx, docs/AVIONICS_PB2_REDESIGN.md, docs/BATTERY_MOUNT.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/ETHERNET_PHY_TRADE.md, docs/FIRST_FLIGHT_READINESS.md, docs/OBSERVER_MANUFACTURING_READINESS.md, docs/PHASED_BUILD_GUIDE.md, docs/README.md, docs/REVN_BUILD_GUIDE_24IN.md, docs/TILT_ENCODER_WIRING_EMI_SPEC.md, docs/TILT_SPAR_ANALYSIS.md, docs/Tilt-Rotor 10AWG Wire Clearance Analysis.md, docs/WBS_FEDERATION.md, docs/electrical_fault_margins.md, docs/plans/2026-08-25-001-finish-avionics-plan.md, docs/plans/2026-08-25-003-feat-emi-rf-design-test-plan-plan.md, docs/plans/2026-08-27-nacelle-wiring-plan.md, docs/plans/2026-08-29-001-nacelle-nav-wire-hall-reconciliation-plan.md, docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md, gcs/skipper/firmware/pb2i/dts/k3-am6254-pocketbeagle2-skipper-cape-b2.dts, graphical-build-guide/flight-phases/WBS.md, tools/TOOL_REFERENCE.md, tools/precommit_index.py, tools/validate_kicad.py +- `documentation` (272): AGENTS.md, CLAUDE-MEMORY.md, CLAUDE.md, README.md, REFERENCES.md, REPO_ENFORCEMENT.md, SECURITY.md, TODO.md, WBS.md, airframe/AGENTS.md, airframe/HULL_FRAME_REFERENCE.md, airframe/README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/TODO.md, airframe/VERIFY_PLACEMENT_CHECKLIST.md, airframe/WBS.md, airframe/fuselage-covers/TODO.md, airframe/fuselage-covers/WBS.md, airframe/fuselage-joints/TODO.md, airframe/fuselage-joints/WBS.md, airframe/fuselage-mid/TODO.md, airframe/fuselage-mid/WBS.md, airframe/landing-gear/HANDOFF.md, airframe/landing-gear/TODO.md, airframe/landing-gear/WBS.md, airframe/wings-nacelles/TODO.md, airframe/wings-nacelles/WBS.md, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/emi-hardening/TODO.md, avionics/emi-hardening/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/README.md, avionics/firmware/TODO.md, avionics/firmware/WBS.md, avionics/firmware/dts/README.md, avionics/kicad/CAN-PERIPH-GW-1/CAN-PERIPH-GW-1.md, avionics/kicad/Commo/Commo.md, avionics/kicad/ENC-NACELLE-1.md, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/GENERATOR_DRIFT_ANALYSIS.md, avionics/kicad/Observer/OBSERVER_SOM_NETMAP.md, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/PCBNEW_SWIG_BUG.md, avionics/kicad/Observer/VERA_NOSE_TRAPEZOID.md, avionics/kicad/Pilot/PILOT_FOOTPRINT_VERIFICATION.md, avionics/kicad/Pilot/Pilot.md, avionics/kicad/README.md, avionics/kicad/TODO-1.2b-CHECKLIST.md, avionics/kicad/TODO-1.2b-KICAD-READY.md, avionics/kicad/TODO-1.2b-STATUS-REPORT.md, avionics/kicad/XO/XO.md, avionics/observer/TODO.md, avionics/observer/WBS.md, avionics/rev-s1/TODO.md, avionics/rev-s1/WBS.md, current-specification/AGENTS.md, current-specification/LICENSE_AND_ATTRIBUTION.md, current-specification/README.md, current-specification/SPEC_TEMPLATE.md, current-specification/TODO.md, deferred/AGENTS.md, deferred/DEFERRED_ITEM_TEMPLATE.md, deferred/README.md, deferred/TODO.md, deferred/WBS.md, deferred/aft-edf/README.md, docs/AGENTS.md, docs/AVIONICS_PB2_REDESIGN.md, docs/BATTERY_MOUNT.md, docs/CARGO_WINCH_SPECIFICATION.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/DOC_TEMPLATES.md, docs/DOC_VERIFICATION_0.6.2.md, docs/ETHERNET_PHY_TRADE.md, docs/FIRST_FLIGHT_READINESS.md, docs/LANDING_GEAR_ANALYSIS.md, docs/LICENSE, docs/MANIFEST.json, docs/MASS_AUDIT_CARGO_WING_ROOT.md, docs/NOZZLE_DRIVE_TRADE.md, docs/OBSERVER_LASER_ANALYSIS.md, docs/OBSERVER_MANUFACTURING_READINESS.md, docs/OSHW_CERTIFICATION.md, docs/PHASED_BUILD_GUIDE.md, docs/POWER_DISTRIBUTION.md, docs/PROTO_PRINT_DAVINCI_JR.md, docs/PYLON_INTEGRATION_2026-07-18.md, docs/README.md, docs/REVN_BUILD_GUIDE_24IN.md, docs/TILT_DRIVE_CONTROL_SPEC.md, docs/TILT_ENCODER_WIRING_EMI_SPEC.md, docs/TILT_SPAR_ANALYSIS.md, docs/TODO.md, docs/TODO_1_1_0_COMPLETION_SUMMARY.md, docs/Tilt-Rotor 10AWG Wire Clearance Analysis.md, docs/VERIFY_PLACEMENT_WORKFLOW.md, docs/WBS.md, docs/WBS_FEDERATION.md, docs/WING_ATTACH_INTERFACE.md, docs/attribution_and_licensing.md, docs/bom_revR.json, docs/electrical_fault_margins.md, docs/failsafe_thresholds.md, docs/flight_envelope.md, docs/img/assembled-iso.png, docs/img/leg.png, docs/img/nozzle_drive_trade.png, docs/img/port-nacelle-horizontal.png, docs/img/splice-collar.png, docs/img/starboard-nacelle-vert.png, docs/img/transparent-nacelle.png, docs/img/wing_rev_r1a_sections.png, docs/img/wing_rev_s1c_sections.png, docs/plans/2026-08-24-001-fix-wing-repair-root-joint-plan.md, docs/plans/2026-08-25-001-finish-avionics-plan.md, docs/plans/2026-08-25-002-finish-airframe-components-plan.md, docs/plans/2026-08-25-003-feat-emi-rf-design-test-plan-plan.md, docs/plans/2026-08-26-001-nacelle-esc-intake-integration-plan.md, docs/plans/2026-08-27-nacelle-wiring-plan.md, docs/plans/2026-08-29-001-nacelle-nav-wire-hall-reconciliation-plan.md, docs/plans/2026-08-29-002-nacelle-esc-power-routing-plan.md, docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md, docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md, docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md, docs/plans/2026-08-30-001-weight-reduction-targets-plan.md, docs/references/108090023_STS3215-C001_Datasheet.pdf, docs/references/Bus_servo_control_circuit.pdf, docs/references/CERN_OHL_rationale.pdf, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model.html, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/1.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/2.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37110-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37111-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37112-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-large_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37114-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37115-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/76a8e5b0fe9140cf057b953efbc49a1f.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/EDF50mm-02.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/analytics.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css_002.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades-3s4.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_003.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_004.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_005.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_006.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_007.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/ga.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/mollie.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_114_a4444adaf1056832dd63b28b928b74e6.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_03a718a060cf48c0e5393bbea5cb1aa7_all.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_c1bb31e2f03fadfdc659940cc14955dc_print.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-64mm-edf-glider-nacelle-for-swift-2100.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-edf-glider-nacelle-for-swift-2100-827-50mm.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-model-logo-16966716258.jpg, docs/references/POCKETBEAGLE2 V10_241220.DSN, docs/references/ST3215 memory register map-EN.xls, docs/references/ST3215-2D.zip, docs/references/ST3215-3D.zip, docs/references/Screenshot 2026-08-18 at 09-50-31 EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades 6S Motor 3200KV - Xfly-Model.png, docs/references/Servo_Driver_with_ESP32_3D.zip, docs/references/The_Official_Serenity_Blueprints_Reference_Pack.pdf, docs/references/nick-henning/Re: Contact got a new submission - Nick Henning - 2026-07-06 1421.txt, docs/references/nick-henning/nick-henning-close-back-combine.jpg, docs/references/nick-henning/nick-henning-close-bridge-combine.jpg, docs/references/nick-henning/nick-henning-close-gear-combine.jpg, docs/references/nick-henning/nick-henning-final-backside-combine.jpg, docs/references/nick-henning/nick-henning-final-front-combine.jpg, docs/references/nick-henning/nick-henning-final-top-combine.jpg, docs/references/nick-henning/nick-henning-uvdisplay-engine.jpg, docs/references/nick-henning/nick-henning-uvdisplay-gear.jpg, docs/references/nick-henning/nick-henning-uvdisplay-wing.jpg, docs/references/nick-henning/port-bow-full-henning.jpg, docs/references/nick-henning/port-bow-upper-bridge-with-wireframe-henning.jpg, docs/references/nick-henning/port-bow-view-with-wireframe-henning.jpg, docs/references/nick-henning/port-quarter-with-wireframe-henning.jpg, docs/references/nick-henning/top-view-with-wireframe-henning.jpg, docs/references/open-source-hardware-checklist.pdf, docs/references/pocketbeagle2 revA_241205.brd, docs/references/scservo.zip.zip, docs/references/thingverse-serenity/LICENSE.txt, docs/references/thingverse-serenity/README.txt, docs/references/thingverse-serenity/files/s_cargo_door.stl, docs/references/thingverse-serenity/files/s_cargo_door_strutts.stl, docs/references/thingverse-serenity/files/s_cargo_sect.stl, docs/references/thingverse-serenity/files/s_eng_left.stl, docs/references/thingverse-serenity/files/s_eng_pistons.stl, docs/references/thingverse-serenity/files/s_eng_piv_outer.stl, docs/references/thingverse-serenity/files/s_eng_piv_pins.stl, docs/references/thingverse-serenity/files/s_eng_right.stl, docs/references/thingverse-serenity/files/s_feet_x_4.stl, docs/references/thingverse-serenity/files/s_head.stl, docs/references/thingverse-serenity/files/s_legs.stl, docs/references/thingverse-serenity/files/s_middle.stl, docs/references/thingverse-serenity/files/s_pivot_arm_a.stl, docs/references/thingverse-serenity/files/s_rear.stl, docs/references/thingverse-serenity/files/s_wings_both.stl, docs/references/thingverse-serenity/images/front-starboard.png, docs/references/thingverse-serenity/images/s_01.jpg, docs/references/thingverse-serenity/images/s_02.jpg, docs/references/thingverse-serenity/images/s_03.jpg, docs/references/thingverse-serenity/images/s_04.jpg, docs/references/thingverse-serenity/images/s_05.jpg, docs/references/thingverse-serenity/images/s_06.jpg, docs/references/thingverse-serenity/images/s_07.jpg, docs/references/thingverse-serenity/images/s_08.jpg, docs/references/thingverse-serenity/images/s_09.jpg, docs/references/thingverse-serenity/images/s_10.jpg, docs/references/thingverse-serenity/images/s_11.jpg, docs/references/thingverse-serenity/images/s_cargo_door.png, docs/references/thingverse-serenity/images/s_cargo_door_strutts.png, docs/references/thingverse-serenity/images/s_cargo_sect.png, docs/references/thingverse-serenity/images/s_eng_left.png, docs/references/thingverse-serenity/images/s_eng_pistons.png, docs/references/thingverse-serenity/images/s_eng_piv_outer.png, docs/references/thingverse-serenity/images/s_eng_piv_pins.png, docs/references/thingverse-serenity/images/s_eng_right.png, docs/references/thingverse-serenity/images/s_feet_x_4.png, docs/references/thingverse-serenity/images/s_head.png, docs/references/thingverse-serenity/images/s_legs.png, docs/references/thingverse-serenity/images/s_middle.png, docs/references/thingverse-serenity/images/s_pivot_arm_a.png, docs/references/thingverse-serenity/images/s_rear.png, docs/references/thingverse-serenity/images/s_wings_both.png, docs/references/vimdrones_can_periph_pico_v1.0.stl, docs/references/vimdrones_esc_s50_v1.0.step, docs/references/vimdrones_esc_s50_v1.0.stl, docs/references/vimdrones_esc_s50_wiring.png, docs/solutions/design-patterns/reducing-token-cost-of-federated-agents-md.md, docs/structural_analysis.md, gcs/AGENTS.md, gcs/README.md, gcs/SKIPPER_SPEC.md, gcs/TODO.md, gcs/WBS.md, gcs/skipper/README.md, gcs/skipper/hardware/docs/skipper_antenna_spec.md, gcs/skipper/hardware/docs/skipper_power_budget.md, gcs/skipper/hardware/docs/skipper_wiring.md, graphical-build-guide/AGENTS.md, graphical-build-guide/BUILD_GUIDE_TEMPLATE.md, graphical-build-guide/README.md, graphical-build-guide/REVN_BUILD_GUIDE_24IN.md, graphical-build-guide/TODO-old.md, graphical-build-guide/TODO.md, graphical-build-guide/WBS.md, graphical-build-guide/flight-phases/TODO.md, graphical-build-guide/flight-phases/WBS.md, tools/AGENTS.md, tools/README.md, tools/TODO.md, tools/TOOL_REFERENCE.md +- `emi-hardening` (67): AGENTS.md, README.md, TODO.md, WBS.md, airframe/FreeCAD-scripts/serenity_placeholders_assembly.py, airframe/README.md, airframe/fuselage-joints/WBS.md, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/cargo/cargo_vera_faraday.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/rcrs49_wire_post.scad, airframe/placeholders/faraday/Far_EMI_vent_40x40x6mm.stl, airframe/placeholders/generate_placeholders.py, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/emi-hardening/TODO.md, avionics/emi-hardening/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/README.md, avionics/firmware/dts/cape-a/k3-am6254-pocketbeagle2-serenity-cape-a2.dts, avionics/firmware/dts/cape-b/k3-am6254-pocketbeagle2-serenity-cape-b2.dts, avionics/firmware/fc/src/mag_mmc5983ma.h, avionics/kicad/Commo/Commo.md, avionics/kicad/Commo/scripts/gen_commo_sch.py, avionics/kicad/ENC-NACELLE-1.md, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/scripts/gen_flight_engineer_pcb.py, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/VERA_NOSE_TRAPEZOID.md, avionics/kicad/Observer/scripts/gen_observer.py, avionics/kicad/Observer/scripts/gen_observer_carrier_sch.py, avionics/kicad/Observer/scripts/gen_observer_pcb.py, avionics/kicad/Pilot/Pilot.md, avionics/kicad/XO/XO.md, avionics/kicad/complete_xcvr_49mhz2.py, avionics/kicad/fix_xcvr_labels.py, avionics/observer/WBS.md, avionics/rev-s1/WBS.md, current-specification/serenity-rev-s.jsx, docs/AVIONICS_PB2_REDESIGN.md, docs/BATTERY_MOUNT.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/ETHERNET_PHY_TRADE.md, docs/FIRST_FLIGHT_READINESS.md, docs/OBSERVER_MANUFACTURING_READINESS.md, docs/PHASED_BUILD_GUIDE.md, docs/README.md, docs/REVN_BUILD_GUIDE_24IN.md, docs/TILT_DRIVE_CONTROL_SPEC.md, docs/TILT_ENCODER_WIRING_EMI_SPEC.md, docs/TILT_SPAR_ANALYSIS.md, docs/Tilt-Rotor 10AWG Wire Clearance Analysis.md, docs/WBS_FEDERATION.md, docs/electrical_fault_margins.md, docs/plans/2026-08-25-001-finish-avionics-plan.md, docs/plans/2026-08-25-003-feat-emi-rf-design-test-plan-plan.md, docs/plans/2026-08-27-nacelle-wiring-plan.md, docs/plans/2026-08-29-001-nacelle-nav-wire-hall-reconciliation-plan.md, docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md, gcs/skipper/firmware/pb2i/dts/k3-am6254-pocketbeagle2-skipper-cape-b2.dts, graphical-build-guide/flight-phases/WBS.md, tools/TOOL_REFERENCE.md, tools/precommit_index.py, tools/validate_kicad.py - `fabrication` (1): airframe/placeholders/gears/Bevel_housing_CF_PETG.stl - `firmware` (44): avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/CMakeLists.txt, avionics/firmware/README.md, avionics/firmware/TODO.md, avionics/firmware/WBS.md, avionics/firmware/cn/CMakeLists.txt, avionics/firmware/cn/src/main.c, avionics/firmware/cn/src/si5351.c, avionics/firmware/cn/src/si5351.h, avionics/firmware/cn/src/xcvr_kiss.c, avionics/firmware/cn/src/xcvr_kiss.h, avionics/firmware/common/CMakeLists.txt, avionics/firmware/common/include/ax25_types.h, avionics/firmware/common/include/failsafe_config.h, avionics/firmware/common/include/kiss_types.h, avionics/firmware/common/include/sbus_input.h, avionics/firmware/common/src/sbus_input.c, avionics/firmware/dts/Makefile, avionics/firmware/dts/README.md, avionics/firmware/dts/cape-a/k3-am6254-pocketbeagle2-serenity-cape-a2.dts, avionics/firmware/dts/cape-b/k3-am6254-pocketbeagle2-serenity-cape-b2.dts, avionics/firmware/fc/CMakeLists.txt, avionics/firmware/fc/src/bmon_ina2xx.c, avionics/firmware/fc/src/bmon_ina2xx.h, avionics/firmware/fc/src/cell_mon_bq769x0.c, avionics/firmware/fc/src/cell_mon_bq769x0.h, avionics/firmware/fc/src/governor_config.h, avionics/firmware/fc/src/mag_mmc5983ma.c, avionics/firmware/fc/src/mag_mmc5983ma.h, avionics/firmware/fc/src/mag_qmc5883l.c, avionics/firmware/fc/src/mag_qmc5883l.h, avionics/firmware/fc/src/main.c, avionics/firmware/fc/src/pwr_fault.c, avionics/firmware/fc/src/pwr_fault.h, avionics/firmware/fc/tools/.gitignore, avionics/firmware/fc/tools/governor_cal.py, avionics/firmware/fc/tools/requirements.txt, gcs/skipper/firmware/pb2i/CMakeLists.txt, gcs/skipper/firmware/pb2i/dts/k3-am6254-pocketbeagle2-skipper-cape-b2.dts, gcs/skipper/firmware/pb2i/src/skipper_config.h, gcs/skipper/firmware/pb2i/src/skipper_gimbal.c, gcs/skipper/firmware/pb2i/src/skipper_gimbal.h, gcs/skipper/firmware/pb2i/src/skipper_telemetry.c, gcs/skipper/firmware/pb2i/src/skipper_telemetry.h - `ground-control` (49): airframe/placeholders/bearings/B6804_20x32x7mm_GCS.stl, airframe/placeholders/gcs/AS5600_encoder_breakout_15x15mm.stl, airframe/placeholders/gcs/Antenna_2p4GHz_zigbee_rubber_duck.stl, airframe/placeholders/gcs/Antenna_49MHz_whip_940mm.stl, airframe/placeholders/gcs/Antenna_5GHz_panel_14dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_Yagi_9dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_omni_5dBi.stl, airframe/placeholders/gcs/Antenna_GNSS_patch_ANN_MB00.stl, airframe/placeholders/gcs/B6804_20x32x7mm_gimbal_pan.stl, airframe/placeholders/gcs/GPS_module_M10Q_SparkFun.stl, airframe/placeholders/gcs/N42_disc_magnet_6x2mm.stl, airframe/placeholders/gcs/Pololu_D24V22F6_6V2A_BEC.stl, airframe/placeholders/gcs/Pololu_D24V50F5_5V5A_BEC.stl, airframe/placeholders/gcs/RF_splitter_915MHz_2way_ZFSC.stl, airframe/placeholders/gcs/Skipper_enclosure_IP65_145x90x65mm.stl, airframe/placeholders/gcs/Skipper_tripod_antenna_mast.stl, airframe/placeholders/gcs/TCA9548A_I2C_mux_breakout.stl, airframe/placeholders/power/LiPo_4S_10000mAh_175x64x38mm_GCS.stl, gcs/AGENTS.md, gcs/LICENSE, gcs/README.md, gcs/SKIPPER_SPEC.md, gcs/TODO.md, gcs/WBS.md, gcs/skipper/README.md, gcs/skipper/firmware/pb2i/CMakeLists.txt, gcs/skipper/firmware/pb2i/dts/k3-am6254-pocketbeagle2-skipper-cape-b2.dts, gcs/skipper/firmware/pb2i/src/skipper_config.h, gcs/skipper/firmware/pb2i/src/skipper_gimbal.c, gcs/skipper/firmware/pb2i/src/skipper_gimbal.h, gcs/skipper/firmware/pb2i/src/skipper_telemetry.c, gcs/skipper/firmware/pb2i/src/skipper_telemetry.h, gcs/skipper/hardware/docs/skipper_antenna_spec.md, gcs/skipper/hardware/docs/skipper_power_budget.md, gcs/skipper/hardware/docs/skipper_wiring.md, gcs/skipper/hardware/enclosure/openscad/skipper_field_enclosure.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_mount.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_pan.scad, gcs/skipper/hardware/gimbal/openscad/skipper_gimbal_tilt.scad, gcs/skipper/software/config/mavlink_router.conf, gcs/skipper/software/config/skipper_config.yaml, gcs/skipper/software/install/install_deps.sh, gcs/skipper/software/install/install_mavlink_router.sh, gcs/skipper/software/install/install_qgc.sh, gcs/skipper/software/tracking/requirements.txt, gcs/skipper/software/tracking/src/gimbal_ctrl.py, gcs/skipper/software/tracking/src/telemetry_feed.py, gcs/skipper/software/tracking/src/tracker.py, gcs/skipper/software/tracking/tests/test_tracker.py @@ -32,10 +32,10 @@ Grep a tag name below to get every matching file in one line, without reading th - `propulsion` (125): airframe/blender-scripts/blender_edf_bore_and_petals.py, airframe/blender-scripts/blender_nacelle_revo.py, airframe/blender-scripts/blender_nozzle_gen.py, airframe/blender-scripts/check_nacelle_alignment.py, airframe/openscad/nacelles/_export_inboard_cut.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/edf_aft_spider_sleeve.scad, airframe/openscad/nacelles/edf_stator_sleeve.scad, airframe/openscad/nacelles/gear_option_compare.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/nacelles/nacelle_nozzle_pushrod.scad, airframe/openscad/nacelles/nacelle_pod_50mm_tandem.scad, airframe/openscad/nacelles/nacelle_servo_bracket.scad, airframe/placeholders/foam/Void_nacelle_pylon_20x80x20mm.stl, airframe/placeholders/propulsion/EDF_120mm_6S_deferred.stl, airframe/placeholders/propulsion/EDF_50mm_6S.stl, airframe/placeholders/propulsion/ESC_40A_6S_BLHeli32.stl, airframe/placeholders/propulsion/ESC_80A_6S_BLHeli32_deferred.stl, airframe/stls/fuselage/middle_canonical_edf_intake.stl, airframe/stls/nacelles/edf_aft_spider_sleeve.stl, airframe/stls/nacelles/edf_stator_sleeve.stl, airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl, airframe/stls/nacelles/eng_right_shell24_50mm_repaired.stl, airframe/stls/nacelles/nacelle_port_revs.stl, airframe/stls/nacelles/nacelle_servo_bracket.stl, airframe/stls/nacelles/nacelle_stbd_revs.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap_seal.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-closed.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-open.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_ring.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_throat.stl, airframe/wings-nacelles/TODO.md, airframe/wings-nacelles/WBS.md, avionics/kicad/ENC-NACELLE-1.kicad_pcb, avionics/kicad/ENC-NACELLE-1.kicad_prl, avionics/kicad/ENC-NACELLE-1.kicad_pro, avionics/kicad/ENC-NACELLE-1.kicad_sch, avionics/kicad/ENC-NACELLE-1.md, avionics/kicad/ENC-NACELLE-1.net, avionics/kicad/gen_enc_nacelle_pcb.py, deferred/aft-edf/README.md, deferred/aft-edf/openscad/aft_edf_plenum.scad, deferred/aft-edf/openscad/edf_120_motor_mount.scad, deferred/aft-edf/openscad/edf_120_thrust_tube.scad, deferred/aft-edf/openscad/neck_intake_frame.scad, deferred/aft-edf/openscad/rear_neck_intake_shell24.scad, deferred/aft-edf/stls/aft_edf_plenum.stl, deferred/aft-edf/stls/edf_120_motor_mount.stl, deferred/aft-edf/stls/edf_120_thrust_tube.stl, deferred/aft-edf/stls/neck_intake_frame.stl, deferred/aft-edf/stls/rear_nozzle_closed_asm.stl, deferred/aft-edf/stls/rear_nozzle_frame.stl, deferred/aft-edf/stls/rear_nozzle_petal.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_0.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_1.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_2.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_3.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_4.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_5.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_6.stl, deferred/aft-edf/stls/rear_nozzle_petal_hull_7.stl, deferred/aft-edf/stls/rear_shell24_2mm_edf_bored.stl, docs/NOZZLE_DRIVE_TRADE.md, docs/img/nozzle_drive_trade.png, docs/img/port-nacelle-horizontal.png, docs/img/starboard-nacelle-vert.png, docs/img/transparent-nacelle.png, docs/plans/2026-08-26-001-nacelle-esc-intake-integration-plan.md, docs/plans/2026-08-27-nacelle-wiring-plan.md, docs/plans/2026-08-29-001-nacelle-nav-wire-hall-reconciliation-plan.md, docs/plans/2026-08-29-002-nacelle-esc-power-routing-plan.md, docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md, docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model.html, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/1.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/2.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37110-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37111-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37112-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-large_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37114-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37115-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/76a8e5b0fe9140cf057b953efbc49a1f.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/EDF50mm-02.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/analytics.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css_002.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades-3s4.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_003.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_004.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_005.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_006.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_007.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/ga.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/mollie.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_114_a4444adaf1056832dd63b28b928b74e6.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_03a718a060cf48c0e5393bbea5cb1aa7_all.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_c1bb31e2f03fadfdc659940cc14955dc_print.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-64mm-edf-glider-nacelle-for-swift-2100.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-edf-glider-nacelle-for-swift-2100-827-50mm.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-model-logo-16966716258.jpg, docs/references/Screenshot 2026-08-18 at 09-50-31 EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades 6S Motor 3200KV - Xfly-Model.png, graphical-build-guide/build_guide_03_print_nacelle.svg, graphical-build-guide/build_guide_06_nacelle_pivot.svg, graphical-build-guide/build_guide_07_edf_install.svg, graphical-build-guide/build_guide_08_nozzle_gear.svg, graphical-build-guide/pngs/17_closeup_nacelle.png, tools/nacelle_esc_service.py, tools/tests/test_nacelle_esc_service.py - `redundancy-failover` (35): AGENTS.md, README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/middle_canonical_shell24.scad, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/emi-hardening/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/WBS.md, avionics/firmware/common/include/failsafe_config.h, current-specification/README.md, deferred/README.md, docs/AVIONICS_PB2_REDESIGN.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/ETHERNET_PHY_TRADE.md, docs/FIRST_FLIGHT_READINESS.md, docs/PHASED_BUILD_GUIDE.md, docs/README.md, docs/REVN_BUILD_GUIDE_24IN.md, docs/electrical_fault_margins.md, docs/failsafe_thresholds.md, gcs/AGENTS.md, gcs/SKIPPER_SPEC.md, gcs/WBS.md, gcs/skipper/hardware/docs/skipper_antenna_spec.md, graphical-build-guide/README.md, graphical-build-guide/TODO-old.md, graphical-build-guide/WBS.md, graphical-build-guide/flight-phases/TODO.md, graphical-build-guide/flight-phases/WBS.md, tools/TOOL_REFERENCE.md, tools/precommit_index.py - `repo-infra` (10): .githooks/pre-commit, .github/linters/.ecrc, .github/linters/.markdown-lint.yml, .github/workflows/ci.yml, .github/workflows/devskim.yml, .github/workflows/stale-branches.yml, .github/workflows/super-linter.yml, .vscode/c_cpp_properties.json, .vscode/extensions.json, .vscode/settings.json -- `security` (106): AGENTS.md, CLAUDE-MEMORY.md, README.md, REFERENCES.md, SECURITY.md, TODO.md, airframe/FreeCAD-scripts/serenity_assembly.py, airframe/README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/fuselage-joints/WBS.md, airframe/landing-gear/WBS.md, airframe/openscad/fuselage/cargo/cargo_spar_drive.scad, airframe/openscad/fuselage/landing_leg_assy.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/port_tilt_spar_assembly.scad, airframe/placeholders/generate_placeholders.py, airframe/stls/fuselage/cargo/generate_cargo_hinge_retention.py, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/README.md, avionics/firmware/TODO.md, avionics/firmware/WBS.md, avionics/firmware/cn/src/main.c, avionics/firmware/common/src/sbus_input.c, avionics/firmware/dts/README.md, avionics/firmware/dts/cape-a/k3-am6254-pocketbeagle2-serenity-cape-a2.dts, avionics/firmware/dts/cape-b/k3-am6254-pocketbeagle2-serenity-cape-b2.dts, avionics/firmware/fc/src/main.c, avionics/kicad/CAN-PERIPH-GW-1/CAN-PERIPH-GW-1.md, avionics/kicad/CAN-PERIPH-GW-1/scripts/gen_can_periph_gw_pcb.py, avionics/kicad/CAN-PERIPH-GW-1/scripts/gen_can_periph_gw_sch.py, avionics/kicad/Commo/Commo.md, avionics/kicad/Commo/scripts/inject_commo_tpm.py, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/scripts/inject_flight_engineer_trust_module.py, avionics/kicad/Observer/OBSERVER_SOM_NETMAP.md, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/scripts/gen_observer_carrier_pcb.py, avionics/kicad/Observer/scripts/gen_observer_carrier_sch.py, avionics/kicad/Observer/scripts/gen_observer_ic_symbols.py, avionics/kicad/Observer/scripts/gen_observer_pcb.py, avionics/kicad/Pilot/PILOT_FOOTPRINT_VERIFICATION.md, avionics/kicad/Pilot/scripts/gen_pilot_sch.py, avionics/kicad/Pilot/scripts/mod_pilot_pcb_reconcile.py, avionics/kicad/README.md, avionics/kicad/TODO-1.2b-STATUS-REPORT.md, avionics/kicad/replace_footprints.py, avionics/kicad/retarget_mspm0g351x_slb9672.py, avionics/kicad/retarget_pcb_footprints.py, avionics/kicad/symbols/SLB9672_TPM.kicad_sym, avionics/observer/TODO.md, avionics/observer/WBS.md, avionics/rev-s1/WBS.md, current-specification/LICENSE_AND_ATTRIBUTION.md, current-specification/README.md, current-specification/serenity-rev-s.jsx, docs/AGENTS.md, docs/AVIONICS_PB2_REDESIGN.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/FIRST_FLIGHT_READINESS.md, docs/OBSERVER_LASER_ANALYSIS.md, docs/OBSERVER_MANUFACTURING_READINESS.md, docs/OSHW_CERTIFICATION.md, docs/PHASED_BUILD_GUIDE.md, docs/POWER_DISTRIBUTION.md, docs/PROTO_PRINT_DAVINCI_JR.md, docs/README.md, docs/TODO_1_1_0_COMPLETION_SUMMARY.md, docs/VERIFY_PLACEMENT_WORKFLOW.md, docs/WBS.md, docs/plans/2026-08-25-001-finish-avionics-plan.md, docs/plans/2026-08-25-003-feat-emi-rf-design-test-plan-plan.md, docs/solutions/design-patterns/reducing-token-cost-of-federated-agents-md.md, gcs/AGENTS.md, gcs/README.md, gcs/SKIPPER_SPEC.md, gcs/TODO.md, gcs/WBS.md, gcs/skipper/README.md, gcs/skipper/firmware/pb2i/dts/k3-am6254-pocketbeagle2-skipper-cape-b2.dts, gcs/skipper/firmware/pb2i/src/skipper_config.h, gcs/skipper/firmware/pb2i/src/skipper_telemetry.c, gcs/skipper/hardware/docs/skipper_power_budget.md, graphical-build-guide/AGENTS.md, graphical-build-guide/BUILD_GUIDE_TEMPLATE.md, graphical-build-guide/README.md, graphical-build-guide/REVN_BUILD_GUIDE_24IN.md, graphical-build-guide/TODO-old.md, graphical-build-guide/TODO.md, graphical-build-guide/build_guide_12_security_hw.svg, graphical-build-guide/flight-phases/TODO.md, graphical-build-guide/flight-phases/WBS.md, tools/TOOL_REFERENCE.md, tools/add_landing_gear_bosses.py, tools/build_landing_gear_views.py, tools/landing_gear_bay_pad_fit.py, tools/open_mating_faces.py, tools/precommit_index.py, tools/wing_internal_clearance.py, tools/wing_root_deconflict.py, tools/wing_spar_carrythrough.py +- `security` (105): AGENTS.md, CLAUDE-MEMORY.md, README.md, REFERENCES.md, SECURITY.md, TODO.md, airframe/FreeCAD-scripts/serenity_assembly.py, airframe/README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/fuselage-joints/WBS.md, airframe/landing-gear/WBS.md, airframe/openscad/fuselage/cargo/cargo_spar_drive.scad, airframe/openscad/fuselage/landing_leg_assy.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/port_tilt_spar_assembly.scad, airframe/placeholders/generate_placeholders.py, airframe/stls/fuselage/cargo/generate_cargo_hinge_retention.py, avionics/AGENTS.md, avionics/README.md, avionics/TODO-1-9-COMPLETION-STATUS.md, avionics/TODO.md, avionics/WBS.md, avionics/firmware/AK7455_CALIBRATION_SPECIFICATION.md, avionics/firmware/README.md, avionics/firmware/TODO.md, avionics/firmware/WBS.md, avionics/firmware/cn/src/main.c, avionics/firmware/common/src/sbus_input.c, avionics/firmware/dts/README.md, avionics/firmware/dts/cape-a/k3-am6254-pocketbeagle2-serenity-cape-a2.dts, avionics/firmware/dts/cape-b/k3-am6254-pocketbeagle2-serenity-cape-b2.dts, avionics/firmware/fc/src/main.c, avionics/kicad/CAN-PERIPH-GW-1/CAN-PERIPH-GW-1.md, avionics/kicad/CAN-PERIPH-GW-1/scripts/gen_can_periph_gw_pcb.py, avionics/kicad/CAN-PERIPH-GW-1/scripts/gen_can_periph_gw_sch.py, avionics/kicad/Commo/Commo.md, avionics/kicad/Commo/scripts/inject_commo_tpm.py, avionics/kicad/FlightEngineer/FlightEngineer.md, avionics/kicad/FlightEngineer/scripts/inject_flight_engineer_trust_module.py, avionics/kicad/Observer/OBSERVER_SOM_NETMAP.md, avionics/kicad/Observer/Observer.md, avionics/kicad/Observer/scripts/gen_observer_carrier_pcb.py, avionics/kicad/Observer/scripts/gen_observer_carrier_sch.py, avionics/kicad/Observer/scripts/gen_observer_ic_symbols.py, avionics/kicad/Observer/scripts/gen_observer_pcb.py, avionics/kicad/Pilot/PILOT_FOOTPRINT_VERIFICATION.md, avionics/kicad/Pilot/scripts/gen_pilot_sch.py, avionics/kicad/Pilot/scripts/mod_pilot_pcb_reconcile.py, avionics/kicad/README.md, avionics/kicad/TODO-1.2b-STATUS-REPORT.md, avionics/kicad/replace_footprints.py, avionics/kicad/retarget_mspm0g351x_slb9672.py, avionics/kicad/retarget_pcb_footprints.py, avionics/kicad/symbols/SLB9672_TPM.kicad_sym, avionics/observer/TODO.md, avionics/observer/WBS.md, avionics/rev-s1/WBS.md, current-specification/LICENSE_AND_ATTRIBUTION.md, current-specification/README.md, current-specification/serenity-rev-s.jsx, docs/AGENTS.md, docs/AVIONICS_PB2_REDESIGN.md, docs/DOCUMENTATION_RECONCILIATION_2026-07-28.md, docs/FIRST_FLIGHT_READINESS.md, docs/OBSERVER_LASER_ANALYSIS.md, docs/OBSERVER_MANUFACTURING_READINESS.md, docs/OSHW_CERTIFICATION.md, docs/PHASED_BUILD_GUIDE.md, docs/POWER_DISTRIBUTION.md, docs/PROTO_PRINT_DAVINCI_JR.md, docs/README.md, docs/TILT_DRIVE_CONTROL_SPEC.md, docs/TODO_1_1_0_COMPLETION_SUMMARY.md, docs/VERIFY_PLACEMENT_WORKFLOW.md, docs/WBS.md, docs/plans/2026-08-25-001-finish-avionics-plan.md, docs/plans/2026-08-25-003-feat-emi-rf-design-test-plan-plan.md, docs/solutions/design-patterns/reducing-token-cost-of-federated-agents-md.md, gcs/AGENTS.md, gcs/README.md, gcs/SKIPPER_SPEC.md, gcs/TODO.md, gcs/WBS.md, gcs/skipper/README.md, gcs/skipper/firmware/pb2i/dts/k3-am6254-pocketbeagle2-skipper-cape-b2.dts, gcs/skipper/firmware/pb2i/src/skipper_config.h, gcs/skipper/firmware/pb2i/src/skipper_telemetry.c, gcs/skipper/hardware/docs/skipper_power_budget.md, graphical-build-guide/AGENTS.md, graphical-build-guide/BUILD_GUIDE_TEMPLATE.md, graphical-build-guide/README.md, graphical-build-guide/REVN_BUILD_GUIDE_24IN.md, graphical-build-guide/TODO-old.md, graphical-build-guide/TODO.md, graphical-build-guide/build_guide_12_security_hw.svg, graphical-build-guide/flight-phases/TODO.md, graphical-build-guide/flight-phases/WBS.md, tools/TOOL_REFERENCE.md, tools/add_landing_gear_bosses.py, tools/build_landing_gear_views.py, tools/landing_gear_bay_pad_fit.py, tools/open_mating_faces.py, tools/precommit_index.py, tools/wing_internal_clearance.py - `sensors-vision` (13): airframe/openscad/fuselage/bow_sensor_faceplate.scad, airframe/openscad/fuselage/bow_sensor_pod.scad, airframe/stls/fuselage/bow_sensor_faceplate.stl, avionics/kicad/Observer/Observer.pretty/DS_Camera_9P.kicad_mod, avionics/kicad/Observer/Observer.pretty/DS_Laser_2P.kicad_mod, avionics/kicad/Observer/Observer.pretty/DS_ToF_4P.kicad_mod, avionics/kicad/Observer/Vera.pretty/DS_Camera_9P.kicad_mod, avionics/kicad/Observer/Vera.pretty/DS_Laser_2P.kicad_mod, avionics/kicad/Observer/Vera.pretty/DS_ToF_4P.kicad_mod, avionics/kicad/Observer/kicads/Jayne.kicad_sch.pre-g351x, avionics/kicad/add_sensors_sbus.py, docs/OBSERVER_LASER_ANALYSIS.md, graphical-build-guide/build_guide_25_obstacle_sensors.svg - `specification` (131): REFERENCES.md, airframe/HULL_FRAME_REFERENCE.md, current-specification/AGENTS.md, current-specification/LICENSE, current-specification/LICENSE_AND_ATTRIBUTION.md, current-specification/README.md, current-specification/SPEC_TEMPLATE.md, current-specification/TODO.md, current-specification/bom_revS.csv, current-specification/bom_revS.json, current-specification/serenity-rev-s.jsx, docs/references/108090023_STS3215-C001_Datasheet.pdf, docs/references/Bus_servo_control_circuit.pdf, docs/references/CERN_OHL_rationale.pdf, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model.html, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/1.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/2.jpg, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37110-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37111-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37112-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37113-large_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37114-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/37115-cart_default.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/76a8e5b0fe9140cf057b953efbc49a1f.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/EDF50mm-02.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/analytics.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/css_002.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x3-xfly-model-30mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades-3s4.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x4-xfly-model-40mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_003.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_004.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_005.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_006.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x5-xfly-model-50mm-12-blades_007.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-3s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-4s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x6-xfly-model-64mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x7-xfly-model-70mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades-6s-.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/edf-ducted-fan-xfly-galaxy-x8-xfly-model-80mm-12-blades_002.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/ga.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/mollie.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_114_a4444adaf1056832dd63b28b928b74e6.js, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_03a718a060cf48c0e5393bbea5cb1aa7_all.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/v_138_c1bb31e2f03fadfdc659940cc14955dc_print.css, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-64mm-edf-glider-nacelle-for-swift-2100.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-edf-glider-nacelle-for-swift-2100-827-50mm.webp, docs/references/EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades + 6S Motor 3200KV - Xfly-Model_files/xfly-model-logo-16966716258.jpg, docs/references/POCKETBEAGLE2 V10_241220.DSN, docs/references/ST3215 memory register map-EN.xls, docs/references/ST3215-2D.zip, docs/references/ST3215-3D.zip, docs/references/Screenshot 2026-08-18 at 09-50-31 EDF Ducted Fan XFly Galaxy X5 XFLY-Model 50mm 12 blades 6S Motor 3200KV - Xfly-Model.png, docs/references/Servo_Driver_with_ESP32_3D.zip, docs/references/The_Official_Serenity_Blueprints_Reference_Pack.pdf, docs/references/nick-henning/Re: Contact got a new submission - Nick Henning - 2026-07-06 1421.txt, docs/references/nick-henning/nick-henning-close-back-combine.jpg, docs/references/nick-henning/nick-henning-close-bridge-combine.jpg, docs/references/nick-henning/nick-henning-close-gear-combine.jpg, docs/references/nick-henning/nick-henning-final-backside-combine.jpg, docs/references/nick-henning/nick-henning-final-front-combine.jpg, docs/references/nick-henning/nick-henning-final-top-combine.jpg, docs/references/nick-henning/nick-henning-uvdisplay-engine.jpg, docs/references/nick-henning/nick-henning-uvdisplay-gear.jpg, docs/references/nick-henning/nick-henning-uvdisplay-wing.jpg, docs/references/nick-henning/port-bow-full-henning.jpg, docs/references/nick-henning/port-bow-upper-bridge-with-wireframe-henning.jpg, docs/references/nick-henning/port-bow-view-with-wireframe-henning.jpg, docs/references/nick-henning/port-quarter-with-wireframe-henning.jpg, docs/references/nick-henning/top-view-with-wireframe-henning.jpg, docs/references/open-source-hardware-checklist.pdf, docs/references/pocketbeagle2 revA_241205.brd, docs/references/scservo.zip.zip, docs/references/thingverse-serenity/LICENSE.txt, docs/references/thingverse-serenity/README.txt, docs/references/thingverse-serenity/files/s_cargo_door.stl, docs/references/thingverse-serenity/files/s_cargo_door_strutts.stl, docs/references/thingverse-serenity/files/s_cargo_sect.stl, docs/references/thingverse-serenity/files/s_eng_left.stl, docs/references/thingverse-serenity/files/s_eng_pistons.stl, docs/references/thingverse-serenity/files/s_eng_piv_outer.stl, docs/references/thingverse-serenity/files/s_eng_piv_pins.stl, docs/references/thingverse-serenity/files/s_eng_right.stl, docs/references/thingverse-serenity/files/s_feet_x_4.stl, docs/references/thingverse-serenity/files/s_head.stl, docs/references/thingverse-serenity/files/s_legs.stl, docs/references/thingverse-serenity/files/s_middle.stl, docs/references/thingverse-serenity/files/s_pivot_arm_a.stl, docs/references/thingverse-serenity/files/s_rear.stl, docs/references/thingverse-serenity/files/s_wings_both.stl, docs/references/thingverse-serenity/images/front-starboard.png, docs/references/thingverse-serenity/images/s_01.jpg, docs/references/thingverse-serenity/images/s_02.jpg, docs/references/thingverse-serenity/images/s_03.jpg, docs/references/thingverse-serenity/images/s_04.jpg, docs/references/thingverse-serenity/images/s_05.jpg, docs/references/thingverse-serenity/images/s_06.jpg, docs/references/thingverse-serenity/images/s_07.jpg, docs/references/thingverse-serenity/images/s_08.jpg, docs/references/thingverse-serenity/images/s_09.jpg, docs/references/thingverse-serenity/images/s_10.jpg, docs/references/thingverse-serenity/images/s_11.jpg, docs/references/thingverse-serenity/images/s_cargo_door.png, docs/references/thingverse-serenity/images/s_cargo_door_strutts.png, docs/references/thingverse-serenity/images/s_cargo_sect.png, docs/references/thingverse-serenity/images/s_eng_left.png, docs/references/thingverse-serenity/images/s_eng_pistons.png, docs/references/thingverse-serenity/images/s_eng_piv_outer.png, docs/references/thingverse-serenity/images/s_eng_piv_pins.png, docs/references/thingverse-serenity/images/s_eng_right.png, docs/references/thingverse-serenity/images/s_feet_x_4.png, docs/references/thingverse-serenity/images/s_head.png, docs/references/thingverse-serenity/images/s_legs.png, docs/references/thingverse-serenity/images/s_middle.png, docs/references/thingverse-serenity/images/s_pivot_arm_a.png, docs/references/thingverse-serenity/images/s_rear.png, docs/references/thingverse-serenity/images/s_wings_both.png, docs/references/vimdrones_can_periph_pico_v1.0.stl, docs/references/vimdrones_esc_s50_v1.0.step, docs/references/vimdrones_esc_s50_v1.0.stl, docs/references/vimdrones_esc_s50_wiring.png, tools/TOOL_REFERENCE.md -- `structural` (266): airframe/AGENTS.md, airframe/FreeCAD-scripts/Makefile, airframe/FreeCAD-scripts/faraday-enclosure.py, airframe/FreeCAD-scripts/freecad-stl2part.py, airframe/FreeCAD-scripts/make_flat_pattern.py, airframe/FreeCAD-scripts/serenity_assembly.py, airframe/FreeCAD-scripts/serenity_placeholders_assembly.py, airframe/HULL_FRAME_REFERENCE.md, airframe/LICENSE, airframe/README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/TODO.md, airframe/VERIFY_PLACEMENT_CHECKLIST.md, airframe/WBS.md, airframe/blender-scripts/add_structural_features.py, airframe/blender-scripts/blender_edf_bore_and_petals.py, airframe/blender-scripts/blender_hollow_shells.py, airframe/blender-scripts/blender_intake_cut.py, airframe/blender-scripts/blender_middle_intake_cut.py, airframe/blender-scripts/blender_nacelle_revo.py, airframe/blender-scripts/blender_nozzle_gen.py, airframe/blender-scripts/blender_shells_2mm_solidify.py, airframe/blender-scripts/blender_shells_v3.py, airframe/blender-scripts/blender_stator_gen.py, airframe/blender-scripts/check_nacelle_alignment.py, airframe/blender-scripts/engrave_plaques.py, airframe/blender-scripts/engrave_shuttles.py, airframe/blender-scripts/files-hollowed-24in/cargo_sect_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/head_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/middle_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_flight_engineer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_inara.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_river.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_shepherd.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_simon.stl, airframe/blender-scripts/files-hollowed-24in/rear_shell24_2mm_repaired.stl, airframe/blender-scripts/fill_thin_details.py, airframe/blender-scripts/finalize_cargo_middle.py, airframe/blender-scripts/finalize_head_rear.py, airframe/blender-scripts/generate_overview_svgs.py, airframe/blender-scripts/hollow_manifold.py, airframe/blender-scripts/inspect_shell_center.py, airframe/blender-scripts/make_bay_text.py, airframe/blender-scripts/make_shuttle_text.py, airframe/blender-scripts/merge_cargo_interior.py, airframe/blender-scripts/merge_head_interior.py, airframe/blender-scripts/morph_open_voxel.py, airframe/blender-scripts/regen_rear_interior.py, airframe/blender-scripts/repair_shells_for_scad.py, airframe/blender-scripts/serenity_render_views.py, airframe/blender-scripts/verify_shells.py, airframe/diagrams/ring_frames/ring_cargo_Y30_inner.csv, airframe/diagrams/ring_frames/ring_cargo_Y30_plate_PROVISIONAL.dxf, airframe/diagrams/ring_frames/ring_rear_Y290_inner.csv, airframe/diagrams/ring_frames/ring_rear_Y290_plate.dxf, airframe/freecad/assembly/SerenityAssembly.FCStd.bak2, airframe/freecad/assembly/leg_2_scaled-oriented.stl, airframe/fuselage-covers/TODO.md, airframe/fuselage-covers/WBS.md, airframe/fuselage-joints/TODO.md, airframe/fuselage-joints/WBS.md, airframe/fuselage-mid/TODO.md, airframe/fuselage-mid/WBS.md, airframe/gcode/davinci-jr-proto/.gitignore, airframe/gcode/davinci-jr-proto/davinci_jr_pla.ini, airframe/gcode/davinci-jr-proto/slice_all_batches.sh, airframe/gcode/davinci-jr-proto/xyz_wrap.py, airframe/landing-gear/HANDOFF.md, airframe/landing-gear/TODO.md, airframe/landing-gear/WBS.md, airframe/openscad/fuselage/access_panels_24in.scad, airframe/openscad/fuselage/battery_tray.scad, airframe/openscad/fuselage/belly_panel.scad, airframe/openscad/fuselage/bow_sensor_faceplate.scad, airframe/openscad/fuselage/bow_sensor_pod.scad, airframe/openscad/fuselage/canonical_leg_r6_1_5in.scad, airframe/openscad/fuselage/canonical_leg_r6_3_0in.scad, airframe/openscad/fuselage/cargo/_export_cargo_port_root_chunk.scad, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/cargo/cargo_spar_drive.scad, airframe/openscad/fuselage/cargo/cargo_vera_faraday.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/landing_leg_assy.scad, airframe/openscad/fuselage/middle_canonical_shell24.scad, airframe/openscad/fuselage/rcrs49_wire_post.scad, airframe/openscad/fuselage/rear_shell24.scad, airframe/openscad/fuselage/wire_brace_leg.scad, airframe/openscad/fuselage/wire_loop_fuse.scad, airframe/openscad/nacelles/_export_inboard_cut.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/edf_aft_spider_sleeve.scad, airframe/openscad/nacelles/edf_stator_sleeve.scad, airframe/openscad/nacelles/gear_option_compare.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/nacelles/nacelle_nozzle_pushrod.scad, airframe/openscad/nacelles/nacelle_pod_50mm_tandem.scad, airframe/openscad/nacelles/nacelle_servo_bracket.scad, airframe/openscad/port_tilt_spar_assembly.scad, airframe/openscad/wings/wings_s1223_revo.scad, airframe/openscad/wings/wings_s1223_revo.scad.backup-2026-07-18, airframe/placeholders/avionics/Cape_A2_PCB_55x35mm.stl, airframe/placeholders/avionics/Cape_B2_PCB_55x35mm.stl, airframe/placeholders/avionics/FlightEngineer_PDB_90x65mm.stl, airframe/placeholders/avionics/PocketBeagle2_Industrial_56x35mm.stl, airframe/placeholders/avionics/XCVR_49MHZ2_PCB_55x35mm.stl, airframe/placeholders/avionics/microSD_64GB.stl, airframe/placeholders/bearings/B6804_20x32x7mm_GCS.stl, airframe/placeholders/bearings/MF104ZZ_4x10x4mm.stl, airframe/placeholders/bearings/MR63ZZ_3x6x2p5mm.stl, airframe/placeholders/cargo/DRV8833_Hbridge_breakout.stl, airframe/placeholders/cargo/Dyneema_SK75_0p5mm_coil.stl, airframe/placeholders/cargo/HX711_loadcell_ADC_breakout.stl, airframe/placeholders/cargo/N20_motor_300RPM_6V.stl, airframe/placeholders/faraday/Far_EMI_vent_40x40x6mm.stl, airframe/placeholders/faraday/Far_FT_panel_55x35mm.stl, airframe/placeholders/faraday/Far_bond_strap_100mm.stl, airframe/placeholders/faraday/Far_cage_AV_70x50x82mm.stl, airframe/placeholders/faraday/Far_fan_40mm_5V.stl, airframe/placeholders/faraday/Far_ferrite_4mm_ID.stl, airframe/placeholders/faraday/Far_gasket_AV_250x6x1mm.stl, airframe/placeholders/faraday/Mal_far_fan_40mm_5V.stl, airframe/placeholders/faraday/Mal_far_gasket_470x8x1p5mm.stl, airframe/placeholders/foam/Foam_fill_cargo_190x200x159mm.stl, airframe/placeholders/foam/Foam_fill_head_125x231x136mm.stl, airframe/placeholders/foam/Foam_fill_middle_horseshoe_173x69x161mm.stl, airframe/placeholders/foam/Foam_fill_rear_136x177x154mm.stl, airframe/placeholders/foam/Void_avionics_bay_62x42x75mm.stl, airframe/placeholders/foam/Void_cargo_bay_120x150x80mm.stl, airframe/placeholders/foam/Void_far_cage_76x56x88mm.stl, airframe/placeholders/foam/Void_far_fan_spur_44x44x50mm.stl, airframe/placeholders/foam/Void_nacelle_pylon_20x80x20mm.stl, airframe/placeholders/foam/Void_power_bus_25x500x25mm.stl, airframe/placeholders/foam/Void_vent_exhaust_20x300x20mm.stl, airframe/placeholders/foam/Void_vent_intake_20x250x20mm.stl, airframe/placeholders/foam/Void_wiring_trunk_30x700x20mm.stl, airframe/placeholders/gcs/AS5600_encoder_breakout_15x15mm.stl, airframe/placeholders/gcs/Antenna_2p4GHz_zigbee_rubber_duck.stl, airframe/placeholders/gcs/Antenna_49MHz_whip_940mm.stl, airframe/placeholders/gcs/Antenna_5GHz_panel_14dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_Yagi_9dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_omni_5dBi.stl, airframe/placeholders/gcs/Antenna_GNSS_patch_ANN_MB00.stl, airframe/placeholders/gcs/B6804_20x32x7mm_gimbal_pan.stl, airframe/placeholders/gcs/GPS_module_M10Q_SparkFun.stl, airframe/placeholders/gcs/N42_disc_magnet_6x2mm.stl, airframe/placeholders/gcs/Pololu_D24V22F6_6V2A_BEC.stl, airframe/placeholders/gcs/Pololu_D24V50F5_5V5A_BEC.stl, airframe/placeholders/gcs/RF_splitter_915MHz_2way_ZFSC.stl, airframe/placeholders/gcs/Skipper_enclosure_IP65_145x90x65mm.stl, airframe/placeholders/gcs/Skipper_tripod_antenna_mast.stl, airframe/placeholders/gcs/TCA9548A_I2C_mux_breakout.stl, airframe/placeholders/gears/Bevel_M1_14T_pair.stl, airframe/placeholders/gears/Bevel_housing_CF_PETG.stl, airframe/placeholders/gears/Pinion_M1_12T_R6mm.stl, airframe/placeholders/gears/Sector_gear_M1_R22mm.stl, airframe/placeholders/generate_placeholders.py, airframe/placeholders/hardware/Batt_strap_silicone_16mm_CAM.stl, airframe/placeholders/hardware/Insert_M25_brass_L5.stl, airframe/placeholders/hardware/Insert_M3_brass_L5.stl, airframe/placeholders/hardware/Pin_SS_3x5mm_hinge.stl, airframe/placeholders/hardware/Screw_M3x8mm_button_ISO7380.stl, airframe/placeholders/lighting/WS2812C_2020_SMD.stl, airframe/placeholders/power/Fuse_MAXI_150A.stl, airframe/placeholders/power/Fuse_mini_40A.stl, airframe/placeholders/power/LiPo_4S_10000mAh_175x64x38mm_GCS.stl, airframe/placeholders/power/LiPo_6S_2800mAh_115x35x35mm.stl, airframe/placeholders/power/LiPo_6S_4000mAh_138x44x36mm.stl, airframe/placeholders/power/Shunt_CSS2H_2512K_1mohm.stl, airframe/placeholders/propulsion/EDF_120mm_6S_deferred.stl, airframe/placeholders/propulsion/EDF_50mm_6S.stl, airframe/placeholders/propulsion/ESC_40A_6S_BLHeli32.stl, airframe/placeholders/propulsion/ESC_80A_6S_BLHeli32_deferred.stl, airframe/placeholders/servos/DS3218MG_25kgcm.stl, airframe/placeholders/servos/SG90_micro.stl, airframe/placeholders/structural/CF_bar_6x3mm_620mm_keel.stl, airframe/placeholders/structural/CF_plate_2mm_200x300mm.stl, airframe/placeholders/structural/CF_rod_3mm_300mm_stock.stl, airframe/placeholders/structural/CF_rod_4mm_300mm_stock.stl, airframe/placeholders/structural/CF_tube_12mm_OD_1p5w_350mm_spar.stl, airframe/placeholders/structural/PTFE_sleeve_4mm_OD_3mm_ID_52mm.stl, airframe/placeholders/wiring/PTFE_conduit_4mm_OD_3mm_ID_700mm.stl, airframe/placeholders/wiring/Post_49MHz_base_load_coil.stl, airframe/placeholders/wiring/Wire_10AWG_silicone_400mm.stl, airframe/placeholders/wiring/Wire_28AWG_STP_bundle_500mm.stl, airframe/placeholders/wiring/Wire_49MHz_antenna_0p3mm_470mm.stl, airframe/placeholders/wiring/Wire_4AWG_silicone_200mm_pair.stl, airframe/stls/fuselage/battery_tray.stl, airframe/stls/fuselage/belly_panel.stl, airframe/stls/fuselage/bow_sensor_faceplate.stl, airframe/stls/fuselage/cargo/cargo_cradle_autolatch.stl, airframe/stls/fuselage/cargo/cargo_door_port.stl, airframe/stls/fuselage/cargo/cargo_door_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_door_stbd.stl, airframe/stls/fuselage/cargo/cargo_drv8833_tray.stl, airframe/stls/fuselage/cargo/cargo_fpv_bezel.stl, airframe/stls/fuselage/cargo/cargo_gps_retention_ring.stl, airframe/stls/fuselage/cargo/cargo_hinge_retention.stl, airframe/stls/fuselage/cargo/cargo_release_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_bossed.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_repaired.stl, airframe/stls/fuselage/cargo/cargo_vera_faraday.stl, airframe/stls/fuselage/cargo/cargo_winch_motor_mount.stl, airframe/stls/fuselage/cargo/cargo_winch_spool.stl, airframe/stls/fuselage/cargo/generate_cargo_doors.py, airframe/stls/fuselage/cargo/generate_cargo_hinge_retention.py, airframe/stls/fuselage/cargo/generate_cargo_mounts.py, airframe/stls/fuselage/cargo/starboard-view-of-cargo_sect_shell24_2mm_repaired.svg, airframe/stls/fuselage/cargo_middle_splice_collar.stl, airframe/stls/fuselage/dorsal_antenna_fin.stl, airframe/stls/fuselage/generate_conforming_collars.py, airframe/stls/fuselage/head_cargo_splice_collar.stl, airframe/stls/fuselage/head_shell24.stl, airframe/stls/fuselage/head_shell24_2mm_repaired.stl, airframe/stls/fuselage/inara_access_cover.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_bay.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_nominal.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_foot.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_nominal.stl, airframe/stls/fuselage/middle_canonical_edf_intake.stl, airframe/stls/fuselage/middle_canonical_shell24.stl, airframe/stls/fuselage/middle_rear_splice_collar.stl, airframe/stls/fuselage/middle_shell24_2mm_repaired.stl, airframe/stls/fuselage/rear_shell24_2mm_repaired.stl, airframe/stls/fuselage/river_access_cover.stl, airframe/stls/nacelles/edf_aft_spider_sleeve.stl, airframe/stls/nacelles/edf_stator_sleeve.stl, airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl, airframe/stls/nacelles/eng_right_shell24_50mm_repaired.stl, airframe/stls/nacelles/nacelle_port_revs.stl, airframe/stls/nacelles/nacelle_servo_bracket.stl, airframe/stls/nacelles/nacelle_stbd_revs.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap_seal.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-closed.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-open.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_ring.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_throat.stl, airframe/stls/wings/wing_port_s1223_revo.stl, airframe/stls/wings/wing_stbd_s1223_revo.stl, airframe/wings-nacelles/TODO.md, airframe/wings-nacelles/WBS.md +- `structural` (269): airframe/AGENTS.md, airframe/FreeCAD-scripts/Makefile, airframe/FreeCAD-scripts/faraday-enclosure.py, airframe/FreeCAD-scripts/freecad-stl2part.py, airframe/FreeCAD-scripts/make_flat_pattern.py, airframe/FreeCAD-scripts/serenity_assembly.py, airframe/FreeCAD-scripts/serenity_placeholders_assembly.py, airframe/HULL_FRAME_REFERENCE.md, airframe/LICENSE, airframe/README.md, airframe/SPEC_VERIFICATION_0.6.1.md, airframe/TODO.md, airframe/VERIFY_PLACEMENT_CHECKLIST.md, airframe/WBS.md, airframe/blender-scripts/add_structural_features.py, airframe/blender-scripts/blender_edf_bore_and_petals.py, airframe/blender-scripts/blender_hollow_shells.py, airframe/blender-scripts/blender_intake_cut.py, airframe/blender-scripts/blender_middle_intake_cut.py, airframe/blender-scripts/blender_nacelle_revo.py, airframe/blender-scripts/blender_nozzle_gen.py, airframe/blender-scripts/blender_shells_2mm_solidify.py, airframe/blender-scripts/blender_shells_v3.py, airframe/blender-scripts/blender_stator_gen.py, airframe/blender-scripts/check_nacelle_alignment.py, airframe/blender-scripts/engrave_plaques.py, airframe/blender-scripts/engrave_shuttles.py, airframe/blender-scripts/files-hollowed-24in/cargo_sect_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/head_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/middle_shell24_2mm_repaired.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/cargo_sect_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/head_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/middle_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_inner_opened.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__inner.stl, airframe/blender-scripts/files-hollowed-24in/operands/rear_shell24_2mm_repaired__outer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_flight_engineer.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_inara.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_river.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_shepherd.stl, airframe/blender-scripts/files-hollowed-24in/operands/text_simon.stl, airframe/blender-scripts/files-hollowed-24in/rear_shell24_2mm_repaired.stl, airframe/blender-scripts/fill_thin_details.py, airframe/blender-scripts/finalize_cargo_middle.py, airframe/blender-scripts/finalize_head_rear.py, airframe/blender-scripts/generate_overview_svgs.py, airframe/blender-scripts/hollow_manifold.py, airframe/blender-scripts/inspect_shell_center.py, airframe/blender-scripts/make_bay_text.py, airframe/blender-scripts/make_shuttle_text.py, airframe/blender-scripts/merge_cargo_interior.py, airframe/blender-scripts/merge_head_interior.py, airframe/blender-scripts/morph_open_voxel.py, airframe/blender-scripts/regen_rear_interior.py, airframe/blender-scripts/repair_shells_for_scad.py, airframe/blender-scripts/serenity_render_views.py, airframe/blender-scripts/verify_shells.py, airframe/diagrams/ring_frames/ring_cargo_Y30_inner.csv, airframe/diagrams/ring_frames/ring_cargo_Y30_plate_PROVISIONAL.dxf, airframe/diagrams/ring_frames/ring_rear_Y290_inner.csv, airframe/diagrams/ring_frames/ring_rear_Y290_plate.dxf, airframe/freecad/assembly/SerenityAssembly.FCStd.bak2, airframe/freecad/assembly/leg_2_scaled-oriented.stl, airframe/fuselage-covers/TODO.md, airframe/fuselage-covers/WBS.md, airframe/fuselage-joints/TODO.md, airframe/fuselage-joints/WBS.md, airframe/fuselage-mid/TODO.md, airframe/fuselage-mid/WBS.md, airframe/gcode/davinci-jr-proto/.gitignore, airframe/gcode/davinci-jr-proto/davinci_jr_pla.ini, airframe/gcode/davinci-jr-proto/slice_all_batches.sh, airframe/gcode/davinci-jr-proto/xyz_wrap.py, airframe/landing-gear/HANDOFF.md, airframe/landing-gear/TODO.md, airframe/landing-gear/WBS.md, airframe/openscad/fuselage/access_panels_24in.scad, airframe/openscad/fuselage/battery_tray.scad, airframe/openscad/fuselage/belly_panel.scad, airframe/openscad/fuselage/bow_sensor_faceplate.scad, airframe/openscad/fuselage/bow_sensor_pod.scad, airframe/openscad/fuselage/canonical_leg_r6_1_5in.scad, airframe/openscad/fuselage/canonical_leg_r6_3_0in.scad, airframe/openscad/fuselage/cargo/_export_cargo_port_root_chunk.scad, airframe/openscad/fuselage/cargo/cargo_sect_shell24.scad, airframe/openscad/fuselage/cargo/cargo_spar_drive.scad, airframe/openscad/fuselage/cargo/cargo_vera_faraday.scad, airframe/openscad/fuselage/head_shell24.scad, airframe/openscad/fuselage/landing_leg_assy.scad, airframe/openscad/fuselage/middle_canonical_shell24.scad, airframe/openscad/fuselage/rcrs49_wire_post.scad, airframe/openscad/fuselage/rear_shell24.scad, airframe/openscad/fuselage/wire_brace_leg.scad, airframe/openscad/fuselage/wire_loop_fuse.scad, airframe/openscad/nacelles/_export_inboard_cut.scad, airframe/openscad/nacelles/_export_pivot_slab.scad, airframe/openscad/nacelles/edf_aft_spider_sleeve.scad, airframe/openscad/nacelles/edf_stator_sleeve.scad, airframe/openscad/nacelles/gear_option_compare.scad, airframe/openscad/nacelles/nacelle_nozzle_iris.scad, airframe/openscad/nacelles/nacelle_nozzle_pushrod.scad, airframe/openscad/nacelles/nacelle_pod_50mm_tandem.scad, airframe/openscad/nacelles/nacelle_servo_bracket.scad, airframe/openscad/port_tilt_spar_assembly.scad, airframe/openscad/wings/wings_s1223_revo.scad, airframe/openscad/wings/wings_s1223_revo.scad.backup-2026-07-18, airframe/placeholders/avionics/Cape_A2_PCB_55x35mm.stl, airframe/placeholders/avionics/Cape_B2_PCB_55x35mm.stl, airframe/placeholders/avionics/FlightEngineer_PDB_90x65mm.stl, airframe/placeholders/avionics/PocketBeagle2_Industrial_56x35mm.stl, airframe/placeholders/avionics/XCVR_49MHZ2_PCB_55x35mm.stl, airframe/placeholders/avionics/microSD_64GB.stl, airframe/placeholders/bearings/B6804_20x32x7mm_GCS.stl, airframe/placeholders/bearings/MF104ZZ_4x10x4mm.stl, airframe/placeholders/bearings/MR63ZZ_3x6x2p5mm.stl, airframe/placeholders/cargo/DRV8833_Hbridge_breakout.stl, airframe/placeholders/cargo/Dyneema_SK75_0p5mm_coil.stl, airframe/placeholders/cargo/HX711_loadcell_ADC_breakout.stl, airframe/placeholders/cargo/N20_motor_300RPM_6V.stl, airframe/placeholders/faraday/Far_EMI_vent_40x40x6mm.stl, airframe/placeholders/faraday/Far_FT_panel_55x35mm.stl, airframe/placeholders/faraday/Far_bond_strap_100mm.stl, airframe/placeholders/faraday/Far_cage_AV_70x50x82mm.stl, airframe/placeholders/faraday/Far_fan_40mm_5V.stl, airframe/placeholders/faraday/Far_ferrite_4mm_ID.stl, airframe/placeholders/faraday/Far_gasket_AV_250x6x1mm.stl, airframe/placeholders/faraday/Mal_far_fan_40mm_5V.stl, airframe/placeholders/faraday/Mal_far_gasket_470x8x1p5mm.stl, airframe/placeholders/foam/Foam_fill_cargo_190x200x159mm.stl, airframe/placeholders/foam/Foam_fill_head_125x231x136mm.stl, airframe/placeholders/foam/Foam_fill_middle_horseshoe_173x69x161mm.stl, airframe/placeholders/foam/Foam_fill_rear_136x177x154mm.stl, airframe/placeholders/foam/Void_avionics_bay_62x42x75mm.stl, airframe/placeholders/foam/Void_cargo_bay_120x150x80mm.stl, airframe/placeholders/foam/Void_far_cage_76x56x88mm.stl, airframe/placeholders/foam/Void_far_fan_spur_44x44x50mm.stl, airframe/placeholders/foam/Void_nacelle_pylon_20x80x20mm.stl, airframe/placeholders/foam/Void_power_bus_25x500x25mm.stl, airframe/placeholders/foam/Void_vent_exhaust_20x300x20mm.stl, airframe/placeholders/foam/Void_vent_intake_20x250x20mm.stl, airframe/placeholders/foam/Void_wiring_trunk_30x700x20mm.stl, airframe/placeholders/gcs/AS5600_encoder_breakout_15x15mm.stl, airframe/placeholders/gcs/Antenna_2p4GHz_zigbee_rubber_duck.stl, airframe/placeholders/gcs/Antenna_49MHz_whip_940mm.stl, airframe/placeholders/gcs/Antenna_5GHz_panel_14dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_Yagi_9dBi.stl, airframe/placeholders/gcs/Antenna_915MHz_omni_5dBi.stl, airframe/placeholders/gcs/Antenna_GNSS_patch_ANN_MB00.stl, airframe/placeholders/gcs/B6804_20x32x7mm_gimbal_pan.stl, airframe/placeholders/gcs/GPS_module_M10Q_SparkFun.stl, airframe/placeholders/gcs/N42_disc_magnet_6x2mm.stl, airframe/placeholders/gcs/Pololu_D24V22F6_6V2A_BEC.stl, airframe/placeholders/gcs/Pololu_D24V50F5_5V5A_BEC.stl, airframe/placeholders/gcs/RF_splitter_915MHz_2way_ZFSC.stl, airframe/placeholders/gcs/Skipper_enclosure_IP65_145x90x65mm.stl, airframe/placeholders/gcs/Skipper_tripod_antenna_mast.stl, airframe/placeholders/gcs/TCA9548A_I2C_mux_breakout.stl, airframe/placeholders/gears/Bevel_M1_14T_pair.stl, airframe/placeholders/gears/Bevel_housing_CF_PETG.stl, airframe/placeholders/gears/Pinion_M1_12T_R6mm.stl, airframe/placeholders/gears/Sector_gear_M1_R22mm.stl, airframe/placeholders/generate_placeholders.py, airframe/placeholders/hardware/Batt_strap_silicone_16mm_CAM.stl, airframe/placeholders/hardware/Insert_M25_brass_L5.stl, airframe/placeholders/hardware/Insert_M3_brass_L5.stl, airframe/placeholders/hardware/Pin_SS_3x5mm_hinge.stl, airframe/placeholders/hardware/Screw_M3x8mm_button_ISO7380.stl, airframe/placeholders/lighting/WS2812C_2020_SMD.stl, airframe/placeholders/power/Fuse_MAXI_150A.stl, airframe/placeholders/power/Fuse_mini_40A.stl, airframe/placeholders/power/LiPo_4S_10000mAh_175x64x38mm_GCS.stl, airframe/placeholders/power/LiPo_6S_2800mAh_115x35x35mm.stl, airframe/placeholders/power/LiPo_6S_4000mAh_138x44x36mm.stl, airframe/placeholders/power/Shunt_CSS2H_2512K_1mohm.stl, airframe/placeholders/propulsion/EDF_120mm_6S_deferred.stl, airframe/placeholders/propulsion/EDF_50mm_6S.stl, airframe/placeholders/propulsion/ESC_40A_6S_BLHeli32.stl, airframe/placeholders/propulsion/ESC_80A_6S_BLHeli32_deferred.stl, airframe/placeholders/servos/DS3218MG_25kgcm.stl, airframe/placeholders/servos/SG90_micro.stl, airframe/placeholders/structural/CF_bar_6x3mm_620mm_keel.stl, airframe/placeholders/structural/CF_plate_2mm_200x300mm.stl, airframe/placeholders/structural/CF_rod_3mm_300mm_stock.stl, airframe/placeholders/structural/CF_rod_4mm_300mm_stock.stl, airframe/placeholders/structural/CF_tube_12mm_OD_1p5w_350mm_spar.stl, airframe/placeholders/structural/PTFE_sleeve_4mm_OD_3mm_ID_52mm.stl, airframe/placeholders/wiring/PTFE_conduit_4mm_OD_3mm_ID_700mm.stl, airframe/placeholders/wiring/Post_49MHz_base_load_coil.stl, airframe/placeholders/wiring/Wire_10AWG_silicone_400mm.stl, airframe/placeholders/wiring/Wire_28AWG_STP_bundle_500mm.stl, airframe/placeholders/wiring/Wire_49MHz_antenna_0p3mm_470mm.stl, airframe/placeholders/wiring/Wire_4AWG_silicone_200mm_pair.stl, airframe/stls/fuselage/battery_tray.stl, airframe/stls/fuselage/belly_panel.stl, airframe/stls/fuselage/bow_sensor_faceplate.stl, airframe/stls/fuselage/cargo/cargo_cradle_autolatch.stl, airframe/stls/fuselage/cargo/cargo_door_port.stl, airframe/stls/fuselage/cargo/cargo_door_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_door_stbd.stl, airframe/stls/fuselage/cargo/cargo_drv8833_tray.stl, airframe/stls/fuselage/cargo/cargo_fpv_bezel.stl, airframe/stls/fuselage/cargo/cargo_gps_retention_ring.stl, airframe/stls/fuselage/cargo/cargo_hinge_retention.stl, airframe/stls/fuselage/cargo/cargo_release_servo_bracket.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_bossed.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl, airframe/stls/fuselage/cargo/cargo_sect_shell24_repaired.stl, airframe/stls/fuselage/cargo/cargo_vera_faraday.stl, airframe/stls/fuselage/cargo/cargo_winch_motor_mount.stl, airframe/stls/fuselage/cargo/cargo_winch_spool.stl, airframe/stls/fuselage/cargo/generate_cargo_doors.py, airframe/stls/fuselage/cargo/generate_cargo_hinge_retention.py, airframe/stls/fuselage/cargo/generate_cargo_mounts.py, airframe/stls/fuselage/cargo/starboard-view-of-cargo_sect_shell24_2mm_repaired.svg, airframe/stls/fuselage/cargo_middle_splice_collar.stl, airframe/stls/fuselage/dorsal_antenna_fin.stl, airframe/stls/fuselage/generate_conforming_collars.py, airframe/stls/fuselage/generate_wing_root_flange.py, airframe/stls/fuselage/head_cargo_splice_collar.stl, airframe/stls/fuselage/head_shell24.stl, airframe/stls/fuselage/head_shell24_2mm_repaired.stl, airframe/stls/fuselage/inara_access_cover.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_1_5in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_legs.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_hull_stance.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_assembled.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_3_0in_leg_frame.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_bay.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_ductile_wire_nominal.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_foot.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_deformed.stl, airframe/stls/fuselage/landing-gear/lg_r6_common_spring_wire_nominal.stl, airframe/stls/fuselage/middle_canonical_edf_intake.stl, airframe/stls/fuselage/middle_canonical_shell24.stl, airframe/stls/fuselage/middle_rear_splice_collar.stl, airframe/stls/fuselage/middle_shell24_2mm_repaired.stl, airframe/stls/fuselage/rear_shell24_2mm_repaired.stl, airframe/stls/fuselage/river_access_cover.stl, airframe/stls/fuselage/wing_root_flange_port.stl, airframe/stls/fuselage/wing_root_flange_stbd.stl, airframe/stls/nacelles/edf_aft_spider_sleeve.stl, airframe/stls/nacelles/edf_stator_sleeve.stl, airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl, airframe/stls/nacelles/eng_right_shell24_50mm_repaired.stl, airframe/stls/nacelles/nacelle_port_revs.stl, airframe/stls/nacelles/nacelle_servo_bracket.stl, airframe/stls/nacelles/nacelle_stbd_revs.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_flap_seal.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-closed.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_iris-open.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_ring.stl, airframe/stls/nacelles/nozzles/nacelle_nozzle_throat.stl, airframe/stls/wings/wing_port_s1223_revo.stl, airframe/stls/wings/wing_stbd_s1223_revo.stl, airframe/wings-nacelles/TODO.md, airframe/wings-nacelles/WBS.md --- @@ -436,6 +436,7 @@ bow_sensor_faceplate.stl — STL mesh (ASCII): OpenSCAD_Model [cad-mesh, sensors cargo_middle_splice_collar.stl — STL mesh (binary) [cad-mesh, structural] dorsal_antenna_fin.stl — STL mesh (binary) [cad-mesh, structural] generate_conforming_collars.py — Generate the three CONFORMING splice collars (hull frame, Rev R3). [structural] +generate_wing_root_flange.py — Generate the two bonded wing-root flanges (WA-R1b, Rev T1c). [structural] head_cargo_splice_collar.stl — STL mesh (binary) [cad-mesh, structural] head_shell24.stl — STL mesh (binary) [cad-mesh, structural] head_shell24_2mm_repaired.stl — STL mesh — header: SerenityUAV HULL-FRAME R1 Head_Shell structural-features 2026-06-14 [cad-mesh, structural] @@ -446,6 +447,8 @@ middle_rear_splice_collar.stl — STL mesh (binary) [cad-mesh, structural] middle_shell24_2mm_repaired.stl — STL mesh — header: SerenityUAV HULL-FRAME R1 Middle_Shell structural-features 2026-06-14 [cad-mesh, structural] rear_shell24_2mm_repaired.stl — STL mesh — header: SerenityUAV HULL-FRAME R1 Rear_Shell interior-regen 2026-07-06 [cad-mesh, structural] river_access_cover.stl — STL mesh (ASCII): OpenSCAD_Model [cad-mesh, structural] +wing_root_flange_port.stl — STL mesh (binary) [cad-mesh, structural] +wing_root_flange_stbd.stl — STL mesh (binary) [cad-mesh, structural] ``` ## airframe/stls/fuselage/cargo/ @@ -1185,7 +1188,7 @@ WBS.md — Serenity UAV — Avionics Rev S1 Redesign (Commo / XO / Flight Engine ```text AGENTS.md — Current Specification — Agent Instructions [documentation, specification] -bom_revS.csv — Bill of materials, CSV (171 items) [bom, specification] +bom_revS.csv — Bill of materials, CSV (176 items) [bom, specification] bom_revS.json — Bill of materials, JSON (168 items) [bom, specification] LICENSE — SERENITY UAV — SPECIFICATION & BOM LICENSING [licensing, specification] LICENSE_AND_ATTRIBUTION.md — Serenity-Class Tiltrotor UAV — License & Attribution [documentation, licensing, security, specification] @@ -1263,6 +1266,7 @@ flight_envelope.md — Serenity UAV — Flight Envelope Document [documentation] LANDING_GEAR_ANALYSIS.md — Serenity UAV — Landing Gear Structural Analysis [documentation, landing-gear] LICENSE — Serenity UAV — docs — License [documentation, licensing] MANIFEST.json — JSON data (12 top-level keys) [documentation] +MASS_AUDIT_CARGO_WING_ROOT.md — Mass Audit — Cargo Section and Wing Roots (Rev T1c) [documentation] NOZZLE_DRIVE_TRADE.md — Nozzle-Drive Mechanism Trade Study (Rev T, 2026-07-19) [documentation, propulsion] OBSERVER_LASER_ANALYSIS.md — Observer Laser Indicator — Single-Source Feasibility & Spread-Angle Analysis [documentation, security, sensors-vision] OBSERVER_MANUFACTURING_READINESS.md — Observer — Manufacturing-Readiness Gap Analysis [documentation, emi-hardening, security] @@ -1275,6 +1279,7 @@ README.md — Serenity UAV — Documentation Index [documentation, emi-hardening REVN_BUILD_GUIDE_24IN.md — Serenity UAV — 24-Inch Build Guide (Rev S Baseline) [documentation, emi-hardening, redundancy-failover] structural_analysis.md — Serenity UAV — Fuselage Structural Analysis [documentation] Tilt-Rotor 10AWG Wire Clearance Analysis.md — **Tilt-Rotor 10AWG Wire Clearance Analysis** [documentation, emi-hardening] +TILT_DRIVE_CONTROL_SPEC.md — Nacelle Tilt Drive — Control References and Sensors (Rev T1) [documentation, emi-hardening, security] TILT_ENCODER_WIRING_EMI_SPEC.md — Nacelle Tilt-Angle Feedback (AK7455) — EMI Wiring Specification [documentation, emi-hardening] TILT_SPAR_ANALYSIS.md — Rotating Tilt-Spar Analysis — 8 mm Through-Spar Option [documentation, emi-hardening] TODO.md — Serenity UAV — Documentation, Standards & Regulatory TODO (Open Work Only) [documentation, project-tracking] @@ -1282,6 +1287,7 @@ TODO_1_1_0_COMPLETION_SUMMARY.md — TODO.md §1.1.0 — Hull-Frame Coordinate S VERIFY_PLACEMENT_WORKFLOW.md — VERIFY Parts Placement — Workflow & Action Plan [documentation, security] WBS.md — Serenity UAV — Documentation, Standards & Regulatory Work Breakdown Structure [documentation, project-tracking, security] WBS_FEDERATION.md — WBS.md / TODO.md Federation Inventory (Rev S2) [documentation, emi-hardening, project-tracking] +WING_ATTACH_INTERFACE.md — Wing Attachment Interface Specification — Rev T1 [documentation] ``` ## docs/img/ @@ -1312,6 +1318,7 @@ wing_rev_s1c_sections.png — Rendered image / reference photo [documentation] 2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md — feat: Unified 20 mm fixed CF spar, trunnion pivot, and belt tilt drive [documentation, emi-hardening] 2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md — feat: Nacelle trunnion pivot on the fixed hollow spar + tilt-drive trade study [documentation, propulsion] 2026-08-29-005-nacelle-mould-line-conformance-plan.md — Nacelle Mould-Line Conformance and Nozzle Shortening - Plan [documentation, propulsion] +2026-08-30-001-weight-reduction-targets-plan.md — chore: Ranked weight-reduction targets [documentation] ``` ## docs/references/ @@ -1779,6 +1786,7 @@ precommit_kicad_load.py — precommit_kicad_load.py — block a commit if any st precommit_sanitize.py — Pre-commit sanitization and delinting hook for Serenity-UAV. [build-tooling] purge_stale_fcstd_objects.py — Remove superseded geometry from the archived FreeCAD assembly (LG-10.7). [build-tooling, cad-mesh] README.md — Serenity UAV — Build Tools & Validation Scripts [build-tooling, documentation] +spar_bundle_fit.py — Size a hollow spar bore against the conductor bundle it has to swallow. [build-tooling] TODO.md — Serenity UAV — Build Tools & Automation Work Breakdown Structure [build-tooling, documentation, project-tracking] TOOL_REFERENCE.md — Build Tool Reference — Usage, Output Locations, and Pre-Commit Checklists [build-tooling, documentation, emi-hardening, redundancy-failover, security, specification] validate_kicad.py — validate_kicad.py — KiCad schematic/PCB validator for CI. [build-tooling, emi-hardening, pcb-design] @@ -1788,9 +1796,9 @@ wing_airfoil_integrity.py — Fail-closed check that the wing's tabulated airfoi wing_airfoil_variants.py — Generate and compare S1223 thickening strategies for the Rev S1b spar move. [build-tooling] wing_cfd_openfoam.py — 2D RANS comparison of S1223 thickening strategies, via OpenFOAM. **WIP.** [build-tooling] wing_internal_clearance.py — Check every internal bore in the wing against its neighbours and the skin. [build-tooling, security] -wing_root_deconflict.py — Deconflict everything crowded into the wing-root zone of the cargo bulkhead. [build-tooling, security] +wing_root_deconflict.py — Python script [build-tooling] wing_section_figure.py — Draw the wing's root and tip sections with every internal bore, to scale. [build-tooling] -wing_spar_carrythrough.py — Does the rotating wing spar have to span the fuselage? First-principles check. [build-tooling, security] +wing_spar_carrythrough.py — Python script [build-tooling] wing_spar_station_fit.py — Size the wing spar's chordwise station against the S1223 section it lives in. [build-tooling] ``` @@ -1826,6 +1834,6 @@ REFERENCES.md — REFERENCES.md — Serenity UAV Standards and Regulatory Refere REPO_ENFORCEMENT.md — Repository Enforcement Checklist [documentation] requirements-dev.txt — Text file SECURITY.md — Security Policy [documentation, security] -TODO.md — Serenity UAV — TODO (Open Work Only) [documentation, project-tracking, security] +TODO.md — Serenity UAV — TODO (Open Work Only) [documentation, emi-hardening, project-tracking, security] WBS.md — Serenity UAV — Work Breakdown Structure (Master Index) [documentation, emi-hardening, project-tracking] ``` diff --git a/REFERENCES.md b/REFERENCES.md index 7c0963e..2a5974e 100644 --- a/REFERENCES.md +++ b/REFERENCES.md @@ -293,6 +293,65 @@ aft white); controlled by FC4 node (Simon's medbay, Bay D). --- +### REF-FAA-004: 14 CFR Part 23 — Airworthiness Standards: Normal Category Airplanes + +| Field | Value | +|---|---| +| **Issuing authority** | U.S. Federal Aviation Administration (FAA), Department of Transportation | +| **Current edition** | Part 23 as restructured by Amdt. 23-64 (effective 2017-08-30), performance-based; as amended through the 2024 annual CFR edition | +| **Official URL** | | +| **Verified source** | Section text quoted below was retrieved and verified verbatim 2026-08-29 from GovInfo's authoritative CFR XML — and . (eCFR's HTML endpoint bot-blocks automated retrieval; GovInfo serves the same authority.) | +| **Applicability caveat** | Part 23 governs **manned** normal-category airplanes. Serenity is an sUAS operated under Part 107 [REF-FAA-002], which imposes **no** structural certification basis. Part 23 is cited here as an **adopted engineering baseline**, exactly as `docs/structural_analysis.md` §3 already frames it — **NOT as a compliance claim.** | + +**Sections applied in this project:** + +| Section | Title | Applied where | +|---|---|---| +| **§ 23.2230** | *Limit and ultimate loads* | The 1.5× ultimate/limit factor used throughout `docs/structural_analysis.md` §3 and `tools/wing_spar_carrythrough.py` | +| **§ 23.2265** | *Special factors of safety* | Justifies an additional factor on FDM-printed structure — see below | + +**§ 23.2230 (verbatim, GovInfo 2024 CFR):** the applicant must determine — +(a) *"The limit loads, which are equal to the structural design loads unless +otherwise specified elsewhere in this part"*; and (b) *"The ultimate loads, +which are equal to the limit loads multiplied by a 1.5 factor of safety unless +otherwise specified elsewhere in this part."* + +**§ 23.2265 (verbatim, GovInfo 2024 CFR):** (a) *"The applicant must determine a +special factor of safety for each critical design value for each part, article, +or assembly for which that critical design value is uncertain, and for each +part, article, or assembly that is —"* … (a)(2) *"Subject to appreciable +variability because of uncertainties in manufacturing processes or inspection +methods."* (c) *"The applicant must multiply the highest pertinent special +factor of safety in the design for each part of the structure by each limit and +ultimate load…"* + +**Why §23.2265 matters here.** Serenity's primary structure is FDM-printed +CF-PETG. Layer adhesion, raster orientation, moisture uptake, and +machine-to-machine variation are precisely the *"appreciable variability because +of uncertainties in manufacturing processes"* that (a)(2) contemplates. This is +the regulatory hook for the FOS 4.0 joint target that `docs/structural_analysis.md` +§3 currently describes only as *"a design-team judgment value"* — the judgment +is sound and now has a citable basis, though the specific numeric value remains +the project's own choice rather than anything Part 23 prescribes. + +**CORRECTION THIS ENTRY MAKES — stale section number.** +`docs/structural_analysis.md` cited **"14 CFR Part 23.303"** in two places. That +section belonged to the **pre-2017** Part 23 and does not exist in the current +rule; the 2017 restructure moved the limit/ultimate relationship to **§23.2230**. +This is the same class of defect this file already recorded for §23.1401 (see +"Removed / Superseded Citations"). Both occurrences corrected 2026-08-29. + +**Also worth recording, to prevent a future fabrication:** the load factors +**+3.8 / −1.52**, widely quoted as "the Part 23 load factors," appear **nowhere +in the current rule** — they belong to the pre-2017 §23.337. Current §23.2200(b) +requires only *"design maneuvering load factors not less than those, which +service history shows, may occur within the structural design envelope."* This +project's 4 g limit factor is an adopted figure under that framing, not a +regulatory quotation. Do not cite +3.8/−1.52 to current Part 23. + +**Used in:** `docs/structural_analysis.md` §2, §3; +`docs/WING_ATTACH_INTERFACE.md` §2.1; `tools/wing_spar_carrythrough.py` + ## Part II — United States Federal Communications Commission Regulations > "Can't stop the signal." — Mr. Universe. We can, however, stay inside Part 15/95 limits while we transmit it. @@ -1185,6 +1244,47 @@ FEA cross-check (ANSYS Workbench, linear-elastic model, Table 11): simulated fle **Used in:** `airframe/README.md` §5.1 +### REF-MATH-001: Melissen, J.B.M. — "Packing and Covering with Circles" + +| Field | Value | +|---|---| +| **Author** | Johannes Bernardus Marinus Melissen, Utrecht University | +| **Publication** | PhD thesis, Universiteit Utrecht, 1997 | +| **Official URL** | — Utrecht University Repository, open access | +| **Retrieved** | 2026-08-29 | +| **Scope applied** | The proven-optimal ratios for packing *n* equal circles inside the smallest enclosing circle, for *n* ≤ 7. Only these small-*n* cases are used; they are proven optimal in this source and are reproduced throughout the standard circle-packing literature. | + +**Values used (enclosing diameter ÷ single-circle diameter):** + +| *n* | Ratio K(*n*) | Closed form | +|---|---|---| +| 1 | 1.00000 | 1 | +| 2 | 2.00000 | 2 | +| 3 | 2.15470 | 1 + 2/√3 | +| 4 | **2.41421** | **1 + √2** | +| 5 | 2.70130 | 1 + √(2(1 + 1/√5)) | +| 6 | 3.00000 | 3 | +| 7 | 3.00000 | 3 (hexagonal + centre) | + +**Why this is catalogued rather than treated as common knowledge:** the *n* = 4 +case decided a load-bearing geometry change. Two independent sources — the +owner's `docs/plans/2026-08-27-nacelle-wiring-plan.md` and the external Gemini +conversation it derived from — both specified an 11.0 mm spar bore for four +10 AWG conductors. Four Ø5.5 mm circles circumscribe **13.28 mm** at zero +clearance, so 11.0 mm does not fit at all, and the error propagated because the +constant was carried in prose rather than computed. It is now computed, from +this table, in `tools/spar_bundle_fit.py`, which refuses to interpolate for any +*n* outside the tabulated set. + +**Caveat on the input, not the ratio:** the ratio is exact; the 5.5 mm wire OD +it is applied to is **not** a verified figure. `current-specification/bom_revS.csv` +records no outside diameter for `WIRE-10AWG`. See `tools/spar_bundle_fit.py`, +which prints that caveat on every run. + +**Used in:** `tools/spar_bundle_fit.py`; +`airframe/openscad/wings/wings_s1223_revo.scad` (`SPAR_BORE_OD` derivation); +`docs/WING_ATTACH_INTERFACE.md` §2 + ## Part XI — FDA / CDRH Laser Product Regulations ### REF-FDA-001: 21 CFR Part 1040 — Performance Standards for Light-Emitting Products @@ -1706,10 +1806,20 @@ Per-application operating mode differs even though the hardware is now identical | Application | Qty | Mode | Range | |---|---|---|---| | Cargo winch | 1 | Continuous rotation, gateway closes position on the AK7455 spool encoder (REF-SENSOR-008) | Multi-turn, unbounded by the servo itself | -| Nacelle tilt | 2 | Position, firmware soft-limited | −5°…140°, backstopped by the existing CF-PETG hard-stop blocks in the external gear train (`docs/NOZZLE_DRIVE_TRADE.md`) — mechanically independent of the servo's own (now-removed) rotation pin | - -Removing the pin on all three is a deliberate commonality choice, not a requirement of the -position-mode applications: LibreServo replaces the servo's potentiometer with a 360° +| Nacelle tilt | 2 | **CORRECTED Rev T1c (2026-08-30):** continuous rotation, Pilot closes position on the AK7455 nacelle encoder (REF-SENSOR-008) — the same pattern as the winch above | **Multi-turn** (1.438 rev of actuator per 145° of nacelle), unbounded by the servo itself. Soft-limited to −5°…140° *at the nacelle*, backstopped by CF-PETG hard stops in the external gear train | + +> **The row above changed at Rev T1c and the change is structural, not editorial.** +> The tilt stage became a REDUCTION (tip 14T/50T, `i` = 3.571), so the drive shaft must +> turn **1.438 revolutions** over the 145° sweep and no limited-rotation servo can drive +> it at any horn radius. Removing the rotation pin is therefore **required** on the tilt +> servos now, not merely convenient — and the AK7455 became **load-bearing for control** +> rather than telemetry, because a multi-turn drive without absolute feedback does not +> know where the nacelle is. See `docs/WING_ATTACH_INTERFACE.md` §4.3b/§4.3c and +> `docs/TILT_DRIVE_CONTROL_SPEC.md`. + +Removing the pin on the winch is a deliberate commonality choice; on the tilt servos it +is now a requirement (see the note above). It is not a requirement of the +position-mode applications generally: LibreServo replaces the servo's potentiometer with a 360° absolute magnetic encoder (AEAT-8800, 16-bit), so position feedback and soft-limit enforcement no longer depend on the mechanical stop the pin used to provide. See REF-SENSOR-013/014/015 below for the individual part records, and diff --git a/TODO.md b/TODO.md index ff1c2c2..aeaeff3 100644 --- a/TODO.md +++ b/TODO.md @@ -35,6 +35,104 @@ - [ ] Verify AK7455 off-axis geometry + pinout vs datasheet (REF-SENSOR-*) - [ ] ASTM D3039/D695 coupon test: CF-PLATE-2MM bending allowable (thwarts built at FOS 8.5/8.7 vs the conservative 300 MPa stand-in only) +- [ ] **ASTM D3039/D695 certificate for the 20 x 16.3 CF SPAR TUBE.** New at + Rev T1 and now load-bearing: the spar is the wing's primary bending + member at FOS 9.0 against the same unverified 300 MPa stand-in + (`docs/TILT_SPAR_ANALYSIS.md` §3.6.3). The 4130 rows above no longer + govern the flight load path. +- [ ] **Measure the procured 10 AWG silicone wire OD.** `bom_revS.csv` records + none; the 5.5 mm figure the Ø20.4 spar bore is derived from is an + ASSUMPTION. A larger real OD re-opens the airfoil trade + (`tools/spar_bundle_fit.py` prints this caveat on every run). +- [x] **Tilt servo angular range (180 vs 270 deg) — CLOSED 2026-08-29, VOID.** + Owner direction: the drive turns the shaft MORE THAN ONE REVOLUTION, so + the stage is a reduction and the actuator's own travel no longer sets the + ratio. Built: module 0.8, 14T pinion / 50T ring, i 3.571, shaft 1.389 rev + per 140 deg, C 25.6 -> station 53.6. +- [ ] **ACTUATOR RE-SELECT (replaces the item above).** A multi-turn output means + the tilt drive is no longer a limited-rotation servo but a + continuous-rotation gearmotor or stepper, closed on the AK7455's absolute + nacelle angle. The DS3225 is ~17x oversized on torque AND now the wrong + kind of device. NOTE this makes the encoder load-bearing for CONTROL, not + telemetry: a multi-turn drive without absolute feedback does not know + where the nacelle is. +- [ ] **LG-11 coupon — DEMOTED at the wing root, still a gate elsewhere.** + Owner ruled the cargo bay clear, so the root joint was re-designed around + it: socket for shear + bonded 80x60 flange for the moment, FOS 29.2 at the + STANDING 5 MPa figure (29.2 / 87.6 / 274.6 at 5 / 15 / 47 MPa). The coupon + no longer decides whether that joint is buildable, only how small the + flange could shrink. It remains a gate for the tenon path and the thwarts. +- [ ] **Aero revalidation of the re-lofted wing (CFD or bench).** The section + is no longer S1223 -- root t/c 12.14 -> 17.72 %, tip 18.93 -> 26.70 %. + Every aero figure in this repo citing this wing is unverified until this + lands, including the 7.6 N cruise-lift figure and everything derived + from it. The built sections are designated S1223/t17.7 (root) and + S1223/t26.7 (tip) -- S1223's CAMBER LINE with the thickness envelope + scaled x1.46 / x2.20; camber is unscaled and exact (8.67% at 49.0% + chord), so zero-lift angle and lift-curve slope partially survive at the + ROOT on thin-airfoil grounds and not at all at the tip. CL_max, L/D and + the cruise-lift figure do NOT survive. Needs XFOIL or a + TRANSITION-SENSITIVE RANS run at Re 1.3-1.8e5 -- a fully-turbulent model + will misrepresent the separation bubble. `tools/wing_cfd_openfoam.py` is + still blocked on mesh generation. See docs/flight_envelope.md's banner. + +#### 0.8.1 — Wing attach interface (Rev T1), open requirements + +→ detail: `docs/WING_ATTACH_INTERFACE.md` §5; fuselage detail: +`airframe/fuselage-mid/WBS.md` §1.1.1.5 + +Wing side is BUILT. These are the two joints it publishes. + +- [x] WA-R1/R1b, R2, R4, R6 — fuselage side BUILT 2026-08-30 (Rev T1c). Socket + Y +21.00 / Z +66.85 / D20.4 / 18.5 mm; F688ZZ and both tie rods deleted; + mortise 30.8 -> 12.8 (tenon fits +0.40 mm/side); nav D4.2, AK7455 D7.5 and + shaft D4.4 conduits cut. `wing_root_deconflict.py` now CLEAR. +- [x] WA-R15 — tilt actuator: DS3225 + LibreServo_v4 run MULTI-TURN, closed on + the AK7455; fuselage spur stage m0.8 38T/38T, C 30.40, 1:1. Coaxial does + not fit and no gear clears the tenon in Y -> 18 mm actuator standoff. +- [ ] WA-R3/R17 — split-collar pinch clamp is still not a part (no SCAD, no STL, + no BOM row). **BLOCKS wing removal/refit.** +- [ ] WA-R16 — the tilt train is NOT self-locking and has no holding provision. + **BLOCKS flight release.** `docs/TILT_DRIVE_CONTROL_SPEC.md` §5.2/§7.3. +- [ ] WA-R18 — Rev T1c is +102.8 g (+2.63 % AUW, revised by the weight audit); + hover T/W was ~1.19 vs a 1.2 minimum. Re-derive mass/CG/T-W. + **BLOCKS flight release.** +- [ ] MA-1 — BOM printed-part masses understate by +521.6 g (13.3 % of AUW) + across 23 rows measured against their STLs. Reconcile + add a CI check. + **BLOCKS any weight statement.** -> `docs/MASS_AUDIT_CARGO_WING_ROOT.md` +- [ ] MA-5 — hollow the actuator standoffs, -32.9 g, no structural question + (deflection 1.4e-5 mm at the gear mesh against a 0.05 mm budget). +- [ ] MA-6 — `PRINT-BATT-TRAY` measures 140.2 g against a 22 g BOM row. +- [ ] MA-7 — BOM mass column mixes installed mass, stock and GCS; add an + `Installed` flag before any weight statement uses it. +- [ ] W1..W8 — ranked weight-reduction targets: battery -225 g, tray floor + -23.1 g, actuator standoffs -32.9 g, cradle ~-25 g; wing root CLOSED. + W2 (BOM reconciliation) gates all of them. + -> `docs/plans/2026-08-30-001-weight-reduction-targets-plan.md` +- [ ] WA-R15a — re-measure the cargo-bay roof band: the actuators now reach + X -158.5 / -221.5 at Z +85.99..+112.99. +- [ ] TILT-CTL-01..06 — tilt control loop open items (plant model, slew rate, + differential-tilt trip, LibreServo_v4 encoder part). + → `docs/TILT_DRIVE_CONTROL_SPEC.md` §8. +- [ ] WA-R7..R12 — nacelle: trunnion bearing bore D20.0 at ring plane X ~ 28 + (axial AND radial duty), ring gear PD 33.8 at C 26.0, ring magnet + ID 27 / OD 41 axially separated from that gear, the 4 x 10 AWG + disconnect relocated into the nacelle annulus, the nav 3-core crossing + at the trunnion, and confirmation of the 32 mm spar-stub protrusion. +- [ ] WA-R13 — avionics: `docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §6.1's + ferromagnetic-spar premise is corrected in place; confirm the firmware + zero-calibration procedure still covers the drive-shaft/pinion field + that replaces it. +- [x] **OWNER DECISION — cargo-bay intrusion: CLOSED 2026-08-29.** The bay stays + clear; nothing goes inboard of hull X ~ -86 against a bay edge at -100. + The constraint produced a better joint (moment FOS 4.02 -> 29.2), not a + compromise. See `airframe/fuselage-mid/WBS.md` §1.1.1.5. +- [ ] **Spar stub / trunnion packaging — NEW, tightest constraint on the joint.** + The spar must terminate >= 26 mm from the nacelle duct axis, so the stub is + 15.0 mm (max 15.7). The trunnion bearing PAIR, the 50T ring gear and the + ID26/OD41.2 ring magnet must all fit inside that 15 mm, and the magnet and + gear are nearly coradial (r 13.0-20.6 vs 20.0) so they need axial + separation. 2x 6804 (20x32x7) = 14.0 mm fits; MF128ZZ retired to QTY 0. ### 0.10 Update and correct documentation touching every non-archived file. diff --git a/airframe/blender-scripts/merge_cargo_interior.py b/airframe/blender-scripts/merge_cargo_interior.py index c56e4d1..5dc9ad7 100644 --- a/airframe/blender-scripts/merge_cargo_interior.py +++ b/airframe/blender-scripts/merge_cargo_interior.py @@ -316,14 +316,19 @@ def extract_envelope(shell_tm): WING_LE_ROOT_Y = -7.0 WING_ROOT_CHORD = 129.0 -# REV S1b (2026-08-16): spar station 30 % -> 35 % of root chord, by owner -# decision. This is the fuselage half of the wings SS1.1.2 spar-interface -# blocker: the wing sat at the 22.0 mm station (hull Y +15) and this shell cut -# at 38.7 mm (Y +31.7), so the spar could not pass through both parts. Both -# are now on 45.15 mm. Keep in step with SPAR_BORE_STATION in +# REV T1 (2026-08-29): spar station 45.15 -> 28.00 mm aft of LE, and the spar +# itself changes KIND. Through Rev S1b it was an 8 mm rotating drive shaft that +# the wing rode on; under Rev T1 it is a FIXED 20 x 16.3 mm CF tube bonded +# through the wing over its full span, and it is the wing's primary bending +# member (docs/WING_ATTACH_INTERFACE.md SS1). The station is set by the wing, +# which moved to 28.00 mm to keep the O20.4 bore inside a skinnable section +# (wings_s1223_revo.scad SPAR_BORE_STATION); this file follows it. +# +# Keep WING_SPAR_STATION in step with SPAR_BORE_STATION in # airframe/openscad/wings/wings_s1223_revo.scad -- they are the same physical -# rod and there is no other link between the two files. -WING_SPAR_Y = WING_LE_ROOT_Y + 0.35 * WING_ROOT_CHORD # = +38.15 +# tube and there is no other link between the two files. +WING_SPAR_STATION = 28.00 # [mm] aft of the LE +WING_SPAR_Y = WING_LE_ROOT_Y + WING_SPAR_STATION # = +21.00 WING_MORT_Y = WING_LE_ROOT_Y + 0.50 * WING_ROOT_CHORD # = +57.5 # Mortise / nacelle-servo reference height. NOT the spar height -- see below. @@ -360,175 +365,136 @@ def extract_envelope(shell_tm): # pre-U1 S1223 table; now derived live from the SCAD's own table via # _wing_midline_mm() so it tracks the corrected airfoil automatically -- see # the note above _wing_midline_mm's definition. -WING_SPAR_MIDLINE = _wing_midline_mm(45.15 / WING_ROOT_CHORD) # ~= 10.72 (post-U1) -WING_SPAR_Z = WING_CHORD_LINE_Z + WING_SPAR_MIDLINE # = 68.42 (pre-U1) / re-derived post-U1 - -# CARGO-01/CARGO-02 (2026-08-24): the wing's single spar is now an 8 mm OD -# rotating AISI 4130 tube per side (wings_s1223_revo.scad TILT_SPAR_OD = 8.0, -# TILT_SPAR_BORE_CLEAR = 8.3), NOT the retired 12 mm fixed CF tube this file -# used to bore for. Per SPAR-01 (airframe/wings-nacelles/WBS.md §1.1.2, -# owner 2026-08-23) each spar terminates at the fuselage wall on its own -# bearing -- it no longer crosses the bay -- so this is a SEAT for that -# bearing, not a full-lateral-span clearance bore. "Root bearing stays -# F688ZZ" (WBS.md, Rev R2d 2026-07-19 wingtip-downsize note): dimensions -# from REF-SENSOR-019 (SMB Bearings F688ZZ datasheet, fetched 2026-08-24). -WING_SPAR_BORE_D = 8.3 # mm, rotating-spar clearance = 8.0 mm OD + 0.15 mm/side - # (matches wings_s1223_revo.scad TILT_SPAR_BORE_CLEAR - # exactly -- same physical shaft, same clearance law) - -# F688ZZ (REF-SENSOR-019): bore 8 / OD 16 / width 5 mm, flange OD 18 / flange -# width 1.1 mm. Seat interference (0.025 mm/side -> seat bore = OD - 0.05 mm) -# mirrors wings_s1223_revo.scad's own TIP_BRG_SEAT_D convention for the -# wingtip MF128ZZ bearing, so both bearing seats in this airframe are sized -# by the same rule. -ROOT_BRG_OD = 16.0 # mm, F688ZZ outer diameter (REF-SENSOR-019) -ROOT_BRG_SEAT_D = 15.95 # mm, press-fit seat bore, 0.025 mm/side interference -ROOT_BRG_W = 5.0 # mm, bearing width = seat depth -ROOT_BRG_FLANGE_OD = 18.0 # mm, flange OD -> shallow counterbore at the boss face -ROOT_BRG_FLANGE_T = 1.1 # mm, flange counterbore depth (= flange width) - -# Boss OD re-derived from the F688ZZ FLANGE OD (18.0 mm, the largest feature -# the boss must enclose) using the SAME 4.85 mm radial wall margin the Rev S1 -# boss originally carried around its old 12.3 mm bore ((22.0-12.3)/2 = 4.85): -# 18.0 + 2*4.85 = 27.7 mm. Explicitly NOT the retired Ø22 press-fit figure -- -# that sized a 12.3 mm bore with no bearing at all, and 22 mm does not clear -# an 18 mm flange with any wall left (22-18)/2 = 2.0 mm, under this boss's own -# established 4.85 mm margin. -WING_SPAR_BOSS_OD = 27.7 +# WA-R1: the spar rides the UNSCALED camber midline. s1223_section() opens the +# thickness envelope ABOUT the camber line, so THICKNESS_SCALE does not move the +# bore centre -- applying it here would lift the socket 4.07 mm above the spar +# it is supposed to receive (docs/WING_ATTACH_INTERFACE.md SS3.2). +WING_SPAR_MIDLINE = _wing_midline_mm(WING_SPAR_STATION / WING_ROOT_CHORD) # = 8.841 +WING_SPAR_Z = WING_CHORD_LINE_Z + WING_SPAR_MIDLINE # = +66.851 + +# WA-R1/WA-R2 (Rev T1, 2026-08-29): the socket is a BONDED/CLAMPED SEAT for a +# FIXED O20 CF tube, not a bearing seat for a rotating shaft. The F688ZZ root +# bearing (ROOT_BRG_*) is DELETED, and deleting it is not a tidy-up: a bearing +# here would let the fixed spar spin under the tilt pinion's gear reaction, +# which is the one thing this joint must not allow +# (docs/WING_ATTACH_INTERFACE.md SS4.3b, WA-R2). +# +# The joint SPLITS BY LOAD TYPE (SS3.3), because the cargo bay caps socket depth +# at 18.67 mm and a socket's moment capacity goes as 1/L^2: +# * SHEAR -> this socket. sigma = 115.1 N / (20 x 18.5) = 0.31 MPa, FOS 16. +# * MOMENT -> the bonded root flange below (ROOT_FLANGE_*), FOS 29.2. +# Bore = 20.0 mm OD + 0.2 mm/side epoxy gap, matching the wing's own +# SPAR_BORE_OD / TILT_SPAR_BORE_CLEAR exactly -- same tube, same bond-gap law. +WING_SPAR_BORE_D = 20.4 # mm, bonded-socket bore = 20.0 OD + 0.2 mm/side + +# Socket reach inboard of the wall. BOUNDED, not chosen: the cargo bay's clear +# span begins at hull X -100 and the wall skin sits at X -81.33, so 18.67 mm is +# all there is; 18.5 is built (SS3.3). The PORT_INB/STBD_INB wall brackets +# below already land at -100/-240, i.e. exactly this reach. +SPAR_SOCKET_REACH = 18.5 # mm, spanwise, inboard of the wall -- SHEAR only + +# The four 10 AWG ESC feeds enter the spar's own hollow bore at the socket and +# must keep running inboard past the socket's end into the bay (SS2.3: four +# O5.5 conductors circumscribe 13.28 mm, the exact 1+sqrt(2) four-circle packing +# ratio, REF-MATH-001). Continue the bore at the spar ID so the corridor the +# wires actually occupy is the corridor that is cut. +SPAR_WIRE_BORE_D = 16.3 # mm, = spar ID; carries the 13.28 mm bundle + +# WA-R1b: bonded root flange on the INNER face of the sidewall, concentric with +# the socket -- the MOMENT path. Triangular bearing pressure over height h with +# arm 2h/3 gives F = 3M/(2h); at h = 80 mm against the 14.60 N.m ultimate root +# moment that is 274 N over 1,600 mm^2 = 0.17 MPa, FOS 29.2 on the 5 MPa +# bond-limited CF-PETG figure (docs/structural_analysis.md SS7.3). It reacts +# over wall AREA, so it needs NO inboard reach -- it protrudes only its own +# thickness, to X ~ -86, against a bay edge at -100. That is what closes WA-R5. +ROOT_FLANGE_H = 80.0 # mm, hull Z extent +ROOT_FLANGE_W = 60.0 # mm, hull Y extent +ROOT_FLANGE_T = 5.0 # mm, protrusion inboard of the wall inner face + +# Sidewall skin stations at the wing-root joint, measured 2026-08-29 against the +# published shell (docs/WING_ATTACH_INTERFACE.md SS3.3). Every Rev T1 wing-root +# feature that has to reference "the wall" rather than a deep-embed bracket uses +# these, so one measured fact drives all of them. +WALL_SKIN_X_PORT = -81.33 +WALL_SKIN_X_STBD = -258.37 # = 2 * X_CL - WALL_SKIN_X_PORT, X_CL = -169.85 +WALL_INNER_X_PORT = WALL_SKIN_X_PORT - ROOT_FLANGE_T # = -86.33 +WALL_INNER_X_STBD = WALL_SKIN_X_STBD + ROOT_FLANGE_T # = -253.37 + +# Boss OD carries the SAME 4.85 mm radial wall margin every wing-root boss in +# this file uses ((22.0-12.3)/2 at Rev S1), now over the O20.4 socket bore: +# 20.4 + 2*4.85 = 30.1 mm. That also lands the boss on the ~O30 outside +# diameter WA-R3 specifies for the split-collar pinch clamp that grips the spar +# at this station, so the printed boss and the clamp share one envelope. +WING_SPAR_BOSS_OD = 30.1 # --------------------------------------------------------------------------- -# Wing root tie-rod couple (U5/KTD1, 2026-08-24) — two bonded CF rods that -# react the wing-root moment as a couple, replacing the tenon's former -# structural role (airframe/fuselage-mid/WBS.md §1.1.1.2 CARGO-03c). Mirrors -# the spar bearing boss's own embed pattern (PORT_INB/PORT_OUTB) exactly, at -# each rod's own chordwise station. Stations/diameters/embeds MUST be kept in -# step with wings_s1223_revo.scad's ROD_FWD_*/ROD_AFT_* -- same physical rods, -# no other link between the two files (same convention as WING_SPAR_Y above). -# -# Y/Z stations use the same midline-tracking derivation as WING_SPAR_Y/Z: -# Y = WING_LE_ROOT_Y + station (mm from LE); Z = WING_CHORD_LINE_Z + the -# S1223 camber-midline height at that station, at the ROOT chord (both rods -# are ROOT-ONLY -- see the wing SCAD's ROD_FWD_*/ROD_AFT_* comment for why -# full-span is not needed for a root-reacting tie rod). Midline heights -# below are freshly measured against the CORRECTED (WING-01-fixed) S1223 -# table via tools/wing_spar_station_fit.py, since the WING_SPAR_MIDLINE -# constant above predates that fix and is not re-derived here. -ROD_FWD_STATION = 14.0 # [mm] chordwise station aft of LE (wings_s1223_revo.scad ROD_FWD_STATION) -ROD_FWD_MIDLINE = 5.768 # [mm] S1223 camber midline at 14.0 mm, root chord -ROD_FWD_Y = WING_LE_ROOT_Y + ROD_FWD_STATION # = +7.00 -ROD_FWD_D = 8.2 # [mm] clearance bore = 8 mm CF rod + 0.1 mm/side -ROD_FWD_BOSS_OD = 17.9 # [mm] bore + 2 x 4.85 mm radial margin (spar-boss convention) -# 41 mm, not the round 40: an exact 40 mm embed puts this boss's inboard end -# cap exactly coplanar with the main spar boss's own end cap (both at -# PORT_INB = -100.0), which produced a degenerate near-zero-length -# non-manifold edge in the boolean-merged, float32-repaired shell (found by -# tools/validate_stls.py-style verify() during U5 verification -- 1 -# nonmanifold edge at X ~ -99.2, on this boss's end-cap rim). 1 mm of -# separation from that coincident plane clears it; forward-rod bearing FOS -# is already 5.26 at 40 mm, so the extra mm only helps. -ROD_FWD_EMBED = 41.0 # [mm] fuselage-side embed depth - -# LG-25 (2026-08-25, airframe/landing-gear/WBS.md, option 1 selected by -# owner): the fwd boss at full 17.9 mm OD stands up to 12.0 mm proud of the -# fore landing-gear bay's flange-rebate pocket (5 mm deep) at both fore -# corners -- lg_bay_features() protects this boss from the bay's own cuts -# (LG-10.4), so the boss itself must relieve locally instead. Stepped, not -# tapered (a taper needs a manifold-safe cone-frustum join this repo has no -# existing helper for; a step is a plain trimesh boolean union, same pattern -# as every other keep-out here). +# Wing root tie-rod couple — RETIRED at Rev T1 (2026-08-29) +# --------------------------------------------------------------------------- +# The two bonded CF tie rods (fwd O8.2 at station 14, aft O6.2 at station 62) +# existed only because a spar on bearings cannot react a moment, so the couple +# had to be closed by something else (U5/KTD1, FOS 4.14). Under Rev T1 the spar +# is bonded and fixed, so it carries its own moment into the root flange, and +# the rods have no remaining job. They are also no longer BUILDABLE: the wing +# gates them off (wings_s1223_revo.scad TENON_LOAD_PATH = "spar_carrythrough", +# whose comment records that the forward rod now intersects the O20.4 spar bore +# outright). Keeping fuselage-side bosses and bores for rods the wing does not +# drill would leave four blind holes in the bulkhead and four keep-outs the +# landing-gear bay must respect for nothing. # -# The overlap is NOT near the boss's wall-facing (OUTB) end as the bay's -# canted plate frame might suggest at a glance -- measured directly (boolean -# intersection of the unrelieved boss against the bay's own rebate volume, -# both corners), the actual overlap sits at hull X [-98.7..-77.1] (port) / -# [-259.5..-237.9] (stbd), i.e. against the *_INB end of each boss (X -100 / -# -237), not *_OUTB (-60 / -278). Relieve there instead: full 17.9 mm OD for -# most of the embed, stepped down to `ROD_FWD_BOSS_OD_RELIEF` for the last -# `ROD_FWD_BOSS_RELIEF_LEN` mm nearest the INB end (25 mm covers the -# measured ~21.6 mm overlap depth with margin). -# Radial margin over the 8.2 mm bore drops from 4.85 mm to 1.2 mm there -- -# thinner than the spar-boss convention, right at this repo's cited 1.16 mm -# minimum-wall figure (Rev R1a) plus a hair of margin, and only over a short -# local length, not along the rod's full structural embed. Value tuned -# empirically against `tools/landing_gear_wing_clearance.py --proud` (12.0 -# mm proud/1632-1909 mm^3 at 17.9 mm OD -> still 12.0 mm/691-718 mm^3 at -# 12.0 mm OD -> fully clear, "none -- the rebate shaves the whole footprint", -# at 10.6 mm OD) -- not derived in closed form, because the interference is -# measured in the bay's own canted plate frame, not this file's hull frame, -# and the reported "proud depth" turned out to depend on OD in a way that -# wasn't obvious from the plate-frame geometry alone (a partial reduction -# left the same 12.0 mm depth reading with much less volume; only the full -# reduction to 10.6 mm actually cleared the check). -ROD_FWD_BOSS_OD_RELIEF = 10.6 # [mm] stepped-down OD at the relieved end -ROD_FWD_BOSS_RELIEF_LEN = 25.0 # [mm] length of the relief, from the INB face - -ROD_AFT_STATION = 62.0 # [mm] chordwise station aft of LE (wings_s1223_revo.scad ROD_AFT_STATION) -ROD_AFT_MIDLINE = 11.182 # [mm] S1223 camber midline at 62.0 mm, root chord -ROD_AFT_Y = WING_LE_ROOT_Y + ROD_AFT_STATION # = +55.00 -ROD_AFT_D = 6.2 # [mm] clearance bore = 6 mm CF rod + 0.1 mm/side -ROD_AFT_BOSS_OD = 15.9 # [mm] bore + 2 x 4.85 mm radial margin (spar-boss convention) -# 42 mm, not 40: at 40 mm the two-rod couple-force split gives the aft rod -# FOS 3.945 against the 5 MPa bond-limited CF-PETG allowable -- just under -# the §3 FOS 4.0 target (see the derivation in wings_s1223_revo.scad and -# tools/wing_spar_carrythrough.py report_root_joint()). 42 mm clears at -# FOS 4.14. -ROD_AFT_EMBED = 42.0 # [mm] fuselage-side embed depth - -ROD_FWD_Z = WING_CHORD_LINE_Z + ROD_FWD_MIDLINE # = +63.778 -ROD_AFT_Z = WING_CHORD_LINE_Z + ROD_AFT_MIDLINE # = +69.192 - -# Lateral-wall X brackets, mirroring PORT_INB/PORT_OUTB and STBD_INB/STBD_OUTB -# exactly (same outboard reference, -60/-278; inboard walked to the rod's own -# embed depth). Envelope-clipped to real skin by main(), same as the spar -# boss, so only rough bracketing is needed -- the outboard end is clipped to -# whatever material actually exists. -ROD_FWD_PORT_OUTB, ROD_FWD_PORT_INB = -60.0, -60.0 - ROD_FWD_EMBED # -60 / -100 -ROD_FWD_STBD_OUTB, ROD_FWD_STBD_INB = -278.0, -278.0 + ROD_FWD_EMBED # -278 / -238 -ROD_AFT_PORT_OUTB, ROD_AFT_PORT_INB = -60.0, -60.0 - ROD_AFT_EMBED # -60 / -102 -ROD_AFT_STBD_OUTB, ROD_AFT_STBD_INB = -278.0, -278.0 + ROD_AFT_EMBED # -278 / -236 - -# Wing harness entry ports (Rev S1c, 2026-08-18) — NEW. +# ROD_FWD_* / ROD_AFT_* constants, bosses and bores removed here; the design +# record stays in the wing SCAD's TENON_LOAD_PATH block and in +# airframe/fuselage-mid/WBS.md CARGO-03c / U5. + +# Wing harness entry ports — Rev T1 bore set (WA-R6, 2026-08-29) # -# Until now the shell had NO harness entry at all: the wing's spanwise conduits -# ran to the wing root face and stopped against solid cargo skin. That was -# survivable only while the EDF double-D was routed through the wing-root tenon -# (which enters the mortise), and Rev S1c moves it off the tenon entirely, so -# the entry has to be cut explicitly. +# The Rev S1c set (two O7 EDF "double-D" conduits at stations 22.75/32.25 plus +# one encoder lead at 54.0) is RETIRED. Three things replaced it, and each is a +# consequence of the fixed spar rather than a preference: # -# Stations come from the wing SCAD and MUST be kept in step with it — there is -# no other link between the two files, exactly as for WING_SPAR_Y: -# CABLE_BORE_STATION 27.5 mm, CABLE_BORE_SEP 9.5 -> conduits at 22.75 / 32.25 -# HALL_CABLE_STATION 54.0 mm -> encoder lead -# Midline heights are midline_frac(station / 129) * 129 evaluated on the same -# S1223 tables the wing uses; the identical derivation reproduces WING_SPAR_Y -# +38.150 and WING_SPAR_Z +68.420 exactly, which is the cross-check that these -# three are on the same footing as the spar. +# 1. The four 10 AWG ESC feeds no longer need their own wall ports at all. +# They ride INSIDE the spar bore, on the tilt axis (SS1, SS2.3), and enter +# the fuselage through the socket itself -- which is why SPAR_WIRE_BORE_D +# exists above. A O7 conduit could never have carried 10 AWG anyway; that +# mismatch is what started the Rev T revision. +# 2. The nav 3-core moved out of the spar (the bore is now full of power) into +# its own leading-edge conduit at station 8.0. +# 3. The AK7455 lead moved 54.0 -> 44.5 to make chordwise room at the wingtip, +# and a THIRD spanwise bore appeared that never existed before: the tilt +# drive shaft at station 53.6. # -# Both wings now share WING_LE_ROOT_Y, so one set of stations serves both sides -# — that is only true since the Wing_Stbd bake was mirror-corrected at Rev S1c -# (tools/bake_hull_frame.py). Before that the starboard wing sat 5 mm aft of -# these ports. -WING_EDF_STATION_FWD = 22.75 -WING_EDF_STATION_AFT = 32.25 -WING_ENC_STATION = 54.0 -# U6 (2026-08-25): these three were hand-copied pre-U1 snapshots (8.685, -# 10.088, 9.823) that drifted up to ~1.3 mm from the corrected S1223 table -- -# same root cause as WING_SPAR_MIDLINE above. Now derived live. -WING_EDF_MIDLINE_FWD = _wing_midline_mm(WING_EDF_STATION_FWD / WING_ROOT_CHORD) -WING_EDF_MIDLINE_AFT = _wing_midline_mm(WING_EDF_STATION_AFT / WING_ROOT_CHORD) +# Stations come from wings_s1223_revo.scad and MUST be kept in step with it -- +# there is no other link between the two files, exactly as for WING_SPAR_STATION: +# NAV_BORE_STATION 8.0 O3.2 nav 3-core +# HALL_CABLE_STATION 44.5 O6.5 AK7455 shielded SPI +# SHAFT_BORE_STATION 53.6 O4.4 tilt drive shaft +# Heights use the same unscaled-camber-midline rule as the spar, so all four +# wing-root penetrations are derived one way. +WING_NAV_STATION = 8.0 +WING_ENC_STATION = 44.5 +WING_SHAFT_STATION = 53.6 + +WING_NAV_MIDLINE = _wing_midline_mm(WING_NAV_STATION / WING_ROOT_CHORD) WING_ENC_MIDLINE = _wing_midline_mm(WING_ENC_STATION / WING_ROOT_CHORD) +WING_SHAFT_MIDLINE = _wing_midline_mm(WING_SHAFT_STATION / WING_ROOT_CHORD) -WING_EDF_Y_FWD = WING_LE_ROOT_Y + WING_EDF_STATION_FWD # = +15.75 -WING_EDF_Y_AFT = WING_LE_ROOT_Y + WING_EDF_STATION_AFT # = +25.25 -WING_ENC_Y = WING_LE_ROOT_Y + WING_ENC_STATION # = +47.00 +WING_NAV_Y = WING_LE_ROOT_Y + WING_NAV_STATION # = +1.00 +WING_ENC_Y = WING_LE_ROOT_Y + WING_ENC_STATION # = +37.50 +WING_SHAFT_Y = WING_LE_ROOT_Y + WING_SHAFT_STATION # = +46.60 -# Bore diameters are the wing conduit + 1.0 mm. The oversize is deliberate: -# the root joint carries assembly tolerance in Y and Z, and a harness port that -# is merely flush leaves the wire pinched on the skin edge at the transition. -# It costs nothing structurally — these are through-skin holes in a 2 mm shell, -# not load paths. -WING_EDF_ENTRY_D = 8.0 # wing conduit Ø7.0 + 1.0 -WING_ENC_ENTRY_D = 4.5 # wing conduit Ø3.5 + 1.0 +# Wire ports are the wing conduit + 1.0 mm. The oversize is deliberate and +# unchanged from Rev S1c: the root joint carries assembly tolerance in Y and Z, +# and a port that is merely flush leaves the wire pinched on the skin edge at +# the transition. It costs nothing structurally -- these are through-skin holes +# in a 2 mm shell, not load paths. +# +# The DRIVE SHAFT bore is the exception and is NOT oversized: it is a running +# fit for a O4 steel shaft carried on bushings at both ribs, so it holds the +# wing's own O4.4 (O4 + 0.2 mm/side) figure. Opening it "for tolerance" would +# put the shaft's own alignment into the skin, and shaft misalignment is what +# the bushings exist to prevent. +WING_NAV_ENTRY_D = 4.2 # wing conduit O3.2 + 1.0 +WING_ENC_ENTRY_D = 7.5 # wing conduit O6.5 + 1.0 +WING_SHAFT_ENTRY_D = 4.4 # = wings_s1223_revo.scad SHAFT_BORE_D, running fit # Inboard end of the harness bores. The lateral wall brackets used for the # spar boss (PORT_INB -100 / STBD_INB -240) are NOT deep enough here: ray-tracing @@ -545,70 +511,134 @@ def extract_envelope(shell_tm): WING_HARNESS_INB_PORT = -125.0 # past the -115.2 encoder-line wall WING_HARNESS_INB_STBD = -213.0 # past the -225.2 encoder-line wall (stbd inboard is +X) -# Entry heights — same camber-midline rule as the spar. -WING_EDF_Z_FWD = WING_CHORD_LINE_Z + WING_EDF_MIDLINE_FWD # = +66.695 -WING_EDF_Z_AFT = WING_CHORD_LINE_Z + WING_EDF_MIDLINE_AFT # = +68.098 -WING_ENC_Z = WING_CHORD_LINE_Z + WING_ENC_MIDLINE # = +67.833 -MORT_W = 30.8 # mortise Y span -MORT_H = 20.8 # mortise Z span +# Entry heights — same unscaled camber-midline rule as the spar. +WING_NAV_Z = WING_CHORD_LINE_Z + WING_NAV_MIDLINE # = +61.974 +WING_ENC_Z = WING_CHORD_LINE_Z + WING_ENC_MIDLINE # = +68.689 +WING_SHAFT_Z = WING_CHORD_LINE_Z + WING_SHAFT_MIDLINE # = +69.090 + +# WA-R4 (Rev T1): mortise Y span 30.8 -> 12.8. The tenon is a LOCATING feature +# at 12 x 20 x 8 mm now that the spar carries the moment +# (wings_s1223_revo.scad TENON_LOAD_PATH = "spar_carrythrough", +# WING_ROOT_TAB_W_LOCATING = 12.0); a 30.8 mm mortise is oversize for it by +# 18 mm and would let the wing rock in the one axis the tenon exists to fix. +# 0.4 mm/side clearance, unchanged -- that is the figure the CARGO-03b datum fix +# established and the tenon-fit check in tools/wing_root_deconflict.py asserts. +MORT_W = 12.8 # mortise Y span = 12.0 tenon + 0.4 mm/side +MORT_H = 20.8 # mortise Z span = 20.0 tenon + 0.4 mm/side # Lateral-wall X reference bands (deep-embed spans; the outboard end is clipped # to the real skin by the envelope, so only rough bracketing is needed). PORT_OUTB, PORT_INB = -60.0, -100.0 # port wall bracket (skin ≈ −83..−90) STBD_OUTB, STBD_INB = -278.0, -240.0 # stbd wall bracket (skin ≈ −250..−255) -# Nacelle-servo mount pads. -# Nacelle-servo mount pads. The servo DRIVES the rotating tilt-spar (horn -> -# pushrod -> spar crank), so its mount is positioned RELATIVE TO THE SPAR, not -# in absolute hull coordinates: move the spar and the servo must move with it -# or the linkage throw is detuned and the pushrod needs re-sizing (root WBS.md -# SS1.1.3 -- Nacelles, "Tune servo->spar horn/pushrod linkage throw -# (-5..140 deg)"). Rev S1b makes that dependency explicit in the code, because -# holding NSVMT_Y/Z absolute through the spar move silently broke it. +# Nacelle-tilt actuator mount pads. # -# The offsets below are exactly those in force before the move -# (spar Y 31.7 -> pad Y 45.0; spar Z 62.5 -> pad Z 93.0), so the linkage -# geometry is carried across unchanged. -# --- Rev S1d (2026-08-23, owner): pads rebuilt for LibreServo_v4 servos -------- +# WHAT CHANGED AT REV T1, AND WHY IT IS NOT A TWEAK +# ------------------------------------------------- +# Through Rev S1d the actuator was a LIMITED-ROTATION servo whose horn drove a +# pushrod to a crank clamped on the ROTATING spar, so the pad was positioned +# relative to the SPAR and the linkage throw was the thing that had to be +# preserved. Under Rev T1 the spar does not rotate, there is no crank and no +# pushrod: tilt torque leaves the fuselage on its own O4 shaft at chord station +# 53.6 (docs/WING_ATTACH_INTERFACE.md SS4.3b). The pad is therefore re-datumed +# from the spar to the DRIVE SHAFT, and the linkage is a gear pair. # -# The nacelle tilt servos are now **DS3218MG bodies fitted with the LibreServo_v4 -# control PCB** (owner direction). That fork's README is explicit that the board -# is "compatible with standard servo motors (No need to change the bottom cover of -# them!)" and its only mechanical addition, `3D/LS_body.stl`, is a 13 x 13 x -# 8.45 mm encoder part that replaces the potentiometer INSIDE the case. So the -# conversion does not change the servo's external envelope, and the pad sizes to -# the bare DS3218MG body. +# THE ACTUATOR IS ALSO A DIFFERENT KIND OF DEVICE (WA-R15). +# The tip stage is a REDUCTION -- 14T pinion into a 50T ring, i = 3.571 -- so +# the shaft must turn 1.389 REVOLUTIONS for 140 deg of nacelle. A limited- +# rotation servo cannot do that at any horn radius. The actuator is a +# MULTI-TURN unit: a DS3225 body carrying the LibreServo_v4 board with the +# rotation-limit pin removed, commanded on the RS-485 bus and closed on the +# AK7455's absolute nacelle angle rather than on its own internal travel. The +# BODY is unchanged, so this pad's footprint, bolt pattern and mass are +# unchanged -- which is the useful part: the Rev S1d datasheet work survives the +# architecture change intact. # -# WHY THE OLD PAD DID NOT FIT -# --------------------------- -# The 52 x 30 mm pad was 10.5 mm short in Z against a 40.5 mm-tall servo body -# (tools/wing_root_deconflict.py, SPAR-02). A servo overhanging its pad lands on -# raw 2 mm skin, which is not a mounting surface for a 2+ N.m actuator. +# WHY THERE IS A GEAR PAIR HERE AT ALL, RATHER THAN A DIRECT COUPLING +# ------------------------------------------------------------------ +# A coaxial coupling would be simpler and was tried first. It does not fit: put +# the DS3225 body on the drive-shaft axis (Y +46.60, Z +69.09) and it overlaps +# the O30.1 spar socket boss by 13.5 mm in Y and the full 20 mm in Z, at EITHER +# orientation of the output shaft along the 40 mm body (the shaft sits 24 mm +# from one end, so the two choices are Y +22.6..+62.6 and Y +30.6..+70.6, and +# both eat the boss). Relieving the boss to clear it would cut into the O20.4 +# socket bore itself, so there is no version of coaxial that survives. # -# DIMENSIONAL BASIS -- NOW FROM THE DATASHEET -# ------------------------------------------- -# `avionics/datasheets/DS3225 datasheet.pdf` (Dongguan City Dsservo Technology -# Co. Ltd, "6V 25kg RC Digital Servo") is authoritative for the servo envelope as -# of 2026-08-23 (owner). It supersedes the standard-size-class estimate this -# block previously carried, and it CORRECTED the mounting orientation. +# Both axes run along hull X, so a plain SPUR pair is legal here with no +# right-angle stage -- the same kinematic argument that selected the tip stage +# (docs/plans/2026-08-29-004-... KTD1), applied at the other end of the shaft. +# Inside the fuselage there is no airfoil to pay for it. # -# The part moved DS3218 -> DS3225 the same day, for torque. The two datasheets -# carry the SAME drawing and the same SS2-1 size, so nothing below changes with the -# swap -- which is the useful fact: the pad is common to both, and a later move -# within this body family will not disturb it. +# THE OFFSET IS SET BY THE LANDING-GEAR BAYS, NOT CHOSEN. +# The Rev R6 bay seats top out at Z +82.39 and the pad overlaps both bays in Y, +# so Z separation is the entire margin and the 3.0 mm clearance budget is a +# floor on the pad's bottom edge (Z >= +85.39). With the 27.0 mm pad that puts +# the actuator axis at Z >= +98.89, i.e. a centre distance of at least 29.80 mm +# above the shaft. At module 0.8 the next integer 1:1 pair above that is +# 38T/38T (PD 30.4, C = 30.40), which lands the pad bottom at +85.99 -- 3.60 mm +# of bay clearance, 0.60 mm better than the budget. 36T would give only 2.00 mm +# and 37T only 2.80 mm; both are under it. # -# size (SS2-1) 40 x 20 x 40.5 mm weight (SS2-2) 60 g -# ear span (drawing) 54.5 mm overall shaft from end 24 mm -# bolt pattern 49.5 x 10 mm flange above base 27.7 mm +# THE STAGE IS 1:1 ON PURPOSE. +# A step-UP here would trade surplus torque for slew rate -- the actuator has +# 2.402 N.m stall against 0.050 N.m at the shaft, a 48x margin, so the torque is +# genuinely there. It is rejected because it would pull the ACTUATOR back below +# one revolution (500 deg of shaft / 1.923 = 260 deg), re-opening the 180-vs-270 +# limited-rotation question that Rev T1 exists to close, and re-coupling the +# drive to a servo's internal travel. 1:1 keeps the actuator multi-turn at +# 1.389 rev, which is the whole point of WA-R15. # -# ORIENTATION. A standard servo's four screws run PARALLEL TO THE OUTPUT SHAFT, -# through a flange that is perpendicular to it. The tilt drive needs the shaft -# along hull X (so the horn sweeps the Y-Z plane, in line with the spar crank), -# which puts the FLANGE in the Y-Z plane -- flat on the bulkhead -- and the -# 40.5 mm HEIGHT along X, into the bay. So the pad footprint is the flange, -# 54.5 (Y) x 20 (Z), NOT 54.5 x 40.5: the 40.5 is depth, not footprint. The -# pre-datasheet estimate had the wrong face on the wall and oversized the pad in -# Z by 20.5 mm. +# CONSEQUENCE TO CARRY INTO FIRMWARE: an external spur pair REVERSES sense. +# Actuator-positive is nacelle-negative. Declare it, do not discover it. +TILT_STAGE_MODULE = 0.8 # mm, module -- same as the tip stage (WA-R8) +TILT_STAGE_N = 38 # teeth, BOTH gears (1:1) +TILT_STAGE_PD = TILT_STAGE_MODULE * TILT_STAGE_N # = 30.40 mm +TILT_STAGE_C = TILT_STAGE_PD # = 30.40 mm, 1:1 pair +TILT_STAGE_OD = TILT_STAGE_PD + 2 * TILT_STAGE_MODULE # = 32.00 mm tip diameter +# Gear plane, spanwise -- and it is FORCED, not chosen. Two solids share the +# shaft's X band at the wall and the shaft gear clears neither: +# +# * the O30.1 spar socket boss (X -100..-60). Shaft and spar axes are only +# 25.70 mm apart, so a shaft gear may have a tip radius of at most +# 25.70 - 15.05 - 1.0 = 9.65 mm to clear it. +# * the wing root TENON (X -100..-108, Y +51.50..+63.50). The tenon's forward +# face is 4.90 mm from the shaft axis, so clearing it in Y needs a tip radius +# under 3.90 mm -- PD 7.8, i.e. under 10 teeth at module 0.8, which undercuts +# savagely and cannot carry the mesh. +# +# The second bound is the killer and it does not yield to gear sizing: NO gear +# that can transmit at this centre distance clears the tenon in Y. It has to be +# cleared AXIALLY instead, so the mesh plane goes INBOARD of the tenon's inboard +# face (X -108 / -232), with 1 mm in hand. +# +# That is what forces the actuator standoff below, and it is the single largest +# packaging cost in the Rev T1 fuselage rework. It is recorded here rather than +# buried in the pad constant because the causal chain -- tenon depth -> gear +# plane -> actuator standoff -> bay intrusion -- is not recoverable from the +# numbers alone. +TILT_STAGE_PLANE_DX = 11.0 # mm inboard of PORT_INB/STBD_INB: tenon ends at 8, + # + the repo's 3.0 mm GAP_BUDGET to a moving part +TILT_STAGE_FACE_W = 6.0 # mm, gear face width + +# Drive-shaft wall bushing. The shaft is supported at the root rib (this wall) +# and at the tip rib; this is the fuselage half of that pair. +# +# It gets NO boss of its own, and that is a finding rather than an omission. +# The boss-margin rule (bore + 2 x 4.85) would want O17.75, i.e. 8.88 mm of +# radius, but the root mortise's forward face sits at Y +51.10 -- only 4.50 mm +# from the shaft axis -- so any boss obeying the rule is cut in half by the +# mortise it has to sit beside. Shrinking the boss to fit leaves 0.48 mm of +# wall, well under this repo's 1.16 mm floor. +# +# The bonded root flange (ROOT_FLANGE_*) solves it for free: it is 5 mm of solid +# material on the inner wall face spanning Y -9..+51 and Z +26.9..+106.9, and +# the shaft axis (Y +46.60, Z +69.09) lies inside it. Seat the bushing THERE, +# through the flange and the skin behind it -- 7 mm of bearing length, more than +# a boss would have given, in material that is already there for the moment path. +SHAFT_BUSH_OD = 8.0 # mm, flanged bronze sleeve bushing, O4 bore +SHAFT_BUSH_SEAT_D = 8.05 # mm, slip-fit seat, bonded (West System 105/206) +SHAFT_BUSH_SEAT_L = 7.0 # mm, seat depth = 5 mm flange + 2 mm skin + NSVMT_BODY_L = 40.0 # servo body length, datasheet SS2-1 -> hull Y NSVMT_BODY_W = 20.0 # servo body width, datasheet SS2-1 -> hull Z NSVMT_BODY_H = 40.5 # servo body height, datasheet SS2-1 -> hull X (inboard) @@ -616,17 +646,36 @@ def extract_envelope(shell_tm): NSVMT_FLANGE_H = 27.7 # flange face above the body base, datasheet drawing NSVMT_MARGIN = 3.5 # pad relief all round the flange footprint -# Pad centre. Kept SPAR-RELATIVE (Rev S1b) so a spar move carries the pad and the -# linkage with it instead of silently breaking them. -NSVMT_DY = 13.3 # pad centre, chordwise aft of the spar axis +# Actuator standoff, spanwise. The mounting face can no longer sit on the wall +# bracket at PORT_INB/STBD_INB: the output shaft protrudes OUTBOARD of the +# flange and carries the gear, and the gear plane is pinned inboard of the tenon +# (TILT_STAGE_PLANE_DX above). So the face steps inboard by +# gear plane offset 11.0 + gear face 6.0 + 1.0 shaft/hub clearance = 18.0 mm +# and the DS3225 body follows it, reaching X -158.5 (port) / -221.5 (stbd) +# instead of -140.5 / -239.5. +# +# COST, STATED PLAINLY: 18 mm per side of extra reach into the cargo bay, in the +# Z band +85.99..+112.99. That band is ABOVE the bay's working floor -- the pad +# is datumed to the Rev R6 landing-gear bay tops (+82.39) plus the 3.0 mm +# clearance budget, and the CARGO-01 payload envelope is measured from the +# closed-door crown at Z +8.72 upward -- so what is lost is roof volume, not +# floor footprint. It is still a real loss and it is tracked, not absorbed: +# see airframe/fuselage-mid/WBS.md WA-R15. +NSVMT_STANDOFF = 18.0 # mm, mounting face inboard of PORT_INB / STBD_INB + +# Pad centre. SHAFT-RELATIVE at Rev T1 (was spar-relative at Rev S1b, for a +# linkage that no longer exists). The actuator axis sits directly ABOVE the +# drive shaft -- pure hull-Z offset -- so the gear centre distance is one number +# and a shaft move carries the actuator and the mesh with it. +NSVMT_DY = 0.0 # pad centre, chordwise offset from the shaft axis: none # Rev S1d: the floor is datumed off the Rev R6 landing-gear bay seats, which top # out at Z +82.39 -- 2.97 mm ABOVE the spar boss crown at Z +79.42 -- and the pad # overlaps both bays in Y, so Z separation is the entire margin. Floor set to the # bay tops + the 3.0 mm clearance budget = Z +85.39; with the datasheet flange # footprint (27.0 mm tall pad) that puts the centre at Z +98.89. -NSVMT_DZ = 30.47 # pad centre, above the spar axis -NSVMT_Y = WING_SPAR_Y + NSVMT_DY # = 51.45 -NSVMT_Z = WING_SPAR_Z + NSVMT_DZ # = 98.89 +NSVMT_DZ = TILT_STAGE_C # pad centre above the shaft = the gear centre distance +NSVMT_Y = WING_SHAFT_Y + NSVMT_DY # = +46.60 +NSVMT_Z = WING_SHAFT_Z + NSVMT_DZ # = +99.49 NSVMT_PAD_W = NSVMT_EAR_SPAN + 2 * NSVMT_MARGIN # = 61.5, Y span NSVMT_PAD_H = NSVMT_BODY_W + 2 * NSVMT_MARGIN # = 27.0, Z span @@ -791,23 +840,6 @@ def extract_envelope(shell_tm): # edge by 7.7-14.5 mm^3, which is accepted and recorded here. -def _stepped_x_cylinder(y_cen, z_cen, x_far, x_near, od_full, od_relief, - relief_len): - """Boss stepped down to `od_relief` for `relief_len` mm nearest x_near - (the end to relieve), full `od_full` for the rest (LG-25, see - ROD_FWD_BOSS_OD_RELIEF above). Booleans the two segments into one - manifold rather than concatenating them raw -- to_man() requires a - genuinely manifold input, and two abutting cylinders are not that until - unioned.""" - direction = 1.0 if x_far > x_near else -1.0 - x_step = x_near + direction * relief_len - lo, hi = sorted([x_far, x_step]) - full = x_cylinder(y_cen, z_cen, lo, hi, od_full / 2.0) - lo2, hi2 = sorted([x_step, x_near]) - relief = x_cylinder(y_cen, z_cen, lo2, hi2, od_relief / 2.0) - return from_man(to_man(full) + to_man(relief)) - - def wing_keepout_positives(envelope_tm=None): """Wing material the gear bay's cuts must not remove. (label, solid). @@ -825,33 +857,47 @@ def wing_keepout_positives(envelope_tm=None): ("spar boss stbd", x_cylinder( WING_SPAR_Y, WING_SPAR_Z, STBD_OUTB, STBD_INB, WING_SPAR_BOSS_OD / 2.0)), - ("servo pad port", box( - PORT_INB, PORT_OUTB, + ("actuator pad port", box( + PORT_INB - NSVMT_STANDOFF, PORT_OUTB, NSVMT_Y - NSVMT_PAD_W / 2, NSVMT_Y + NSVMT_PAD_W / 2, NSVMT_Z - NSVMT_PAD_H / 2, NSVMT_Z + NSVMT_PAD_H / 2)), - ("servo pad stbd", box( - STBD_OUTB, STBD_INB, + ("actuator pad stbd", box( + STBD_OUTB, STBD_INB + NSVMT_STANDOFF, NSVMT_Y - NSVMT_PAD_W / 2, NSVMT_Y + NSVMT_PAD_W / 2, NSVMT_Z - NSVMT_PAD_H / 2, NSVMT_Z + NSVMT_PAD_H / 2)), - # U5/KTD1 (2026-08-24): wing-root tie-rod bosses, mirroring the spar - # boss's own embed-boss pattern exactly. - # LG-25 (owner-selected option 1): stepped down near the INB end, - # where the fore landing-gear bay's flange rebate footprint left the - # boss standing up to 12.0 mm proud (measured overlap, see the - # ROD_FWD_BOSS_OD_RELIEF comment above -- NOT the OUTB/wall-facing - # end intuition would suggest). - ("rod fwd boss port", _stepped_x_cylinder( - ROD_FWD_Y, ROD_FWD_Z, ROD_FWD_PORT_OUTB, ROD_FWD_PORT_INB, - ROD_FWD_BOSS_OD, ROD_FWD_BOSS_OD_RELIEF, ROD_FWD_BOSS_RELIEF_LEN)), - ("rod fwd boss stbd", _stepped_x_cylinder( - ROD_FWD_Y, ROD_FWD_Z, ROD_FWD_STBD_OUTB, ROD_FWD_STBD_INB, - ROD_FWD_BOSS_OD, ROD_FWD_BOSS_OD_RELIEF, ROD_FWD_BOSS_RELIEF_LEN)), - ("rod aft boss port", x_cylinder( - ROD_AFT_Y, ROD_AFT_Z, ROD_AFT_PORT_INB, ROD_AFT_PORT_OUTB, - ROD_AFT_BOSS_OD / 2.0)), - ("rod aft boss stbd", x_cylinder( - ROD_AFT_Y, ROD_AFT_Z, ROD_AFT_STBD_OUTB, ROD_AFT_STBD_INB, - ROD_AFT_BOSS_OD / 2.0)), + # WA-R1b (Rev T1): the bonded root flange -- the MOMENT path. + # + # THIS IS A KEEP-OUT, NOT PRINTED HULL MATERIAL, and that distinction was + # arrived at by measurement. The flange footprint is 80 (Z) x 60 (Y), + # and the sidewall skin moves through **34.3 mm (port) / 37.0 mm (stbd)** + # of hull X across it (ray-cast on a 13 x 17 grid, 221/221 hits, + # 2026-08-30). So: + # * "internal thickening to a plane", the idiom every other positive + # here uses, would make the flange up to 34 mm thick -- hundreds of + # grams for a plate whose specified 5 mm already gives FOS 29.2; and + # * a plane at the nominal inner face is TANGENT to the skin inside the + # footprint, which is a knife edge. Measured on the first rebuild: + # a 0.46 mm non-manifold edge with 4 incident faces at + # (-86.33, +8.6, +52.2) plus a zero-area sliver body. Subtracting a + # translated envelope to get a conforming plate instead was WORSE -- + # a near-coincident boolean over 4,800 mm^2 of a 900k-face mesh left + # 4 boundary edges, 2 non-manifold edges and 4 bodies. + # + # The flange is therefore a SEPARATE BONDED PART, exactly like the CF + # thwarts (asf.RING_POCKETS) and the splice collars: generated to the + # real skin curvature by + # airframe/stls/fuselage/generate_wing_root_flange.py, bonded in with + # West System 105/206. The shell contributes nothing to it but the + # obligation not to have the landing-gear bay grow into its volume -- + # which is what this entry is. + ("root flange port", box( + WALL_INNER_X_PORT, WALL_SKIN_X_PORT, + WING_SPAR_Y - ROOT_FLANGE_W / 2, WING_SPAR_Y + ROOT_FLANGE_W / 2, + WING_SPAR_Z - ROOT_FLANGE_H / 2, WING_SPAR_Z + ROOT_FLANGE_H / 2)), + ("root flange stbd", box( + WALL_SKIN_X_STBD, WALL_INNER_X_STBD, + WING_SPAR_Y - ROOT_FLANGE_W / 2, WING_SPAR_Y + ROOT_FLANGE_W / 2, + WING_SPAR_Z - ROOT_FLANGE_H / 2, WING_SPAR_Z + ROOT_FLANGE_H / 2)), ] if envelope_tm is None: return raw @@ -867,96 +913,96 @@ def wing_keepout_positives(envelope_tm=None): def wing_harness_ports(): """Wing harness entry bores through each lateral wall. (label, solid). - Rev S1c. Three per side: the EDF double-D (two Ø8.0) and the AK7455 - encoder lead (one Ø4.5), each coaxial with the matching spanwise conduit in - wings_s1223_revo.scad so the wire runs straight from the wing into the - cargo cavity instead of dead-ending on the skin. + Rev T1 (WA-R6). Three per side, each coaxial with the matching spanwise + bore in wings_s1223_revo.scad so the wire -- or, for the third one, the + shaft -- runs straight from the wing into the cargo cavity instead of + dead-ending on the skin: + + nav 3-core station 8.0 O4.2 (wing conduit O3.2 + 1.0) + AK7455 SPI pair station 44.5 O7.5 (wing conduit O6.5 + 1.0) + tilt drive shaft station 53.6 O4.4 (running fit, NOT oversized) + + The two Rev S1c O8.0 EDF ports are gone: the four 10 AWG feeds now enter + through the spar's own bore at the socket (SPAR_WIRE_BORE_D), which is the + whole point of a hollow fixed spar on the tilt axis. - These are cut through the same wall brackets the spar bore uses, so the + These are cut through the same wall brackets the spar socket uses, so the outboard end is clipped to the real skin by the envelope exactly as the - spar bore is. + socket is. """ return [ - ("EDF entry fwd", x_cylinder( - WING_EDF_Y_FWD, WING_EDF_Z_FWD, WING_HARNESS_INB_PORT, PORT_OUTB, - WING_EDF_ENTRY_D / 2.0)), - ("EDF entry aft", x_cylinder( - WING_EDF_Y_AFT, WING_EDF_Z_AFT, WING_HARNESS_INB_PORT, PORT_OUTB, - WING_EDF_ENTRY_D / 2.0)), + ("nav entry", x_cylinder( + WING_NAV_Y, WING_NAV_Z, WING_HARNESS_INB_PORT, PORT_OUTB, + WING_NAV_ENTRY_D / 2.0)), ("encoder entry", x_cylinder( WING_ENC_Y, WING_ENC_Z, WING_HARNESS_INB_PORT, PORT_OUTB, WING_ENC_ENTRY_D / 2.0)), - ("EDF entry fwd stbd", x_cylinder( - WING_EDF_Y_FWD, WING_EDF_Z_FWD, STBD_OUTB, WING_HARNESS_INB_STBD, - WING_EDF_ENTRY_D / 2.0)), - ("EDF entry aft stbd", x_cylinder( - WING_EDF_Y_AFT, WING_EDF_Z_AFT, STBD_OUTB, WING_HARNESS_INB_STBD, - WING_EDF_ENTRY_D / 2.0)), + ("tilt shaft entry", x_cylinder( + WING_SHAFT_Y, WING_SHAFT_Z, WING_HARNESS_INB_PORT, PORT_OUTB, + WING_SHAFT_ENTRY_D / 2.0)), + ("nav entry stbd", x_cylinder( + WING_NAV_Y, WING_NAV_Z, STBD_OUTB, WING_HARNESS_INB_STBD, + WING_NAV_ENTRY_D / 2.0)), ("encoder entry stbd", x_cylinder( WING_ENC_Y, WING_ENC_Z, STBD_OUTB, WING_HARNESS_INB_STBD, WING_ENC_ENTRY_D / 2.0)), + ("tilt shaft entry stbd", x_cylinder( + WING_SHAFT_Y, WING_SHAFT_Z, STBD_OUTB, WING_HARNESS_INB_STBD, + WING_SHAFT_ENTRY_D / 2.0)), + # Bushing seat, counterbored into the root flange + skin (see + # SHAFT_BUSH_* above for why it does not get a boss of its own). + ("tilt shaft bushing seat", x_cylinder( + WING_SHAFT_Y, WING_SHAFT_Z, + WALL_INNER_X_PORT, WALL_INNER_X_PORT + SHAFT_BUSH_SEAT_L, + SHAFT_BUSH_SEAT_D / 2.0)), + ("tilt shaft bushing seat stbd", x_cylinder( + WING_SHAFT_Y, WING_SHAFT_Z, + WALL_INNER_X_STBD - SHAFT_BUSH_SEAT_L, WALL_INNER_X_STBD, + SHAFT_BUSH_SEAT_D / 2.0)), ] -def spar_bearing_seat_cuts(outb_x, inb_x, label, harness_inb=None): - """CARGO-01/CARGO-02 (2026-08-24): one side's F688ZZ bearing seat + - rotating-spar clearance bore, coaxial with the spar boss, STOPPING at the - wall station `inb_x` instead of crossing the bay. `outb_x` is the - wing-facing (outboard) end of the boss, where the bearing's flange seats - flush; `inb_x` is the bay-facing (inboard) end -- the spar's inboard tip, - per SPAR-01's measured "spar inboard end if it stops at the boss" station - (X -100 port / -240 stbd). Three coaxial cylinders, outboard to inboard: - 1. flange counterbore (ROOT_BRG_FLANGE_OD, ROOT_BRG_FLANGE_T deep) -- - seats the F688ZZ flange flush with the boss face. - 2. bearing seat (ROOT_BRG_SEAT_D, ROOT_BRG_W deep) -- press-fit for the - bearing's outer race. - 3. rotating-spar clearance (WING_SPAR_BORE_D) for the remainder of the - boss depth, down to `inb_x` -- the spar's shaft, NOT a through-bore - to the opposite wall. - - U6 (2026-08-25): a 4th cut, "nav-light wire exit", was added when - tools/wing_root_deconflict.py's bulkhead-penetration sweep found 7.4 mm^3 - of uncut material on the STBD side only, 15-45 mm inboard of `inb_x`. - Root cause: the spar's rotating tube physically ends at `inb_x` (the - boss's own inboard tip), so cut #3 above stops there too -- but the - nav-light 3-core wire that rides inside the spar's hollow ID does NOT - stop there. It has to keep going, loose, to the same deep interior wall - the EDF/Hall harness ports were already cut through - (`WING_HARNESS_INB_PORT`/`_STBD`, "past the ... encoder-line wall"). On - port that wall sits close enough to `inb_x` (-100 -> harness limit -125) - that no separate cut was needed by coincidence; on stbd the gap is wider - (-240 -> -213) and the wire path was left uncut. Pass `harness_inb` (the - same constant `wing_harness_ports()`/`wing_keepout_negatives()` already - use for that side) to extend the wire-clearance bore the rest of the way; - omit it to keep the old 3-cut behaviour (there is no other caller). +def spar_socket_cuts(outb_x, inb_x, label, harness_inb=None): + """One side's bonded spar socket (WA-R1/WA-R2, Rev T1). + + `outb_x` is the wing-facing (outboard) end of the boss; `inb_x` is the + bay-facing end, which is ALSO the socket's inboard limit -- the cargo bay's + clear span begins there (X -100 port / -240 stbd) and the owner ruling is + that it stays clear. Against a wall skin at X -81.33 that is + SPAR_SOCKET_REACH = 18.5 mm of depth, and 18.67 mm is the most the bay + allows at all. + + Two coaxial cylinders, outboard to inboard: + 1. socket bore (WING_SPAR_BORE_D = O20.4) from the boss face to `inb_x` -- + the bonded/clamped seat for the FIXED CF spar. sigma = 0.31 MPa on + projected bearing area, FOS 16, and shear never needed depth. + 2. wire continuation (SPAR_WIRE_BORE_D = O16.3, the spar's own ID) from + `inb_x` to `harness_inb`. The spar stops at the socket; the four + 10 AWG conductors inside it do not, and they have to reach the same + deep interior wall the encoder/nav ports are cut through. Omitting + this is what left 7.4 mm^3 of uncut material on the stbd side at Rev + S1c -- see the U6 note in git history for that finding. + + WHAT IS NOT HERE ANY MORE: the F688ZZ flange counterbore and bearing seat. + A bearing at this station would let the fixed spar rotate under the tilt + pinion's gear reaction, so it is not merely redundant, it is wrong (WA-R2). + Returns a list of (label, solid) tuples, matching the module's convention. """ span = abs(outb_x - inb_x) - depth = ROOT_BRG_FLANGE_T + ROOT_BRG_W - assert depth < span, ( - f"{label}: flange+seat depth {depth} mm exceeds boss span {span} mm" + assert span >= SPAR_SOCKET_REACH - 1e-6, ( + f"{label}: boss span {span:.2f} mm is shorter than the " + f"{SPAR_SOCKET_REACH} mm socket reach WA-R1 requires" ) - # Walk inward from outb_x toward inb_x by a signed step (handles both the - # port boss, where inb_x < outb_x, and the stbd boss, where inb_x > outb_x). - step = -1.0 if inb_x < outb_x else 1.0 - x0 = outb_x - x1 = outb_x + step * ROOT_BRG_FLANGE_T - x2 = x1 + step * ROOT_BRG_W cuts = [ - (f"{label} bearing flange counterbore", x_cylinder( - WING_SPAR_Y, WING_SPAR_Z, min(x0, x1), max(x0, x1), - ROOT_BRG_FLANGE_OD / 2.0)), - (f"{label} bearing seat", x_cylinder( - WING_SPAR_Y, WING_SPAR_Z, min(x1, x2), max(x1, x2), - ROOT_BRG_SEAT_D / 2.0)), - (f"{label} spar clearance", x_cylinder( - WING_SPAR_Y, WING_SPAR_Z, min(x2, inb_x), max(x2, inb_x), + (f"{label} spar socket", x_cylinder( + WING_SPAR_Y, WING_SPAR_Z, min(outb_x, inb_x), max(outb_x, inb_x), WING_SPAR_BORE_D / 2.0)), ] if harness_inb is not None and abs(harness_inb - inb_x) > 0.01: - cuts.append((f"{label} nav-light wire exit", x_cylinder( + cuts.append((f"{label} 10 AWG bundle exit", x_cylinder( WING_SPAR_Y, WING_SPAR_Z, min(inb_x, harness_inb), - max(inb_x, harness_inb), WING_SPAR_BORE_D / 2.0))) + max(inb_x, harness_inb), SPAR_WIRE_BORE_D / 2.0))) return cuts @@ -965,10 +1011,10 @@ def wing_keepout_negatives(): my0, my1 = WING_MORT_Y - MORT_W / 2, WING_MORT_Y + MORT_W / 2 mz0, mz1 = WING_ROOT_Z - MORT_H / 2, WING_ROOT_Z + MORT_H / 2 return [ - *spar_bearing_seat_cuts(PORT_OUTB, PORT_INB, "port", - harness_inb=WING_HARNESS_INB_PORT), - *spar_bearing_seat_cuts(STBD_OUTB, STBD_INB, "stbd", - harness_inb=WING_HARNESS_INB_STBD), + *spar_socket_cuts(PORT_OUTB, PORT_INB, "port", + harness_inb=WING_HARNESS_INB_PORT), + *spar_socket_cuts(STBD_OUTB, STBD_INB, "stbd", + harness_inb=WING_HARNESS_INB_STBD), # CARGO-03 (2026-08-24): these used to span PORT_INB+1 .. PORT_OUTB-10 # (X -99..-70), described as cutting "through each wall". They did not: # at the mortise station the wall lies at X -115..-99, so the cut began @@ -989,22 +1035,6 @@ def wing_keepout_negatives(): # A gear-bay flange that plugs a harness port is the same class of # failure as one that plugs the spar bore: it is found at assembly, # with the wire in hand, and it cannot be relieved from outside. - # - # U5/KTD1 (2026-08-24): wing-root tie-rod clearance bores, same - # treatment as the spar bore -- deep bonded holes that must stay - # open for the CF rod to seat, not be plugged by the gear bay. - ("rod fwd bore port", x_cylinder( - ROD_FWD_Y, ROD_FWD_Z, ROD_FWD_PORT_INB, ROD_FWD_PORT_OUTB, - ROD_FWD_D / 2.0)), - ("rod fwd bore stbd", x_cylinder( - ROD_FWD_Y, ROD_FWD_Z, ROD_FWD_STBD_OUTB, ROD_FWD_STBD_INB, - ROD_FWD_D / 2.0)), - ("rod aft bore port", x_cylinder( - ROD_AFT_Y, ROD_AFT_Z, ROD_AFT_PORT_INB, ROD_AFT_PORT_OUTB, - ROD_AFT_D / 2.0)), - ("rod aft bore stbd", x_cylinder( - ROD_AFT_Y, ROD_AFT_Z, ROD_AFT_STBD_OUTB, ROD_AFT_STBD_INB, - ROD_AFT_D / 2.0)), ] + wing_harness_ports() @@ -1317,9 +1347,8 @@ def build_negatives(shell_tm, envelope_tm=None): cutters.append(box(xm, xx, ym, yx, zm, zx)) notes.append("CF thwart pockets Y=-40 (fore) / Y=+118 (aft), U4/SPAR-01") - # Wing spar F688ZZ bearing seat + rotating-spar clearance (per side, - # terminating at the wall -- CARGO-01/CARGO-02) + 2 root mortises (through - # each wall). + # Wing spar bonded socket + 10 AWG bundle exit (per side, terminating at + # the bay edge -- WA-R1/WA-R2) + 2 root mortises (through each wall). # Built by wing_keepout_negatives() so the solids the gear bay is trimmed # against are the SAME solids the hull is actually cut with (LG-10.4). for label, solid in wing_keepout_negatives(): @@ -1328,7 +1357,10 @@ def build_negatives(shell_tm, envelope_tm=None): # Nacelle-servo M3 heat-set pilot bores (into the cavity face of each pad). if NSVMT_HOLES_ENABLED: - for x_in, x_out in ((PORT_INB, PORT_INB + 8.0), (STBD_INB, STBD_INB - 8.0)): + face_port = PORT_INB - NSVMT_STANDOFF + face_stbd = STBD_INB + NSVMT_STANDOFF + for x_in, x_out in ((face_port, face_port + 8.0), + (face_stbd, face_stbd - 8.0)): for dy in (-NSVMT_HOLE_S_Y, NSVMT_HOLE_S_Y): for dz in (-NSVMT_HOLE_S_Z, NSVMT_HOLE_S_Z): cutters.append( @@ -1361,16 +1393,22 @@ def build_positives(shell_tm, envelope_tm=None): feats.append(hinge._block(side, station)) notes.append("4 hinge retention blocks") - # Wing-spar bearing bosses (Ø27.7, coaxial with the spar), the two - # nacelle-servo mount pads, and (U5/KTD1) the two wing-root tie-rod - # bosses -- deep boxes/cylinders, envelope-clipped. Built by + # Wing-spar socket bosses (Ø30.1, coaxial with the spar), the two + # nacelle-tilt actuator mount pads, and (WA-R1b) the two bonded wing-root + # flanges -- deep boxes/cylinders, envelope-clipped. Built by # wing_keepout_positives() so the solids the gear bay is trimmed against # are the SAME solids the hull actually carries (LG-10.4). + # The root flanges are in the keep-out set but are NOT built here: they are + # separate bonded parts (see wing_keepout_positives), so the shell must + # reserve their volume without printing it. for _label, solid in wing_keepout_positives(): # raw: main() clips + if _label.startswith("root flange"): + continue feats.append(solid) - notes.append("2 wing-spar bearing bosses") - notes.append("2 nacelle-servo mount pads") - notes.append("2 wing-root tie-rod bosses (fwd+aft, port+stbd)") + notes.append("2 wing-spar socket bosses (O30.1, Rev T1)") + notes.append("2 nacelle-tilt actuator mount pads") + notes.append("root flanges RESERVED, not printed -- separate bonded parts " + "(generate_wing_root_flange.py)") # Inara avionics-bay standoff bosses (dorsal port, deep Z-cyl, clipped). n = 0 @@ -1636,6 +1674,13 @@ def finalize_watertight(mesh): if not mesh.is_watertight: trimesh.repair.fill_holes(mesh) trimesh.repair.fix_normals(mesh) + # Rev T1c: sweep slivers ONE LAST TIME. The body drop above happens before + # close_zero_area_slits(), and that pass can leave a fresh zero-area + # fragment behind -- observed as a 2-face, 0.0 mm^3 body at + # (-84.0, +7.7, +64.4) surviving to the published STL, which reads as + # "bodies=2" in every downstream check even though the shell is watertight. + # A trailing sweep costs one split() and removes the false signal. + mesh = drop_slivers(mesh) return mesh diff --git a/airframe/fuselage-mid/WBS.md b/airframe/fuselage-mid/WBS.md index 4e400f3..62e962f 100644 --- a/airframe/fuselage-mid/WBS.md +++ b/airframe/fuselage-mid/WBS.md @@ -1070,3 +1070,214 @@ are **DEFERRED to Phase 11** — do not cut or modify the inner neck before Phas - [ ] Wire strobe to led pwd output on Pilot cape in Simon's medbay + +--- + +## §1.1.1.5 — Wing-Root Spar Socket (Rev T1 joint requirements) + +**Owning specification:** [`docs/WING_ATTACH_INTERFACE.md`](../../docs/WING_ATTACH_INTERFACE.md) +**Owning plans:** `docs/plans/2026-08-29-003-...` U7; `airframe/wings-nacelles/WBS.md` §1.1.4 SPAR-20-7 + +**Why this section exists.** The wing side of the Rev T1 tilt-spar migration is +BUILT (2026-08-29); the fuselage side is not. The spar is no longer a rotating +drive shaft that the wing rides on — it is a **fixed 20 × 16.3 mm CF tube bonded +into the wing**, and it is now the wing's primary bending member. That makes the +fuselage socket the wing's structural root joint, replacing both the enlarged +tenon (FOS 0.49, CARGO-03c) and the two-rod couple (FOS 4.14, U5/KTD1) that +existed only because a spar on bearings could not react a moment. + +**LANDED 2026-08-30 (Rev T1c).** The gate was RED on three findings that were +all one cause — the fuselage still carried `WING_SPAR_Y = +38.15` and +`WING_SPAR_BORE_D = 8.3`. All of `merge_cargo_interior.py`'s wing-root geometry +is rebuilt against the Rev T1 wing and the cargo shell regenerated. + +Fixing the fuselage also exposed three genuine defects in the CHECKING tool, +masked while the geometry was wrong — most importantly `tenon_params()` +selecting the enlarged 30 mm tenon for the Rev T1 default load path, which +reported an 8.60 mm/side mortise foul against a correctly-sized mortise. See +`docs/WING_ATTACH_INTERFACE.md` §7. + +- [x] **WA-R1 — Spar socket (SHEAR path). DONE 2026-08-30.** Hull **Y +21.00**, **Z +66.85**, + bore **Ø20.4** (20.0 OD + 0.2 mm/side epoxy gap), **18.5 mm** deep — all the + cargo bay allows, and all the shear needs (σ = 0.31 MPa, **FOS 16**). Both stations are DERIVED, not chosen: the wing LE root bakes to hull + Y −7.0 and the spar is at chord station 28.0 → +21.0; the chord line bakes + to Z +58.01 and the spar rides the **unscaled** camber midline (+8.84 mm at + this station) → +66.85. **Do not apply `THICKNESS_SCALE` to the midline** — + `s1223_section()` opens the thickness envelope *about* an unscaled camber + line, so scaling it would lift the socket 4.07 mm above the spar. + Update `WING_SPAR_Y` / `WING_SPAR_Z` / `WING_SPAR_BORE_D` in + `airframe/blender-scripts/merge_cargo_interior.py`. + +- [x] **WA-R1b — Bonded root flange (MOMENT path). DONE 2026-08-30 — as a SEPARATE BONDED PART, not printed hull.** **80 mm (hull Z) × 60 mm + (hull Y)** bonded to the INNER face of the sidewall, concentric with the + socket. σ = 0.17 MPa, **FOS 29.2**. + **This replaces the 55 mm socket the first revision of this section asked + for, and the bay requirement is why.** A socket's capacity goes as 1/L² and + the bay caps L at 18.67 mm, where the socket gives FOS 0.51 on the moment — + unrecoverable by any depth the bay permits. A flange reacts over wall AREA + instead, needs no inboard reach at all (it protrudes only its own ~5 mm, to + X ≈ −86 against a bay edge at −100), and lands at FOS 29.2. The constraint + forced a better joint, not a compromise. + **BUILD NOTE 2026-08-30 — it is a bonded PART, not printed hull material, + and that was settled by measurement.** Ray-casting the baked envelope on a + 13 × 17 grid over the footprint (221/221 hits) shows the sidewall skin moving + through **34.3 mm (port) / 37.0 mm (stbd)** of hull X across the 80 × 60 + window. So "internal thickening to a plane" — the idiom every other positive + in `merge_cargo_interior.py` uses — would make the flange up to 34 mm thick, + and a plane at the nominal inner face is *tangent* to the skin inside the + footprint (measured: a 0.46 mm non-manifold edge, 4 incident faces, at + (−86.33, +8.6, +52.2), plus a sliver body). A conforming plate cut from the + full 900k-face shell was worse still. Done LOCALLY it is clean: + `airframe/stls/fuselage/generate_wing_root_flange.py` → + `wing_root_flange_port/stbd.stl`, **24.3 / 24.4 cm³, 12.7 g each at 40 % + gyroid**, both watertight single bodies. The shell's only obligation is to + reserve the volume, which `wing_keepout_positives()` does. + +- [x] **WA-R2 — Delete the F688ZZ root bearing. DONE 2026-08-30.** A bearing here is no longer + merely unnecessary, it is wrong: it would let the fixed spar spin under the + tilt pinion's gear reaction. Replace with a bonded/clamped socket. + +- [ ] **WA-R3 — Split-collar pinch clamp.** ≥ 5 mm wall over Ø20 (≈ Ø30 outside), + M3 heat-set inserts, 2 screws. **No set screws** — CF tube splinters under a + point load. The split must retain a **positive gap when clamped**; if the + halves close on each other first, the collar grips itself and the spar is + free. This is the joint that makes wing + bonded spar removable as one + assembly (the spar is bonded in the wing, clamped in the fuselage). + +- [x] **WA-R4 — Re-size the mortise. DONE 2026-08-30.** 30.8 → **12.8 mm** wide. The tenon is now + a locating feature at 12 × 20 × 8 mm (`TENON_LOAD_PATH = "spar_carrythrough"`), + not a 30 mm structural tenon. The current mortise is oversize for it. + +- [x] **WA-R5 — CLOSED by design, 2026-08-29.** The joint no longer enters the + bay at all, so the envelope is unchanged and needs no re-verification. The + spar still stops at X −100 and the flange protrudes to X ≈ −86. + +- [x] **WA-R6 — Fuselage-side conduits. DONE 2026-08-30.** to match the wing's new bore set: + nav 3-core at hull Y +1.0 (Ø3.2), AK7455 shielded pair at Y +37.5 (Ø6.5), + tilt drive shaft at Y +46.6 (Ø4.4). The retired Ø7 EDF double-D ports can go + — the four 10 AWG feeds now enter the spar bore directly at the socket. + +- [x] **WA-R15 — Tilt actuator re-select and fuselage drive stage. DONE + 2026-08-30.** The actuator is a **DS3225 body + LibreServo_v4 with the + rotation-limit pin removed**, run continuous-rotation (multi-turn) and closed + on the AK7455 — the architecture the cargo winch already uses. The *body* is + unchanged, so the Rev S1d pad footprint (61.5 × 27.0), the 49.5 × 10 mm bolt + pattern and the 60 g mass all carry over; what is new is the mode, the + position, and a gear stage. + - Pad re-datumed from the (now non-rotating) spar to the **drive shaft**: + axis at hull **Y +46.60, Z +99.49**. The old spar-relative datum existed + to preserve a horn-and-pushrod linkage that no longer exists. + - **Fuselage spur stage, module 0.8, 38T/38T, C = 30.40 mm, 1:1.** A coaxial + coupling was tried first and does not fit — the body overlaps the Ø30.1 + socket boss by 13.5 mm in Y at either shaft orientation. + - C is set by the **landing-gear bays** (seats top out at Z +82.39; the 3.0 mm + budget puts the pad bottom at ≥ +85.39, so C ≥ 29.80; 36T and 37T fall + under budget, 38T clears at 3.60 mm). + - **The stage is 1:1 deliberately.** A step-up would trade the 48× torque + surplus for slew rate but pulls the actuator back to 260° — under one + revolution — re-opening the question `WING_ATTACH_INTERFACE.md` §4.3b + closed. + - **COST — 18 mm/side of extra reach into the cargo bay.** The gear plane is + forced 11 mm inboard of X −100/−240 because the wing root **tenon** + (X −100…−108) leaves only 4.90 mm to the shaft axis, and no gear that can + transmit at C = 30.40 clears it in Y. The actuator follows the gear and + reaches X −158.5 / −221.5, in the hull Z band +85.99…+112.99 — above the + bay's working floor, so the loss is roof volume, not floor footprint. + +- [ ] **WA-R18 — Mass/CG/T-W re-derivation after Rev T1c.** **REVISED 2026-08-30 + by the weight audit — the first figure was low.** The rework is + **+102.8 g (+0.227 lbm) net**, +2.63 % on the 3,911 g (8.62 lbm) AUW, and it + is *not* concentrated at the CG — it sits at the wing root, outboard and low. + + | | Δ mass | + |---|---| + | Cargo shell (Ø30.1 sockets, 18 mm actuator standoffs, less the retired tie-rod bosses) | **+56.0 g** — 301,145 → 354,486 mm³ | + | New parts: `SPAR-CF-20X16` 58.2, `SHAFT-TILT-4MM` 49.4, `GEAR-TILT-FUS-38T` 16.0, `BUSH-TILT-4MM` 6.0, `PRINT-WING-ROOT-FLANGE` **51.2** | +180.8 g | + | Retired: `SPAR-TILT-4130` 96, `BRG-F688ZZ` 10, `PRINT-PUSHROD-CRANK` 6, `CF-ROD-8MM` 14, `CF-ROD-6MM` 8 | −134.0 g | + | **Net** | **+46.8 g** parts, **+102.8 g** with the shell | + + The flange was under-massed by 25.6 g at Rev T1c: it was costed as + `RHO_SOLID × 40 % infill`, but at 5 mm thick with 4 perimeters at 0.6 mm the + plate is 4.8 mm of *wall* and 0.2 mm of infill — essentially solid, and infill + barely touches it. Use `RHO_PRINT` (1.05e-3 g/mm³). + + Hover T/W was ~1.19 (~1.25 with `BATT-6S-2800`) against the 1.2 minimum + (§1.1.5 mass notice), so **this puts the un-swapped case below the minimum**. + Re-derive T/W and the CG envelope before flight release. + **`docs/MASS_AUDIT_CARGO_WING_ROOT.md` §6 identifies −32.9 g of low-risk + recovery** (hollow the actuator standoffs), which lands Rev T1c at +69.9 g on + the corrected basis. **BLOCKS flight release.** + +- [ ] **MA-1 — Reconcile every `PRINT-*` BOM row to its STL.** The weight audit + measured 23 matched rows and found the BOM understates printed mass by + **+521.6 g — 13.3 % of AUW**. `PRINT-CARGO-SECT` (+207), `FOAM-PU-2LB` (kit + mass, −878) and `PRINT-WING-ROOT-FLANGE` (+12.9) are corrected; the rest are + measured and reported in `docs/MASS_AUDIT_CARGO_WING_ROOT.md` §5 for their + owning branches. Add a CI check so a regenerated STL cannot silently diverge + from its BOM row again. **BLOCKS any weight statement.** + +- [ ] **MA-5 — Hollow the actuator standoffs (−32.9 g).** They are 109.5 g of + solid block reacting a 5.0 N worst-case combined load; deflection at the gear + mesh is 1.4 × 10⁻⁵ mm, three-plus orders inside the 0.05 mm that would matter. + 2.5 mm wall + 2.5 mm face. Do **not** instead shrink `NSVMT_STANDOFF` (it is + 11 + 6 + 1, all of it accounted) or the 6 mm gear face (already 7.5 × module). + `docs/MASS_AUDIT_CARGO_WING_ROOT.md` §6. + +- [ ] **MA-6 — `PRINT-BATT-TRAY` measures 140.2 g against a 22 g BOM row.** It is + in the cargo section, it is 3.6 % of AUW, and it is 30 % of its own bounding + box for a part whose job is to locate a LiPo and react 4 g × 1.5 through + straps. Re-measure, then lighten on its merits. + +- [ ] **MA-7 — The BOM mass column mixes installed mass, consumable stock and + ground equipment.** Summing `Total_Mass_g` returns 12.7 kg, which is the mass + of nothing. Add an `Installed` flag before any weight statement uses it. + +- [ ] **WA-R15a — Cargo-bay envelope re-measure after the actuator standoff.** + `tools/cargo_bay_envelope.py` measured the 140 mm lateral limit against pads + at X −100/−240. The actuator bodies now reach −158.5/−221.5 in the + Z +85.99…+112.99 band. Re-measure and record the roof-band clear width before + the five roof/wall-mounted CARGO-01 accessories are placed. + +- [ ] **WA-R16 — Tilt drive holding provision (TILT-CTL-01).** The train is two + spur stages at a total reduction of 3.571 and is **not self-locking**; with + the motor unpowered the nacelle back-drives under any residual moment. The + gravity term is nulled by the pivot-at-CG, but the **aero moment is + unquantified**, and an unquantified moment on a non-self-locking train is an + unbounded rate. Candidates, in preference order: motor short-brake held by + LibreServo_v4; a detent at the hover/cruise ends; a self-locking worm stage + (rejected on the plan-004 KTD1 right-angle argument unless nothing else + works). **Do not close by assuming the motor holds** — see + `docs/TILT_DRIVE_CONTROL_SPEC.md` §7.3 for the bench test that decides it. + **BLOCKS flight release.** + +- [ ] **WA-R17 — Split-collar pinch clamp (WA-R3) is still not a part.** The + Ø30.1 socket boss now matches the ~Ø30 clamp envelope WA-R3 specifies, but + the clamp itself has no SCAD, no STL and no BOM row. ≥ 5 mm wall over Ø20, + M3 heat-set inserts, 2 screws, **positive split gap when clamped** (if the + halves close on each other first the collar grips itself and the spar is + free), **no set screws** (CF tube splinters under a point load). This is the + joint that makes wing + bonded spar removable as one assembly. + **BLOCKS wing removal/refit.** + +### RESOLVED 2026-08-29 — no bay intrusion, and no decision needed + +The first revision of this section asked the owner to choose between intruding +36 mm into the bay, waiting on the LG-11 coupon, or splitting the socket into +two collars. **The owner instead ruled the bay clear, and that ruling produced a +better joint than any of the three options.** + +Splitting the load by TYPE rather than stretching one feature — socket for +shear, flange for moment — moves the moment off a 1/L² depth term onto a linear +area term: + +| | socket, 55 mm (was) | **socket 18.5 + flange 80 × 60 (now)** | +|---|---|---| +| Bay clear span | 140 → ~104 mm | **140 mm, unchanged** | +| Moment FOS | 4.02 | **29.2** | +| Reaches inboard to | X −136 | **X ≈ −86** | + +**The LG-11 coupon is demoted here** from a gate to a packaging convenience: +the flange gives FOS 29.2 / 87.6 / 274.6 at 5 / 15 / 47 MPa, so it clears the +FOS 4.0 target by 7× on the *standing* figure. The coupon would only decide how +small the flange could shrink. It remains a gate elsewhere (root `TODO.md` +§0.8). diff --git a/airframe/openscad/wings/wings_s1223_revo.scad b/airframe/openscad/wings/wings_s1223_revo.scad index ab9330e..4e90ee7 100644 --- a/airframe/openscad/wings/wings_s1223_revo.scad +++ b/airframe/openscad/wings/wings_s1223_revo.scad @@ -33,7 +33,12 @@ // - Span (WING_SEMI_SPAN = 85.7 mm) unchanged // Aerodynamic note: wing area reduced from 22 717 mm² to 19 025 mm² (both wings). // At 40 kt cruise: L ≈ 7.6 N vs. 9.1 N previously; still ~22 % AUW contribution. -// Re at root (40 kt, 129 mm chord) ≈ 177 000 — above S1223 operating floor. +// ^ SUPERSEDED AT REV T1 — this figure is for the true S1223 section and +// does NOT describe the built wing. See "SECTION DESIGNATION AND DATA". +// Re at root (40 kt, 129 mm chord) ≈ 182 000 (ISA SL, ν = 1.46e-5 m²/s); +// the chord is unchanged at Rev T1, so Re is unchanged. The older "177 000" +// figure here corresponds to ν ≈ 1.51e-5 (≈ 20 °C) and is not wrong, just +// stated at a different air temperature — both are LOW-Re (< 5e5). // Carried forward from Rev R (2026-06-11). // // Description @@ -48,10 +53,17 @@ // // Why S1223 // --------- -// At cruise (40 kts, Re ≈ 177 000 at root), the S1223 delivers CL ≈ 1.55 at -// 3° AoA versus CL ≈ 0.32 for the original flat-plate wing. Wing lift rises -// from ~3 % to ~22 % AUW at cruise — nearly a 7× improvement with no change to -// the outward planform silhouette. +// HISTORICAL — WHY S1223 WAS CHOSEN IN REV R1. At cruise (40 kts, Re ≈ 1.8e5 +// at root) the true S1223 delivers CL ≈ 1.55 at 3° AoA versus CL ≈ 0.32 for the +// original flat-plate wing, so wing lift rose from ~3 % to ~22 % AUW — nearly a +// 7× improvement with no change to the outward planform silhouette. +// +// ⚠ THIS NO LONGER DESCRIBES THE BUILT WING. Rev T1 scales the thickness +// envelope ×1.46 (root) and ×2.20 (tip) to swallow the Ø20.4 spar, so the built +// sections are S1223/t17.7 and S1223/t26.7, not S1223. The camber line — the +// source of the high-lift behaviour — IS preserved, which is the reason to +// expect the change to be less severe than the thickness numbers suggest, but +// no performance figure above transfers. See "SECTION DESIGNATION AND DATA". // Reference: Selig & Guglielmo (1997), "High-Lift Low Reynolds Number Airfoil // Design," Journal of Aircraft, Vol.34, No.1, pp.72–79. // UIUC Airfoil Database: https://m-selig.ae.illinois.edu/ads/afplots/s1223.gif @@ -69,16 +81,93 @@ // straight leading edge (zero sweep), wing-to-pylon mount pocket (WING_SLOT_W / // WING_SLOT_H), all structural hardware interface dimensions. // -// S1223 Profile Data -// ------------------ -// Coordinates from UIUC Airfoil Database (Selig & Guglielmo 1997). -// Normalised: x ∈ [0,1] (LE=0, TE=1), y = t/c ratio (positive = upper surface). -// Characteristics at design Re ≈ 100 000–200 000: -// Maximum thickness : 12.14% chord at 22.6% chord -// Maximum camber : 8.65% chord at 39.4% chord -// CL_max : ≈ 2.0 (Re=100k, from Selig & Guglielmo wind tunnel) -// CL at 3° AoA : ≈ 1.55 (Re=91k, interpolated) -// L/D at CL=1.0 : ≈ 30–35 +// SECTION DESIGNATION AND DATA — Rev T1 (re-derived 2026-08-29) +// ------------------------------------------------------------ +// ⚠ THE BUILT SECTION IS NOT A SELIG S1223. It shares S1223's CAMBER LINE and +// nothing else. Naming it "S1223" in analysis, in the BOM, or in a +// performance claim is the single most misleading thing this file could do, so +// the built sections carry their own designations: +// +// root S1223/t17.7 S1223 camber line, thickness envelope x1.46 +// tip S1223/t26.7 S1223 camber line, thickness envelope x2.20 +// +// (The filename keeps "s1223" for continuity of the git/STL/BOM trail. The +// SECTION does not.) +// +// GEOMETRY, DERIVED FROM THE TABULATED COORDINATES BELOW, NOT QUOTED +// ------------------------------------------------------------------ +// Computed by sampling S1223_UPPER/S1223_LOWER at 1/20000 chord. The camber +// line is NOT scaled (see s1223_section()), so camber is identical in all three +// columns and only the thickness envelope moves: +// +// baseline root built tip built +// thickness scale 1.000 1.460 2.200 +// max t/c 12.14 % 17.72 % 26.71 % +// at x/c 0.198 0.198 0.198 +// max camber 8.67 % 8.67 % 8.67 % +// at x/c 0.490 0.490 0.490 +// LE radius r/c 0.02502 0.05333 0.12110 +// LE radius, abs 3.23 mm 6.88 mm 11.26 mm +// (at c=129) (at c=129) (at c=93) +// +// CORRECTION TO THE PREVIOUS HEADER. It claimed "Maximum thickness : 12.14% +// chord at 22.6% chord" and "Maximum camber : 8.65% chord at 39.4% chord". +// Both MAGNITUDES were about right; both CHORDWISE LOCATIONS were wrong. The +// re-derivation above returns 19.8 % and 49.0 %, which is exactly the published +// characterisation of S1223 (max thickness 12.1 % at 19.8 %, max camber 8.7 % +// at 49.0 % — UIUC / airfoiltools, REF-CAD-006). The old figures were never +// traceable to the coordinate table this file actually builds from. +// +// LE radius scales as t_scale² — a consequence of scaling the thickness +// envelope of a fixed shape, verified numerically above (0.02502 × 1.46² = +// 0.05333; × 2.20² = 0.12110). The tip's leading edge is therefore 3.5× the +// baseline radius and 12 % of its own chord: geometrically it is closer to a +// strut fairing nose than to a low-Reynolds high-lift section. +// +// WHAT PUBLISHED S1223 DATA STILL APPLIES — AND WHAT DOES NOT +// ----------------------------------------------------------- +// Flow regime, stated first because it governs which methods are even legal: +// Re = V·c/ν at 40 kt (20.58 m/s), ν = 1.46e-5 m²/s (ISA SL, 15 °C): +// root (c 129 mm) Re ≈ 182,000 +// MAC (c 111 mm) Re ≈ 156,000 +// tip (c 93 mm) Re ≈ 131,000 +// All below Re = 5e5, i.e. the LOW-REYNOLDS regime where laminar separation +// bubbles dominate and published polars do not transfer between Reynolds +// numbers, let alone between sections. +// +// SURVIVES — the geometric camber figures. Camber is unscaled by +// construction, so max camber 8.67 % at 49.0 % chord is exact for the built +// sections, not an approximation. +// +// PARTIALLY SURVIVES, ROOT ONLY — zero-lift angle and lift-curve slope. +// Thin-airfoil theory makes dc_l/dα = 2π and α_(L=0) functions of the CAMBER +// LINE alone, independent of thickness, so preserving the camber line is the +// reason to expect the linear range to be broadly retained. But that theory +// is valid for THIN sections; at 17.7 % t/c the root is already outside its +// comfortable range and at 26.7 % the tip is emphatically outside it. Treat +// this as a reason for optimism at the root and as nothing at all at the tip. +// +// DOES NOT SURVIVE — every performance number. c_l,max, the L/D figure, the +// drag polar, and the stall behaviour all depend on the thickness +// distribution, the LE radius, and the separation bubble, and all three have +// changed substantially. c_l,max in particular comes only from measurement +// or computation at the actual Re; it cannot be carried over from a different +// section and it cannot be derived from theory. +// +// NOT CHARACTERISED AT ALL — the printed surface. FDM layer lines act as a +// de-facto trip strip whose effect at these Reynolds numbers is real and +// uncharacterised for this part. +// +// The Rev R1 claims below (CL ≈ 1.55 at 3° AoA, CL_max ≈ 2.0, L/D 30–35, and +// the 7.6 N cruise-lift figure derived from them) are RETAINED AS HISTORY ONLY +// and are marked at each use. They describe the true S1223 and MUST NOT be +// applied to the built wing. Establishing real numbers needs XFOIL or a +// transition-sensitive RANS run at Re 1.3e5–1.8e5, or a bench/tunnel result — +// tracked in TODO §0.8 and docs/flight_envelope.md. +// +// Reference: Selig, M.S. & Guglielmo, J.J. (1997), "High-Lift Low Reynolds +// Number Airfoil Design," Journal of Aircraft 34(1), 72–79; coordinates from +// the UIUC Airfoil Coordinates Database s1223.dat (REFERENCES.md REF-CAD-006). // // Coordinate System (internal / module geometry) // ----------------------------------------------- @@ -148,12 +237,33 @@ WING_DIHEDRAL = 0.0; // [mm] tip rise vs. root (+Z in output); Serenity ≈ // THICKNESS_SCALE=1.0 → full S1223 t/c (12.14%); reduce to thin down profile. // Reducing below 1.0 preserves camber ratio but reduces absolute thickness. // Set to 0.90 to lower max thickness from 7.9mm to 7.1mm at the root (subtle). -THICKNESS_SCALE = 1.0; // [1.0 = full S1223 t/c; 0.85–1.0 recommended range] +// REV T1 (2026-08-29, plan 003 KTD2): 1.00 → 1.46. THE ROOT OML NOW MOVES. +// Through every prior revision this stayed at 1.00 and only the TIP was +// thickened — the root had 15.60 mm of section against a Ø8.3 bore and was +// never the binding station. The unified Ø20.4 spar breaks that: at the +// 28.0 mm station the root section is 15.60 mm deep against a bore that needs +// 20.4 + 2 × 1.16 = 22.72 mm, so at 1.00 the bore BREAKS OUT of the root skin +// by 2.40 mm top and bottom. +// +// 1.456 is the exact solved figure (tools/wing_spar_station_fit.py --bore 20.4 +// --station 28, which now solves the ROOT as well as the tip for exactly this +// reason); 1.46 is used so the wall is not sitting on its own limit — the same +// convention Rev S1b (1.447 → 1.45) and U6 (1.5505 → 1.56) applied at the tip. +// It gives 1.19 mm over the bore against the 1.16 mm floor. +// +// AERO CONSEQUENCE — READ THIS BEFORE CITING ANY LIFT FIGURE FROM THIS FILE. +// Root t/c goes 12.14 % → 17.7 %. S1223 is a high-lift low-Reynolds section +// CHARACTERISED at 12.14 % t/c; scaling its thickness ~1.5× changes its +// behaviour qualitatively, not by a correction factor. The 7.6 N cruise-lift +// figure in this file's header, and everything derived from it, is therefore +// UNVERIFIED for the built section (plan 003 RISK-1). Do not present the +// re-lofted wing as an S1223 performance match until a CFD or bench result +// exists (tools/wing_cfd_openfoam.py is still blocked on mesh generation). +THICKNESS_SCALE = 1.46; // [1.0 = full S1223 t/c; ROOT-station thickness scale] // Camber line is NOT affected — only thickness offset. // Below 0.75: separation bubble risk at Re < 100k. - // This is the ROOT-station thickness scale; the tip - // uses THICKNESS_SCALE_TIP (below) so the 12 mm spar - // stays fully skinned in the thin outboard section. + // The tip uses THICKNESS_SCALE_TIP (below); the loft + // tapers linearly between the two. // ── Tip-station thickness (Rev R1a) ────────────────────────────────────────── // The tip chord (93 mm) at full S1223 t/c gives only ≈ 11.3 mm perpendicular @@ -199,22 +309,57 @@ THICKNESS_SCALE = 1.0; // [1.0 = full S1223 t/c; 0.85–1.0 recommended range // (tools/wing_cfd_openfoam.py, WIP). The camber-preservation argument does // not depend on it, but the absolute penalty of the thicker tip is not yet // quantified. -THICKNESS_SCALE_TIP = 1.56; // [tip thickness multiplier; root stays THICKNESS_SCALE] - -// ── Rotating tilt-spar bore (Rev R2 — unified 8 mm rotating spar) ──────────── -// UNIFIED ROTATING SPAR (2026-07-18): the wing's single spar is now the 8 mm -// rotating tilt-spar (AISI 4130, hollow 5 mm ID) — it is BOTH the wing -// structural spar AND the nacelle tilt axis, and it ROTATES (servo-driven from -// the cargo bay, keyed to the nacelle). The 12 mm fixed CF tube is retired. -// This bore is therefore a rotating CLEARANCE bore (not a press-fit): the wing -// is located by its fuselage root tab and rides on the spar at the wingtip -// bearing (§ wing_tip_bearing_seat); the second bearing lives in the cargo bay. +// REV T1 (2026-08-29, plan 003 KTD2): 1.56 → 2.20, forced by the same Ø20.4 +// unified spar that moved the root. 2.190 is the exact solved figure +// (tools/wing_spar_station_fit.py --bore 20.4 --station 28); 2.20 keeps the +// wall off its own limit per the convention above, giving 1.21 mm. +// Tip t/c goes 18.93 % → 26.7 %. See the RISK-1 note under THICKNESS_SCALE — +// it applies MORE strongly here, and this scale is also well outside the +// 0.85–1.0 range s1223_section()'s decomposition was written for, which is why +// tools/wing_airfoil_integrity.py now validates the sections at their ACTUAL +// scales rather than only validating the t_scale = 1.0 table (plan 003 RISK-2). +THICKNESS_SCALE_TIP = 2.20; // [tip thickness multiplier; root stays THICKNESS_SCALE] + +// ── Structural spar bore (Rev T1 — unified 20 mm FIXED CF spar) ───────────── +// UNIFIED FIXED SPAR (2026-08-29, plan 003 KTD1/KTD4). The spar stops being a +// rotating drive shaft and becomes a BONDED STRUCTURAL MEMBER OF THE WING: +// +// • It is a 20 × 16.3 mm roll-wrapped CARBON FIBRE tube, FIXED — it does not +// rotate. The nacelle now pivots on a trunnion ring carried at its own +// inboard face (plan 003 KTD3), so nothing about the tilt motion is +// transmitted through this tube. +// • Because it is fixed and bonded over its full span, it is the wing's +// PRIMARY BENDING MEMBER, not a shaft the wing merely rides on. That is +// the whole structural point of the change and it re-routes the root load +// path — see the WING ROOT LOAD PATH block below. +// • Its 16.3 mm bore carries the FOUR 10 AWG ESC feeds, coaxially with the +// tilt axis. This is why the tube is 20 mm and not 8 mm: it is sized by +// WIRE VOLUME, not by torque. 4 × Ø5.5 conductors circumscribe a 13.28 mm +// circle (exact 4-circle packing ratio 1 + √2, tools/spar_bundle_fit.py), +// so the 11.0 mm bore proposed in docs/plans/2026-08-27-nacelle-wiring-plan.md +// does not fit at all, and a 16 mm tube fits only as 16 × 14 with 0.36 mm +// radial — no room for the bundle to twist. +// +// WHY THE WIRES HAD TO COME ON-AXIS. The old double-D conduit sat 17.65 mm +// FORWARD of the tilt axis, so every nacelle transition dragged the harness +// through a ~45 mm arc (plan 003 Problem Frame; logged unclosed as U6 in +// docs/plans/2026-08-26-001-nacelle-esc-intake-integration-plan.md). On the +// axis, that sweep becomes distributed TORSION spread along the captive span +// instead of a swept arc at a fixed port. +// // The bore stays a SINGLE STRAIGHT cylinder at a fixed chordwise station // (parallel to the straight LE → no plan-view skew) centred on the airfoil -// CAMBER MIDLINE so it stays skin-enclosed as the section tapers. See -// docs/TILT_SPAR_ANALYSIS.md. (The nav-light 3-core routes INSIDE the hollow -// spar's 5 mm ID; the EDF power/signal keep the separate double-D cableway.) -SPAR_BORE_OD = 8.3; // [mm] rotating-spar clearance bore = 8 mm OD + 0.15 mm/side +// CAMBER MIDLINE so it stays skin-enclosed as the section tapers. +// +// CLEARANCE, NOT PRESS FIT — and deliberately so. 0.2 mm/side is a BONDING +// gap, not a rotating gap: the tube is epoxy-bonded into the wing (West System +// 105/206, the same system §6/§7 of docs/structural_analysis.md already +// specifies for CF stock in this airframe). A press fit into a printed +// CF-PETG bore would rely on interference against a part whose bore is the +// least dimensionally repeatable feature on it, and would put a hoop tension +// on the skin exactly where it is thinnest (1.19 mm). An adhesive-filled +// clearance loads the joint in shear over the full 85.7 mm span instead. +SPAR_BORE_OD = 20.4; // [mm] bonded-spar bore = 20 mm OD + 0.2 mm/side epoxy gap // REV S1b (2026-08-16): station moved 22.0 → 45.15 mm = 35 % ROOT chord, by // owner decision, to bring the nacelle tilt axis back toward its canonical // station. The 2026-07-19 reconciliation had slid the NACELLE forward to @@ -235,66 +380,151 @@ SPAR_BORE_OD = 8.3; // [mm] rotating-spar clearance bore = 8 mm OD + 0.1 // Cost, and why THICKNESS_SCALE_TIP moves with it: 45.15 mm is 35.0 % of root // chord but 48.5 % of TIP chord, well aft of S1223's ~20 % max-thickness // point. Verify any change with tools/wing_spar_station_fit.py. -SPAR_BORE_STATION = 45.15; // [mm] chordwise station aft of LE — CONSTANT over the - // span (35.0 % root / 48.5 % tip chord). +// +// REV T1 (2026-08-29, plan 003 KTD2): station moved 45.15 → 28.0 mm. This +// RE-OPENS the Rev S1b decision above, and does so on the record rather than +// quietly: S1b moved the spar aft partly to stop dragging the nacelle forward +// off its canonical station. THAT MOTIVE NO LONGER APPLIES — the nacelle now +// keeps its station because the pivot moves inside it (plan 003 KTD7/KTD8), +// so only the wing-INTERNAL station moves, and it moves back toward a line +// this wing was already built on before S1b. +// +// The station is forced by the airfoil. A Ø20.4 bore needs 22.72 mm of +// section depth, and S1223 loses depth fast aft of its ~20 % max-thickness +// point, so holding 45.15 mm would need a 40 % t/c tip — not an airfoil. +// Station 28.0 was selected over the thinner 22.0 because the station also +// moves HOVER GROUND CLEARANCE (the bore rides the camber midline, which sits +// lower forward): 28.0 buys +7.2 mm of clearance for 1.8 points of tip t/c, +// at constant canonical nacelle offset when paired with the ESC1 relocation +// (plan 003 KTD2/KTD8 three-way trade table). +// +// Bore inventory at this station, ALL constant-mm and therefore parallel +// (checked by tools/wing_internal_clearance.py, which is fail-closed on +// bore-to-bore convergence — the fault class Rev S1c was written for): +// 8.0 Ø3.2 nav-light 3-core conduit (web to spar 8.20 mm) +// 28.0 Ø20.4 THIS BORE spans 17.80 .. 38.20 +// 44.5 Ø6.5 AK7455 SPI + power conduit (web to spar 3.05 mm) +// 53.6 Ø4.4 nacelle tilt drive shaft (web to SPI 3.65 mm) +// No root-only bores remain: the tie-rod couple is retired on the default +// path (see the WING ROOT LOAD PATH block). +SPAR_BORE_STATION = 28.00; // [mm] chordwise station aft of LE — CONSTANT over the + // span (21.7 % root / 30.1 % tip chord). // Constant ⇒ bore ∥ leading edge ⊥ centreline. // The bore-centre thickness height is taken from the // ACTUAL S1223 camber midline at each station's own // chord fraction (midline_frac(), below) — NOT a single // constant — so it stays centred (root breakout fixed). -// ── Harness cableway (Rev S1c — moved FORWARD of the spar) ─────────────────── -// The 8 mm rotating spar is hollow (≈ 5 mm ID) — enough for the nav-light 3-core -// (Ø 2.5 mm), which routes THROUGH the hollow spar to the outboard nacelle nav -// light (95° tilt twist is benign — docs/TILT_SPAR_ANALYSIS.md §5), but NOT for -// the 40 A EDF ESC feeds. This dedicated wing conduit carries those: TWO -// parallel Ø CABLE_BORE_D bores forming a flat "double-D", camber-centred at -// each station. Splitting into two bores keeps the EDF power pair separate -// from ESC signal/telemetry (noise). -// -// REV S1c (2026-08-18) — WHY THIS MOVED, AND WHY IT IS NOW CONSTANT-mm: -// Rev S1b moved SPAR_BORE_STATION 22.0 → 45.15 mm but left this conduit on a -// constant chord FRACTION (0.48c). A constant-mm bore and a constant-fraction -// bore CONVERGE as the chord tapers 129 → 93 mm, so the pair that cleared at -// the root did not stay clear: the forward Ø7 conduit entered the Ø8.3 spar -// bore from 29.6 % span outboard, and at the tip BOTH conduits were merged -// with the spar (0.48 × 93 = 44.6 mm vs a spar at 45.15 mm). That is 40 A of -// EDF feed sharing a cavity with a rotating steel spar — a wiring fault, not a -// tolerance one. It was invisible to tools/wing_spar_station_fit.py, which -// sizes ONE bore against its section and cannot see bore-to-bore convergence; -// tools/wing_internal_clearance.py was written for exactly this class of fault -// and is fail-closed. -// -// The conduit had to go FORWARD of the spar: every station AFT of it fails, and -// not marginally — S1223 loses depth fast, and at the 58 mm station the TIP -// section is only 4.17 mm deep, so a Ø7 bore has negative wall before any -// thickening. Forward of the spar the section is near its max-thickness point -// at BOTH ends (27.5 mm = 21.3 % root / 29.6 % tip chord), which is why the -// walls there are ~4.5 mm instead of ~1.2 mm. -// -// CONSTANT MILLIMETRES, matching the spar's own law (see SPAR_BORE_STATION): -// with a straight LE (WING_SWEEP_LE = 0), two constant-mm bores hold the SAME -// chordwise separation at every span station, so the web cannot be eroded by -// taper. A chord-fraction conduit re-creates the convergence this change -// exists to remove. -CABLE_BORE_D = 7.0; // [mm] each conduit bore diameter (Ø7 ≈ 38 mm² each) -// REV S1c: separation 8.0 → 9.5 mm. At 8.0 the web between the two Ø7 bores -// was 1.0 mm — under two extrusions at the 0.6 mm nozzle WALL_T is specified -// for, so it would not have printed as a wall and the "double-D" would have -// fused into one cavity, defeating the power/signal split it exists for. -// 9.5 mm gives a 2.5 mm web = WALL_T. The chord forward of the spar affords -// it; this was only ever tight because the pair was crowded aft. -CABLE_BORE_SEP = 9.5; // [mm] chordwise centre-to-centre (overall ≈ 16.5 mm) -CABLE_BORE_STATION = 27.5; // [mm] chordwise station aft of LE — CONSTANT over the - // span, like SPAR_BORE_STATION. Conduits land at - // 22.75 / 32.25 mm. Clearances held over the whole - // span (tools/wing_internal_clearance.py): - // web to spar bore +5.25 mm - // skin, root / tip +4.45 / +4.84 mm - // gap to wingtip pad +1.40 mm - // The nacelle harness entry port must match this - // station — see HARNESS_PORT_Z in - // airframe/openscad/nacelles/nacelle_pod_50mm_tandem.scad. +// ── Nav-light / auxiliary signal conduit (Rev T1 — replaces the double-D) ──── +// THE 2 × Ø7 "DOUBLE-D" EDF POWER CONDUIT IS RETIRED (2026-08-29, plan 003 U2). +// Two independent reasons, either of which alone would end it: +// +// 1. It never actually carried the wiring it was named for. Two 10 AWG +// silicone conductors need ≥ 2 × 5.5 = 11.0 mm side by side; each bore +// was Ø7.0, so one bore held ONE conductor and the pair capped out at two +// of the four required. This was sized without ever checking wire OD +// against bore area (verified in plan 002 §A.1) — the conduit was a +// 40 A-labelled cavity that could not take the load it was labelled for. +// 2. Its 27.5 mm station is now INSIDE the Ø20.4 spar bore (17.80 .. 38.20). +// Even had it fitted the wire, it could not stay where it was. +// +// The ESC feeds now run inside the spar, on the tilt axis (see SPAR_BORE_OD). +// What is left needing a wing conduit is the WS2812C nav-light 3-core, and it +// gets its own bore rather than sharing the spar's: +// +// • The nav light is on the nacelle OUTBOARD face and rotates WITH the +// nacelle [REF-FAA-003 §91.209(a)], so its 3-core must cross the tilt +// joint. It crosses at the trunnion, not through the spar. +// • Under a FIXED spar the old plan-001 reasoning — "route the nav wire in +// the spar bore" — is VOID. That depended on the spar rotating WITH the +// nacelle so the wire twisted with it. The spar no longer rotates; the +// light still does. Putting the nav 3-core in the spar bore would now +// bond it to the non-rotating side AND sit it in the middle of four 40 A +// conductors, which is the exact co-location the whole routing decision +// exists to avoid (plan 003 R7). +// +// FORWARD of the spar deliberately: this is where the section is near its +// max-thickness point at BOTH ends (10.5 mm = 8.1 % root / 11.3 % tip chord), +// so the walls are ~7–9 mm rather than the ~1.2 mm they would be aft, and it +// puts the spar's grounded CF wall physically between this conduit and the +// power bundle inside it. +// +// CONSTANT MILLIMETRES, matching the spar's own law: with a straight LE +// (WING_SWEEP_LE = 0) a constant-mm bore holds the same chordwise separation +// at every span station, so the web cannot be eroded by taper. A chord- +// fraction conduit re-creates the convergence Rev S1c existed to remove. +NAV_BORE_D = 3.2; // [mm] conduit for the WS2812C 3-core (bundle Ø ≈ 2.5) +// STATION 8.0. The nav conduit must EXIT the tip face into the nacelle (the +// light rotates with the nacelle), so it must clear the tip pad — the exact +// failure Rev S1c found when a Ø29.5 disc capped the EDF double-D, which is a +// blocked harness, not a cosmetic overlap. The Rev T1 pad's forward edge is +// at X = 28 - TIP_PAD_R = 14.0; this conduit's aft edge is 9.6, clearing it by +// 4.40 mm. Forward is also the only side with room: the whole region aft of +// the spar is taken by the AK7455 conduit, the tilt drive shaft, and the root +// tenon. +NAV_BORE_STATION = 8.0; // [mm] chordwise station aft of LE — CONSTANT over span. + // Walls (tools/wing_spar_station_fit.py, at the + // Rev T1 thickness scales): root ±6.88, tip ±8.76. + // Web to the spar bore's forward edge: 8.20 mm. + // Gap to the tip pad's forward edge: 4.40 mm. + +// ── Nacelle tilt drive shaft bore (Rev T1) ────────────────────────────────── +// Plan 004 KTD1/KTD4. With the spar fixed, tilt torque can no longer travel +// down the spar, so a separate Ø4 mm steel shaft runs spanwise from the +// bulkhead servo to a spur pinion at the wingtip. It is a SHAFT and not a +// belt because the tilt axis IS the spanwise axis: a belt's pulley axes are +// perpendicular to its run, so a spanwise belt would need a bevel or worm +// stage at the tip purely to turn the axis 90°, in the most congested region +// on the airframe. A shaft lying parallel to the tilt axis needs no such +// stage — a plain spur pair transfers the motion directly (plan 004 trade +// study, Options A–D). +// +// STATION 53.6 — FIXED BY THE GEAR MESH, NOT A PACKAGING CHOICE. +// +// THE DRIVE IS A REDUCTION, NOT A STEP-UP (owner direction, 2026-08-29): the +// shaft turns MORE THAN ONE REVOLUTION to sweep the nacelle through 140°. That +// inverts the whole stage. An earlier pass here assumed a limited-rotation +// hobby servo (180°/270°), which forces a step-UP — the ring smaller than the +// pinion — and that is what produced an impossible geometry: +// +// The tilt ring gear is CONCENTRIC WITH THE SPAR, so its ROOT diameter must +// clear Ø20 plus a hub wall (≥ 26 mm). Under a step-up at plan 004 KTD4's +// C = 15 mm the algebra returns PD_ring = 10.5 mm — a ring gear SMALLER THAN +// THE SPAR IT ENCIRCLES. No tooth count fixes that. +// +// As a REDUCTION the ring is the LARGER member and the geometry closes easily. +// With i = N_ring/N_pinion, the shaft turns 140° × i: +// +// module 0.8, 14T pinion (PD 11.2 — 14T is the no-undercut floor at 20° PA) +// N_ring PD i shaft rev C station ring OD root Ø +// 36 28.8 2.571 1.000 20.00 48.00 30.4 26.8 +// 45 36.0 3.214 1.250 23.60 51.60 37.6 34.0 +// >> 50 40.0 3.571 1.389 25.60 53.60 41.6 38.0 << +// 54 43.2 3.857 1.500 27.20 55.20 44.8 41.2 +// +// **50T is selected.** 36T turns exactly one revolution, not "more than"; 54T +// pushes this bore to 55.2, leaving only 1.1 mm to the root tenon at 58.5 — +// under the 1.16 mm floor. 50T lands the shaft at 53.6 with 2.7 mm to the +// tenon and, more importantly, leaves the AK7455 pocket a 10.2 mm chordwise +// window between the spar bore's aft edge (38.2) and the shaft's forward edge +// (51.4). Ring OD 41.6 fits the 53.4 mm trunnion envelope (plan 003 OQ2) and +// its 38.0 mm root diameter clears the spar with 9 mm of hub wall each side. +// +// CONSEQUENCE FOR THE ACTUATOR — this is a real change, not a detail. A +// multi-turn output means the drive is NO LONGER a limited-rotation servo. It +// is a continuous-rotation gearmotor or a stepper, closed on the AK7455's +// absolute nacelle angle rather than on the actuator's own travel. That +// removes the 145°-of-travel constraint which plan 004 KTD5 found was the +// BINDING one (torque never was — the reduction now delivers 3.571 × whatever +// the actuator gives, against a 0.177 N·m grounded requirement), and it retires +// the 180°-vs-270° question entirely. Shaft torque is 0.050 N·m; wind-up over +// the installed length is 0.27° (Ø4 steel, G 79 GPa). +SHAFT_BORE_D = 4.4; // [mm] Ø4 shaft + 0.2 mm/side running clearance +SHAFT_BORE_STATION = 53.6; // [mm] chordwise station aft of LE — CONSTANT over span. + // = spar station 28.0 + centre distance 25.6. + // Walls: root ±6.1, tip ±3.5. + // Web to the AK7455 conduit: 3.65 mm. + // Gap to the root tenon (58.5): 2.70 mm. // ── Pylon mount pocket (must match wing_nacelle_pylon_revo.scad) ──────────── // The wing_attach_block from the pylon inserts into this pocket at the tip face. @@ -307,160 +537,266 @@ WING_BOLT_D = 3.4; // [mm] M3 clearance bore (nominal 3.0 mm + 0.4 cleara WING_BOLT_N = 4; // [count] bolt holes (45°/135°/225°/315°) WING_SLOT_X_CTR = 0.50; // [chord fraction] pocket centre chordwise at tip — VERIFY -// ── Wing-tip rotating-spar interface (Rev R2, 2026-07-18) ───────────────────── -// ROTATING TILT-SPAR mechanism — see docs/TILT_SPAR_ANALYSIS.md. -// • The 8 mm spar ROTATES (servo-driven from the cargo bay) and is FIXED to -// the nacelle. The wing is fixed; the spar spins inside it on two bearings -// (root bearing in the cargo bay + this wingtip bearing). -// • The wing tip therefore provides: (a) a rotating-clearance through-bore for -// the spar, (b) a wingtip BEARING SEAT (F688ZZ 8×16×5) that carries the -// wing/nacelle loads and lets the spar rotate, and (c) a FIXED R22 sector -// gear bolted coaxial with the spar (the SAME sector previously carried by -// the retired pylon — relocated to the fixed wingtip; nacelle gear housing -// unchanged). The nacelle's Pinion A orbits this fixed sector as the -// nacelle tilts → nozzle iris tracks tilt angle. (Gear-size study -// 2026-07-18: R22 kept over an R14+idler shrink — docs/TILT_SPAR_ANALYSIS.md -// §6; airframe/openscad/nacelles/gear_shell_compare.scad.) -// • The fixed gear stands only minimally proud (TIP_PAD_PROUD) and butts -// against the nacelle inboard face; the nacelle housing already clears -// Pinion A, so no pylon/block is needed. -// • NO nacelle boss socket (the nacelle is keyed to the spar, not pinned to a -// socket) — that feature is deleted vs Rev R1b. -// -// Rotating tilt spar (8 mm OD × 1.5 mm wall, 5 mm ID, AISI 4130 — docs §3): -TILT_SPAR_OD = 8.0; // [mm] rotating spar outer diameter -TILT_SPAR_BORE_CLEAR = 8.3; // [mm] rotating clearance bore through wing (0.15 mm/side) - -// Wingtip bearing seat (MF128ZZ flanged, 8 ID × 12 OD × 3.5 W): -// Rev R2d (2026-07-19): DOWNSIZED from F688ZZ (Ø16). The Ø16 seat radius -// (7.975 mm) exceeded the S1223 tip half-thickness (7.80 mm at the spar station, -// even at THICKNESS_SCALE_TIP = 1.25) and cut through BOTH airfoil skins by -// ~0.21 mm along the seat depth. MF128ZZ (Ø12, flange Ø13.5 → r6.75) seats with -// ~1.0 mm skin margin. Wingtip radial reaction ≈ 19 N (dyn) ≪ MF128 dynamic -// capacity (~700 N), so the downsize is load-safe. The ROOT (cargo-bay) bearing -// stays F688ZZ — no thin section there. docs/TILT_SPAR_ANALYSIS.md §8. -TIP_BRG_OD = 12.0; // [mm] bearing outer diameter -TIP_BRG_SEAT_D = 11.95; // [mm] press-fit seat bore (0.025 mm/side interference) -TIP_BRG_W = 3.5; // [mm] bearing width (seat depth) -TIP_BRG_FLANGE_OD = 13.5; // [mm] flange OD → shallow counterbore, seats flush -TIP_BRG_FLANGE_T = 1.0; // [mm] flange counterbore depth - -// Fixed R22 sector gear mount (coaxial with spar, on nacelle-facing tip face): -FIX_GEAR_BC_R = 11.0; // [mm] M2.5 insert bolt circle (outside 18 mm flange, inside 21 mm R22 gear root) -FIX_GEAR_N_BOLTS = 3; // [count] 120° spacing -FIX_GEAR_INSERT_OD = 3.7; // [mm] M2.5 heat-set insert bore -FIX_GEAR_INSERT_L = 5.5; // [mm] insert pocket depth -FIX_GEAR_PLATE_H = 3.0; // [mm] sector gear plate thickness (reference) - -// Wingtip mount pad — minimal boss around the spar at the tip face that houses -// the bearing seat + gear-bolt inserts. Kept low (butts against the nacelle -// inboard face) rather than standing on a tall block. -// REV S1c (2026-08-18): the pad is a TEARDROP, not a disc. As a Ø29.5 disc it -// spanned X 30.4..59.9 on the tip face and capped BOTH relocated EDF conduits — -// the conduits exit the tip face into the nacelle, so a pad over them is not a -// cosmetic overlap, it is a blocked harness. The disc OD was sized when the -// EDF pair sat AFT at 0.48c; with the pair now forward at 27.5 mm the disc can -// no longer be both small enough to clear them and large enough to host the -// AK7455 pocket at R = 11. -// -// A teardrop resolves it because the two things the pad must host are on -// OPPOSITE sides of the spar: the Ø13.5 bearing flange is concentric (needs -// r ≥ 6.75 all round) and the sensor pocket is purely AFT (needs reach to -// R ≈ 14.5). Hulling a small forward circle to a small aft one covers both -// without the forward sweep a disc would need. Forward edge lands at -// X = 37.15, clearing the aft EDF conduit (aft edge 35.75) by 1.40 mm. -// Proud only (Z > tip face) — sits in the nacelle inboard-face footprint; does -// not touch the exposed OML. -TIP_PAD_FWD_R = 8.0; // [mm] pad radius at the spar — Ø13.5 flange (r6.75) + 1.25 rim -TIP_PAD_AFT_R = 5.5; // [mm] pad radius at the sensor pocket centre (spar + HALL_SENS_R); - // reaches X 61.65, covering the 7 mm PCB seat (52.65..59.65) +// ── Wing-tip FIXED-spar trunnion interface (Rev T1, 2026-08-29) ────────────── +// THE WINGTIP'S JOB CHANGES COMPLETELY AT THIS REVISION. +// +// Rev R2 wingtip: a BEARING SEAT. The spar rotated inside the wing, so the +// wing had to journal it, and the nacelle was keyed to the spar's far end. +// Rev T1 wingtip: a CLAMP. The spar is fixed, so the wing's job is to GRIP it +// and the bearing moves to the NACELLE side of the joint, inside the trunnion +// ring (plan 003 KTD3 / plan 004 U2). A bearing here would now be actively +// wrong — it would let the spar spin under the drive-shaft's gear reaction, +// which is precisely what must not happen. +// +// This is also the structural termination of the wing's primary bending +// member, so the clamp is a load path, not a locating feature. See the WING +// ROOT LOAD PATH block for the beam model both ends belong to. +// +// Fixed structural spar (20 × 16.3 mm roll-wrapped CF — plan 003 KTD4): +TILT_SPAR_OD = 20.0; // [mm] fixed CF spar outer diameter +TILT_SPAR_BORE_CLEAR = 20.4; // [mm] bonded clearance bore through wing (0.2 mm/side) + +// ── HOW THE SPAR IS RETAINED — and why the clamp is at the ROOT, not here ──── +// Plan 003 U3 specified a split-collar pinch clamp at the WINGTIP. Two +// findings during implementation moved it to the root instead. Recording both +// rather than silently relocating it: +// +// 1. IT DOES NOT FIT. A collar that grips a Ø20 CF tube without crushing it +// needs ~5 mm of wall, i.e. Ø30 outside. The re-lofted TIP section is +// 22.83 mm deep at the spar station — its deepest point anywhere. A Ø30 +// collar cannot exist inside it at any thickness scale that is still an +// airfoil. (Same class of finding as Rev R2d's F688ZZ → MF128ZZ downsize, +// which was forced by exactly this section limit.) +// +// 2. IT WOULD HAVE NOTHING TO DO. The spar is EPOXY-BONDED into the wing over +// the full 85.7 mm span — bond area π × 20.4 × 85.7 = 5,492 mm². At the +// repo's 5 MPa figure that is ~27 kN of axial retention. A clamp cannot add +// to that, and a bonded spar cannot be withdrawn, so a clamp at this end +// could not make the joint serviceable either. A clamp is only meaningful +// at a joint that is meant to come apart. +// +// So: WING + BONDED SPAR ARE ONE SERVICEABLE ASSEMBLY. It separates from the +// aircraft at the FUSELAGE socket, by releasing the root clamp and withdrawing +// the spar outboard — which is how a light aircraft's spar-stub-into-socket +// wing attach works, and it satisfies plan 003 R10's intent at the joint that +// is actually designed to open. The clamp hardware therefore belongs with the +// fuselage-side socket; this file publishes the requirement +// (ROOT_SOCKET_REACH_MIN, above) and the fuselage owns building it. +// +// SPAR PROTRUSION PAST THE TIP FACE — BOUNDED BY THE THRUST DUCT, NOT CHOSEN. +// Owner requirement (2026-08-29): **the spar must not penetrate the nacelle +// thrust tube.** That is a hard geometric bound, and it is tighter than the +// bearing stack would like. +// +// The duct is a cylinder of r = 25 mm about the nacelle's local Z axis. The +// spar runs along local X at Y = 0, so every point of it at station X sits +// sqrt(X² + Y²) ≥ |X| from the duct axis. The spar therefore clears the duct +// **iff it TERMINATES at |X| ≥ 26** (25 + the 1 mm margin plan 003 R4 states). +// +// wing tip face |X| = 37.7 (NACELLE_OD_X/2) + 4.0 (joint gap) = 41.7 +// spar must stop |X| = 26.0 +// => MAXIMUM STUB 15.7 mm +// +// The 32.0 mm previously specified here would have reached |X| = 9.7 — fifteen +// millimetres INSIDE the duct wall, straight through the thrust column between +// the two EDFs. It was budgeted from the bearing stack outward and never +// checked against the duct, which is the same class of error as the Rev R2 +// through-duct spar this whole revision exists to remove. +// +// 15.0 is used, keeping 0.7 mm in hand. THE BEARING STACK MUST FIT INSIDE IT: +// 15 mm between |X| 41.7 and 26.7 carries a pair of thin-section bearings — +// 2 × 6804 (20 × 32 × 7) = 14.0 mm fits, and 6804 is already a BOM item +// (SKIPPER-BRG-6804). A deeper stack does not fit and must not be assumed. +SPAR_TIP_PROTRUSION = 15.0; // [mm] spar stub proud of the wing tip face — + // DUCT-BOUNDED (max 15.7). PUBLISHED + // JOINT REQUIREMENT; the nacelle must fit + // its trunnion bearing pair within it. + +// ── Wingtip service access (Rev T1) ────────────────────────────────────────── +// Plan 003 U3 also specified a wingtip "maintenance garage" housing the +// high-current bullet disconnects. MOVED TO THE NACELLE, with cause: +// +// The four 10 AWG feeds need ~6 mm of clear height for any disconnect +// (ring-terminal studs, bullets, or blade tabs). Aft of the spar the TIP +// section falls away fast — measured depths at THICKNESS_SCALE_TIP = 2.20: +// station 40 → 17.50, 54 → 11.20, 66 → 6.78, 78 → 3.43 mm. Subtracting +// 2 × WALL_T of skin leaves 12.5 mm at station 40 and 1.8 mm by station 66, +// over a chordwise run too short to lay four disconnects out in. There is no +// wingtip volume for this. +// +// The nacelle HAS that volume: plan 003's own wire-routing diagram already +// lands the power bundle in "the annular space between the duct wall (r = 25) +// and the outer skin" before it reaches ESC1/ESC2. That annulus is the right +// home for the disconnect, and putting it there also keeps the 40 A break out +// of the same pocket as the AK7455 plug — which is what +// docs/TILT_ENCODER_WIRING_EMI_SPEC.md §2.3 asks for and what a shared wingtip +// garage would have violated. Published to the nacelle plan this revision. +// +// What the wingtip keeps is access, and it needs no hatch to get it: sliding +// the nacelle off the spar exposes the whole tip face — the AK7455 board and +// its plug, the drive-shaft pinion, and the nav-light crossing. THE NACELLE IS +// THE COVER. That is the nacelle-off-spar service model this repo already +// adopted in docs/plans/2026-08-26-001-nacelle-esc-intake-integration-plan.md, +// and it is why plan 001 rejected a wingtip hatch. The only part of that +// rejection which does NOT survive Rev T1 is its reasoning about disturbing +// bearing seats — there is no longer a bearing here to disturb. + +// SUPERSEDED Rev T1 — the MF128ZZ wingtip bearing and its seat. Retained as +// named constants (NOT deleted) so the Rev R2d downsize record stays legible: +// F688ZZ (Ø16) was cut to MF128ZZ (Ø12) because the Ø16 seat radius exceeded +// the S1223 tip half-thickness and breached both skins. Under Rev T1 the +// question is moot — there is no wingtip bearing at all — but a future revert +// to a rotating spar would need this history, not a rediscovery of it. +// NOT REFERENCED by any module below. +TIP_BRG_OD = 12.0; // [mm] SUPERSEDED — MF128ZZ outer diameter +TIP_BRG_SEAT_D = 11.95; // [mm] SUPERSEDED — press-fit seat bore +TIP_BRG_W = 3.5; // [mm] SUPERSEDED — bearing width +TIP_BRG_FLANGE_OD = 13.5; // [mm] SUPERSEDED — flange OD +TIP_BRG_FLANGE_T = 1.0; // [mm] SUPERSEDED — flange counterbore depth + +// SUPERSEDED Rev R2b — fixed R22 sector-gear mount. The tilt→nozzle gear +// train was archived when the nozzle drive became a pushrod/bellcrank; plan +// 004 KTD3 now re-datums the nozzle sync gear onto the FIXED TRUNNION on the +// nacelle side, so no gear mounts on the wing tip under Rev T1 either. +// NOT REFERENCED by any module below. +FIX_GEAR_BC_R = 11.0; // [mm] SUPERSEDED — M2.5 insert bolt circle +FIX_GEAR_N_BOLTS = 3; // [count] SUPERSEDED — 120° spacing +FIX_GEAR_INSERT_OD = 3.7; // [mm] SUPERSEDED — M2.5 heat-set insert bore +FIX_GEAR_INSERT_L = 5.5; // [mm] SUPERSEDED — insert pocket depth +FIX_GEAR_PLATE_H = 3.0; // [mm] SUPERSEDED — sector plate thickness + +// ── Wingtip mount pad ──────────────────────────────────────────────────────── +// Low boss around the spar at the tip face, proud by TIP_PAD_PROUD only, that +// carries the split collar and the AK7455 pocket and butts against the nacelle +// inboard face. +// +// REV T1: the pad grows from the Rev S1c teardrop to a plain disc, and the +// reason the teardrop existed has gone away. S1c needed a teardrop because a +// Ø29.5 disc capped BOTH relocated Ø7 EDF conduits where they exited the tip +// face — a blocked harness, not a cosmetic overlap. Those conduits no longer +// exist (see the nav-conduit block), so nothing forward of the spar has to be +// kept clear, and the pad can simply be concentric with the collar it carries. +// The nav conduit at station 10.5 is 17.5 mm forward of the spar axis, outside +// the pad radius. +TIP_PAD_R = 14.0; // [mm] pad radius at the spar — Ø20.4 bore + + // 3.8 mm rim, enough to seat the nacelle + // trunnion register against a flat face. + // Forward edge X = 14.0, clearing the nav + // conduit's exit (aft edge 9.6) by 4.40. +TIP_PAD_SENS_R = 7.5; // [mm] pad radius at the AK7455 pocket centre. + // Covers the 10 × 8 board: half-diagonal + // sqrt(5² + 4²) = 6.40, + 1.1 rim. +TIP_PAD_SHAFT_R = 5.0; // [mm] pad radius at the drive-shaft exit. + // The shaft leaves the tip face at station + // 53.6, where the section is ~11.4 mm deep + // and the skin alone is no bushing seat. + // This lobe gives it a supported boss. + // Pad aft reach = 53.6 + 5 = 58.6 mm. TIP_PAD_PROUD = 2.0; // [mm] proud height beyond wing tip face (minimal) -// ── Wing/nacelle tilt-angle Hall sensor (Rev R2c, 2026-07-19) ───────────────── +// ── Wing/nacelle tilt-angle encoder — AKM AK7455 (Rev T1, 2026-08-29) ──────── // TRUE-NACELLE-TILT FEEDBACK. A magnetic angle encoder at the wing/nacelle -// joint closes the tilt-servo loop on the ACTUAL nacelle angle (output side), so -// the loop is independent of tilt-spar torsional wind-up -// (docs/TILT_SPAR_ANALYSIS.md §1, §3.5). This is the FIXED (sensor) half; the -// rotating diametric RING magnet is carried on the nacelle spar hub (see -// airframe/openscad/nacelles/_export_pivot_slab.scad). -// -// OFF-AXIS read: the spar is a THROUGH-shaft here (it continues into the nacelle -// — there is no free shaft end), so an on-axis end-of-shaft encoder cannot be -// used. A diametric RING magnet rides the spar on the nacelle and the sensor IC -// sits OFF-AXIS on this pad, under the ring annulus, facing it across a small -// axial air gap. Use an off-axis-capable IC (MPS MA732 / Magntek MT6701) — the -// on-axis AS5600 used on the antenna gimbal will NOT work through a shaft. -// -// STEEL-SPAR ISSUE (docs §3.5 / EMI-hardening WBS §1.4.6): the 4130/17-4 PH spar -// is ferromagnetic and runs through the ring centre, distorting the bias field. -// Geometry mitigations: (a) the ring is stood off the steel by a non-ferrous -// collar on the nacelle side (magnet ID 10 mm over the 8 mm spar); (b) the PCB -// seat and every fastener within HALL_KEEPOUT_R of the IC are NON-FERROUS -// (brass / 316 / Al / nylon — NOT the steel bearing screws); (c) a factory -// zero-cal over the full −5..90° sweep absorbs residual distortion (firmware — -// avionics). A ferrous shaft through a ring magnet is a supported commercial -// arrangement, but MUST be calibrated in situ. -// -// PLACEMENT (Rev R2d 2026-07-19; sensor part and pad revised Rev S1c 2026-08-18): -// SENSOR = AKM AK7455 (SPI, off-axis capable) on a small in-house PCB -// (ENC-NACELLE-1 / MAL-TILT-ENC-PCB; REFERENCES.md REF-SENSOR-008). It -// SUPERSEDES the Magntek MT6701 named in earlier revisions of this block — -// MT6701 and AS5600 were both rejected as on-axis parts, and the spar is a -// THROUGH-shaft here. -// -// The IC sits OFF-AXIS at HALL_SENS_R = 11 mm, CHORDWISE-aft of the spar, clear -// of the Ø13.5 bearing-flange keep-out (r6.75) on the pad face — the earlier -// R = 6 pocket collided with the flange/seat. The diametric ring is OD 22 -// (mean r ≈ 11) so the IC reads mid-annulus. Aft offset (not +Y) keeps the -// pocket in the wide chord direction, well under the top skin (Y-thickness is -// limited at the tip). -// -// REV S1c: the pocket is no longer THREADED between the bearing flange and the -// EDF double-D. That congestion existed only because the EDF pair sat aft at -// 0.48c; with the pair moved forward to the 27.5 mm station (see the harness -// cableway block) the whole region aft of the spar is free, and the pad's aft -// lobe (TIP_PAD_AFT_R) hosts the 7 × 7 board outright. A compact in-house -// board (AK7455 QFN + 2 decoupling caps + a direct-solder pigtail, no -// connector) fits at R = 11. -// -// The off-axis air-gap / ring geometry remains a REQUIRES-VERIFICATION item vs -// the AK7455 datasheet (REFERENCES.md; TODO §0.8) and is gated on the bench-cal -// in wings-nacelles WBS §1.1.3.6. -HALL_RING_OD = 22.0; // [mm] diametric ring-magnet OD (matches nacelle seat) -HALL_RING_ID = 10.0; // [mm] ring-magnet ID (rides non-ferrous collar on spar) -HALL_SENS_R = 11.0; // [mm] MT6701 IC offset from spar axis ≈ ring mean radius +// joint closes the tilt-servo loop on the ACTUAL nacelle angle (output side), +// so the loop is independent of drive-train compliance — shaft wind-up and +// spur backlash show up as hysteresis the encoder sees through, not as tilt +// error (plan 004 RISK-2). This is the FIXED (sensor) half; the rotating +// diametric RING magnet is carried on the nacelle trunnion. +// +// OFF-AXIS read, and it STAYS off-axis under Rev T1 for a NEW reason. Under +// Rev R2 the spar was a through-shaft into a keyed nacelle hub, so there was +// no free shaft end for an on-axis encoder. Under Rev T1 the spar is fixed +// and its bore is FULL OF THE FOUR 10 AWG POWER CONDUCTORS, so there is still +// no free end — and now there is 40 A of switching current where an on-axis +// sensor would have to sit. AS5600 and MT6701 remain rejected: both are +// on-axis-only parts (WBS §1.1.3.6, REF-SENSOR-008). AK7455 (SPI, +// off-axis-capable) remains correct — plan 003 KTD6. +// +// WHAT CHANGED AT REV T1, AND WHY EACH FIGURE MOVED +// +// 1. THE SPAR IS NO LONGER FERROMAGNETIC. Rev R2's whole magnetic-siting +// problem was a 4130/17-4 PH steel shaft running through the ring centre +// and distorting the bias field (docs/TILT_ENCODER_WIRING_EMI_SPEC.md §6.1; +// docs/TILT_SPAR_ANALYSIS.md §3.5). The Rev T1 spar is roll-wrapped CARBON +// FIBRE (plan 003 KTD4), which is not ferromagnetic, so that distortion +// source is REMOVED — not mitigated, removed. The non-ferrous keep-out +// below is RETAINED anyway, because it also governs the fasteners and the +// nacelle-side collar, which are still free to be steel if nobody says +// otherwise. The in-situ zero-calibration stays required: it now absorbs +// the drive-shaft and pinion (which ARE steel and ARE nearby), not the spar. +// NOTE this INVALIDATES §6.1's stated premise; that spec needs the same +// correction — flagged, not silently diverged from. +// +// 2. THE RING MAGNET HAS TO GROW. It rides a non-ferrous collar on the spar, +// and the spar went Ø8 → Ø20. A ring of ID 10 physically cannot pass over +// a Ø20 tube. ID 22 / OD 34 clears the spar plus a 1 mm collar wall, and +// fits inside the trunnion ring's measured 53.4 mm envelope (plan 003 OQ2). +// Mean radius 14.0 → HALL_SENS_R moves 11.0 → 14.0 so the IC still reads +// MID-ANNULUS. Reading off the mean radius is not a nicety: a diametric +// ring's field is only clean over the annulus, and an IC left at R = 11 +// would be reading 3 mm inboard of the magnet's inner edge, i.e. off it. +// +// 3. THE PCB SEAT WAS STILL SIZED FOR THE PART THAT WAS REJECTED. HALL_PCB_W/H +// were 7.0 × 7.0, dimensioned around the MT6701's 3 × 3 mm QFN — a part +// rejected on 2026-07-19, over a year of revisions ago. AK7455 is a QFN24 +// 4 × 4 mm package; with its decoupling passives and a 7-way direct-solder +// pigtail landing with strain relief, the real board is 12 × 10 mm. This +// closes the open item at airframe/wings-nacelles/WBS.md §1.1.3.6. +// +// 4. THE CONDUIT WAS SIZED FOR 4-WIRE I2C. AK7455 is SPI: CS, CLK, MOSI, MISO +// plus +3V3/GND plus ERROR = 7 conductors, and TILT_ENCODER_WIRING_EMI_SPEC +// §2.1 specifies them as TWO separately-shielded cables — a 28 AWG shielded +// quad and a 24 AWG shielded pair. Ø3.5 carries neither pair of cables. +// Ø6.5 carries both side by side with pull-through clearance. +// +// DOCUMENTED DEVIATION: §2.3 asks for ≥ 15 mm between the signal group and +// the power group. Inside a single printed wing conduit that is not +// achievable at any bore this section can hold, and splitting it into two +// 15-mm-separated conduits would need a second bore in the shallow aft +// section where the Ø7 EDF conduits already could not hold wall. Both +// groups are 100 % braid-shielded per §2.1, which is the actual mitigation; +// the 15 mm rule guards UNSHIELDED proximity. Separation from the 40 A +// feeds — the clearance that matters — is now far BETTER than Rev S1c's, +// because those conductors are inside the spar's own grounded CF wall, +// 26 mm forward, instead of in an open conduit 9 mm away. +HALL_RING_OD = 41.2; // [mm] diametric ring-magnet OD (matches nacelle trunnion seat) +HALL_RING_ID = 26.0; // [mm] ring-magnet ID (clears Ø20 spar + 3.0 mm non-ferrous collar) +HALL_SENS_R = 16.8; // [mm] IC offset from spar axis = ring MEAN radius + // (26 + 41.2) / 4 = 16.8 — the IC reads mid-annulus HALL_AIR_GAP = 1.5; // [mm] axial magnet-face → IC-face gap (set by nacelle standoff) -HALL_PCB_W = 7.0; // [mm] sensor PCB seat width (chordwise, X) — fits the flange↔EDF - // gap; MT6701 3×3 QFN + caps + direct-solder pigtail -HALL_PCB_H = 7.0; // [mm] sensor PCB seat height (thickness, Y) — clears top skin - // (pocket top Y≈12.4 vs ceiling ≈16.4 at this station) +HALL_PCB_W = 9.0; // [mm] sensor PCB seat width (chordwise, X) — AK7455 QFN24 + // 4×4 + decoupling + 7-way pigtail landing. + // Spans X 40.3..49.3: 2.10 mm clear of the spar + // bore (aft edge 38.2) and 2.10 mm clear of the + // drive-shaft bore (fwd edge 51.4). Those two + // gaps are what set HALL_SENS_R and the gear's + // centre distance; none of the three is free. + // The whole aft-of-spar window is 10.2 mm wide. +HALL_PCB_H = 8.0; // [mm] sensor PCB seat height (thickness, Y) HALL_PCB_SEAT_T = 2.0; // [mm] PCB + solder recess depth into the pad face HALL_PCB_SCR_D = 1.7; // [mm] M2 self-tap pilot (2×, chordwise ±HALL_PCB_SCR_S) -HALL_PCB_SCR_S = 2.5; // [mm] screw pilot half-spacing (chordwise) — within W/2 span +HALL_PCB_SCR_S = 3.0; // [mm] screw pilot half-spacing (chordwise) — within W/2 span HALL_KEEPOUT_R = 10.0; // [mm] NON-FERROUS keep-out radius around the IC -HALL_CABLE_D = 3.5; // [mm] 4-wire I²C sensor conduit (VCC/GND/SDA/SCL, shielded) +HALL_CABLE_D = 6.5; // [mm] AK7455 conduit: shielded 28 AWG SPI quad + + // shielded 24 AWG power pair (EMI spec §2.1) // REV S1c (2026-08-18): 0.33c → a CONSTANT 54.0 mm station, AFT of the spar. -// The old fraction was sized against a spar at the 22.0 mm station and was left -// behind by Rev S1b: 0.33 × 129 = 42.57 mm put this Ø3.5 conduit INSIDE the -// Ø8.3 spar bore at the ROOT (conduit 40.82..44.32 vs spar 41.00..49.30) — the -// mirror image of the EDF failure above, converging inboard instead of outboard. -// -// It stays AFT of the spar deliberately, where the EDF double-D could not go. -// A Ø3.5 sensor bore needs far less depth than a Ø7 power conduit, and 54.0 mm -// is the station where BOTH constraints close: a 2.95 mm web to the spar bore -// (≥ WALL_T) and 1.28 mm of tip skin — thicker than the 1.16 mm the root spar -// already runs at and prints, which is the floor tools/wing_spar_station_fit.py -// enforces. Keeping it aft preserves the original EMI intent in its strongest -// form: the ferromagnetic spar sits physically BETWEEN the shielded low-level -// sensor pair and the 40 A EDF feeds (EMI-hardening WBS §1.4.4/§1.4.6), rather -// than trading that barrier for a printed web, which is what any forward -// placement would have done. -// -// Its tip end (X = 54.0) also sits just aft of the Ø11.95 bearing seat -// (aft edge X ≈ 51.1), shortening the jog to the R = 11 sensor pocket (X ≈ 56.15) -// to ~2.2 mm. -HALL_CABLE_STATION = 54.0; // [mm] chordwise station aft of LE — CONSTANT over the - // span, like SPAR_BORE_STATION and CABLE_BORE_STATION +// REV T1 (2026-08-29): 54.0 → 44.0. Still aft of the spar, but now BETWEEN +// the spar and the tilt drive shaft rather than outboard of everything, and +// the move is forced rather than chosen: the drive shaft's gear-mesh centre +// distance puts IT at station 53.6 (see SHAFT_BORE_STATION), and two bores +// cannot share a station. 44.5 balances the surviving window — 3.05 mm of web +// to the spar bore and 3.65 mm to the shaft, both above the 2.5 mm WALL_T +// minimum, with walls root ±6.5 / tip ±4.5, comfortably above the 1.16 mm +// floor. 44.0 also passes but leaves only 0.05 mm of margin on the spar-side +// web, and there is no reason to spend it. +// +// The EMI reason for staying aft is UNCHANGED IN INTENT but has a different +// mechanism now. Under Rev S1c the barrier between this shielded low-level +// pair and the 40 A feeds was the ferromagnetic steel spar sitting between +// them. Under Rev T1 the feeds are INSIDE the spar, so the barrier is the +// spar's own grounded CF wall wrapped all the way around them — a closed +// shield rather than an interposed obstacle, and a strictly stronger one. +HALL_CABLE_STATION = 44.5; // [mm] chordwise station aft of LE — CONSTANT over the + // span, like SPAR_BORE_STATION and SHAFT_BORE_STATION // Spar-axis thickness (internal Y) height at the TIP station — the spar exits on // the camber midline, NOT the chord line, so all wingtip spar features (pad, @@ -475,40 +811,124 @@ HALL_CABLE_STATION = 54.0; // [mm] chordwise station aft of LE — CONSTANT over function spar_tip_y() = midline_frac(SPAR_BORE_STATION / WING_CHORD_TIP) * WING_CHORD_TIP; -// ── Wing root joint load path (KTD1/U5, 2026-08-24 refinement) ─────────────── -// CARGO-03c found the tenon under-strength as a STRUCTURAL joint (10.14 MPa -// bearing at ultimate vs. the repo's only CF-PETG figure, 5 MPa bond-limited, -// docs/structural_analysis.md §7.3 — FOS 0.49) and, per the owner's -// <15 MPa-fusion-strength decision rule, routed the load to a bonded CF rod -// instead of growing the tenon. A feasibility pass then found a SINGLE rod -// forward of the main spar works (Ø8.2 mm at 14 mm from LE) but a matching -// Ø8.2 mm AFT rod does not fit anywhere aft of the spar (the main spar bore's -// aft edge at 49.30 mm and the Hall/encoder conduit's fixed Rev S1c span -// 52.25..55.75 mm leave no room at any station without breaking the wall or -// the skin). A smaller Ø6.2 mm aft rod DOES fit, aft of the Hall conduit — -// see ROD_AFT_* below. So the tenon is now traded out of the load path -// entirely and replaced by a TWO-ROD couple (not a single rod vs. the tenon). +// ── WING ROOT LOAD PATH (Rev T1, 2026-08-29) ──────────────────────────────── +// THIS IS THE STRUCTURAL CONSEQUENCE OF THE SPAR BECOMING A WING MEMBER. +// +// Under Rev R2 the spar was a ROTATING drive shaft riding bearings at both +// ends. A bearing transmits shear, not moment, so the spar was structurally +// incapable of reacting the wing-root moment — and every root-joint design in +// this file's history existed to work AROUND that: +// +// CARGO-03c enlarged tenon alone reacts the couple FOS 0.49 FAILED +// U5 / KTD1 two bonded CF tie rods react it as a couple FOS 4.14 passed +// +// Rev T1 removes the constraint at its source. The spar is FIXED and bonded +// over its full span, so it carries moment, and the load path is simply: +// +// nacelle → wingtip trunnion → SPAR → fuselage socket +// +// The wing skin carries only its own local air load into the spar. The tenon +// and the tie rods are no longer in the path at all. This is not a stronger +// version of the old joint — it is a different joint, and it is why the couple +// arm improves from 48 mm (chordwise rod spacing) to the full 86.7 mm span. +// +// LOADS (tools/wing_spar_carrythrough.py, measured from the baked STLs): +// ultimate root moment 14.60 N.m (4 g gust+maneuver × 1.5) +// ultimate root shear 115.1 N +// spar bending, cantilever bound 33.28 MPa → FOS 9.0 vs the 300 MPa +// cross-ply stand-in (UNVERIFIED — DEP-1) +// spar bending, two-support bound 10.04 MPa → FOS 29.9 +// The CANTILEVER bound is the quoted one: it does not depend on the printed +// skin sharing load, which is not characterised. +// +// WHY THE TIE RODS RETIRE RATHER THAN STAYING AS BACKUP: +// 1. The forward rod is geometrically impossible. It sat at station 14.0 at +// Ø8.2 (9.9..18.1); the Ø20.4 spar bore spans 17.80..38.20. They +// overlap. There is no clearance version of the old joint. +// 2. The aft rod fits but has no remaining job. Its only candidate duty +// would be wing TORSION about the spar axis, and that is 0.41 N.m at +// ultimate — bond shear 0.016 MPa, FOS 306. Thrust adds none: the duct +// axis passes through the pivot, which is on the spar axis. +// 3. An unnecessary bonded rod is not free — it is a second bonded interface +// competing for root volume with the spar socket and a stress riser in +// the skin at the station where the section is thinnest. +// +// REQUIREMENT PUBLISHED TO THE FUSELAGE — REVISED 2026-08-29 (owner direction: +// **the centre of the cargo bay must remain clear**). +// +// An earlier pass here asked the fuselage for a 55 mm-deep bonded socket. That +// is now ruled out: the bay's clear span begins at hull X −100 and the wall +// skin is at −81.33, so only **18.67 mm** of socket depth exists before the +// spar enters the bay. A 55 mm socket would have reached X −136. +// +// 18.67 mm is not enough for the moment, and not close: +// F = 3M/(2L) + V/2 = 1,231 N over D·L/3 = 124 mm² → 9.89 MPa, FOS 0.51 +// The stress goes as 1/L², so depth is the only lever a socket has, and the +// bay has taken it away. **The socket therefore stops being the moment path.** +// +// THE JOINT SPLITS IN TWO, each part carrying what it is actually good at: +// +// 1. SHEAR — the Ø20.4 socket, 18.5 mm deep, entirely inside the sidewall. +// σ = 115.1 / (20 × 18.5) = 0.31 MPa → **FOS 16**. Shear was never the +// problem; it does not need depth. +// +// 2. MOMENT — a bonded ROOT FLANGE on the INNER FACE of the sidewall. The +// moment is reacted over wall AREA instead of socket DEPTH, so it needs +// no inboard reach at all: the flange lies flat against the wall and +// protrudes only its own thickness (~5 mm, to X ≈ −86). Triangular +// pressure over the flange height h, arm 2h/3: +// h 60 × w 50 → 365 N over 1,000 mm² → 0.37 MPa, FOS 13.7 +// h 80 × w 60 → 274 N over 1,600 mm² → 0.17 MPa, **FOS 29.2** +// **80 × 60 mm is specified.** The cargo section is ~150 mm tall inside +// at this station, so 80 mm of height is available without crowding. +// +// This is strictly better than the 55 mm socket it replaces — FOS 29 instead +// of 4.0 — because a flange trades an unfavourable 1/L² depth term for a linear +// area term. The bay is untouched: nothing goes inboard of X ≈ −86, against a +// bay edge at −100. +// +// The tube-to-flange transfer is the wing's own 85.7 mm bond, not a fitting: +// the spar is bonded through the wing root, and the flange is clamped to the +// spar at the wall by the same split collar that makes the joint releasable. // // TENON_LOAD_PATH selects which joint reacts the wing-root couple: -// "two_rod" (DEFAULT) — two bonded CF rods (below) react the couple; -// the tenon is reduced to a locating/index feature only. -// "enlarged_tenon" — the tenon alone reacts the couple, grown to the -// airframe's measured maximum envelope (39.2 x 20.1 mm, -// tools/wing_root_deconflict.py max_tenon_envelope()). -// Requires a CF-PETG fusion/bearing coupon test clearing -// >= 15 MPa (root TODO.md §1.1.4 LG-11) — NOT yet -// available, so this path is documented but NOT default. -// Kept as a named constant, not deleted, per KTD1: a future coupon result -// >= 15 MPa can swap the joint back to the tenon without re-deriving the -// sizing table. -TENON_LOAD_PATH = "two_rod"; // ["two_rod" | "enlarged_tenon"] - -// ── Wing root fuselage tab (locating/index feature under "two_rod"; full -// structural tenon under "enlarged_tenon") ──────────────────────────────── +// "spar_carrythrough" (DEFAULT, Rev T1) — the fixed bonded spar reacts the +// couple into a fuselage socket; the tenon is a locating +// feature and no tie rods are cut. +// "two_rod" (SUPERSEDED, Rev S1d) — two bonded CF tie rods react the +// couple. Retained and still renderable, but note the +// forward rod now INTERSECTS the Ø20.4 spar bore, so this +// path is geometrically invalid at the Rev T1 spar and is +// kept only as the design record. +// "enlarged_tenon" (SUPERSEDED, CARGO-03c) — the tenon alone reacts the +// couple at the measured maximum envelope. Requires a +// CF-PETG fusion/bearing coupon clearing >= 15 MPa +// (root TODO.md §1.1.4 LG-11) — not available. +TENON_LOAD_PATH = "spar_carrythrough"; // ["spar_carrythrough" | "two_rod" | "enlarged_tenon"] + +// Fuselage-side figures this wing requires (published interface — the wing does +// not build them; see the REQUIREMENT block above). +ROOT_SOCKET_REACH = 18.5; // [mm] spanwise, inboard of the wall. BOUNDED by + // the cargo bay (clear span starts at hull + // X −100; wall skin −81.33), not chosen. + // Carries SHEAR only, at FOS 16. +ROOT_FLANGE_H = 80.0; // [mm] bonded root-flange height (hull Z extent) +ROOT_FLANGE_W = 60.0; // [mm] bonded root-flange width (hull Y extent) + // Together these carry the MOMENT at FOS 29.2 + // against the 5 MPa bond-limited figure. + +// ── Wing root fuselage tab (locating/index feature under "spar_carrythrough" +// and "two_rod"; full structural tenon under "enlarged_tenon") ──────────── // The root face (Z=0) must interface with the fuselage wing slot. // VERIFY slot dimensions in fuselage hull STL before printing. // -// "two_rod" (default): the two CF rods below react the entire wing-root +// "spar_carrythrough" (DEFAULT, Rev T1): the tab's job is UNCHANGED from the +// "two_rod" case below and for the same reason — something else reacts the +// couple, so the tab locates and does not carry moment. Only the "something +// else" changed, from two bonded rods to the spar itself. The sizing is +// therefore identical and is shared rather than duplicated. +// +// "two_rod" (superseded): the two CF rods below react the entire wing-root // couple, so the tab reverts to ITS OWN documented job — radial restraint // and rotational location, not moment reaction. Only W and L shrink: those // are the two dimensions that actually appear in the bearing-stress formula @@ -543,11 +963,17 @@ WING_ROOT_TAB_W_ENLARGED = 30.0; // [mm] VERIFY — fuselage slot width in X WING_ROOT_TAB_H_ENLARGED = 20.0; // [mm] VERIFY — fuselage slot height in Y WING_ROOT_TAB_L_ENLARGED = 12.0; // [mm] VERIFY — insertion depth into slot -WING_ROOT_TAB_W = (TENON_LOAD_PATH == "two_rod") ? +// The tab is a LOCATING feature on every path except "enlarged_tenon", which +// is the only one where the tenon itself is the moment path. Written as +// "not enlarged_tenon" rather than an explicit list so a future fourth path +// inherits the safe (locating) sizing by default instead of silently picking +// up the structural tenon. +TAB_IS_LOCATING = (TENON_LOAD_PATH != "enlarged_tenon"); +WING_ROOT_TAB_W = TAB_IS_LOCATING ? WING_ROOT_TAB_W_LOCATING : WING_ROOT_TAB_W_ENLARGED; -WING_ROOT_TAB_H = (TENON_LOAD_PATH == "two_rod") ? +WING_ROOT_TAB_H = TAB_IS_LOCATING ? WING_ROOT_TAB_H_LOCATING : WING_ROOT_TAB_H_ENLARGED; -WING_ROOT_TAB_L = (TENON_LOAD_PATH == "two_rod") ? +WING_ROOT_TAB_L = TAB_IS_LOCATING ? WING_ROOT_TAB_L_LOCATING : WING_ROOT_TAB_L_ENLARGED; // ── Wing root tie-rod couple (U5, "two_rod" path) ───────────────────────────── @@ -921,45 +1347,61 @@ module spar_bore() { // ============================================================================= -// ── Module: cableway_bore ──────────────────────────────────────────────────── +// ── Module: nav_bore — nav-light / auxiliary signal conduit ────────────────── // ============================================================================= -// Two parallel spanwise conduits (a flat "double-D") for the nacelle harness — -// the 40 A EDF power feeds cannot fit the spar-tube ID (see parameter block). -// -// REV S1c (2026-08-18): the pair sits at a CONSTANT chordwise station -// (CABLE_BORE_STATION), FORWARD of the spar, replacing the constant chord -// FRACTION that converged onto the spar bore after Rev S1b moved the spar aft. -// Because the station is now constant in millimetres — the same law the spar -// uses — root and tip share one X, so the conduits run truly parallel to the -// spar and the web between them cannot be eroded by chord taper. Each is still -// camber-centred at its own end station via midline_frac(), so the pair follows -// the camber line as the section changes. Runs full span (root face into the -// fuselage, tip face into the nacelle harness port). -module cableway_bore() { - // Chordwise conduit centre — one constant station, root and tip alike. - xc = CABLE_BORE_STATION; - // Camber-centred at each end. No thickness-scale factor: s1223_section() - // scales thickness about the camber line, so the camber line is unscaled - // (Rev S1b) and re-applying the scale here would lift the bore off it. +// Single spanwise conduit FORWARD of the spar, carrying the WS2812C nav-light +// 3-core from the fuselage to the wing tip, where it crosses the tilt joint at +// the trunnion (the light rotates with the nacelle; the spar does not). +// +// REPLACES cableway_bore(), the 2 × Ø7 EDF "double-D" — retired Rev T1. See +// the NAV_BORE_* parameter block for why: it could not carry two 10 AWG +// conductors at any realistic wire OD, and its 27.5 mm station now falls +// inside the Ø20.4 spar bore. +// +// Camber-centred at each end station via midline_frac(), so it follows the +// camber line as the section changes. No thickness-scale factor: +// s1223_section() opens the thickness envelope about an UNSCALED camber line +// (Rev S1b), so re-applying the scale here would lift the bore off it. +module nav_bore() { + xc = NAV_BORE_STATION; root_yc = midline_frac(xc / WING_CHORD_ROOT) * WING_CHORD_ROOT; tip_yc = midline_frac(xc / WING_CHORD_TIP) * WING_CHORD_TIP; - // NO ROOT-TENON PASS-THROUGH IS NEEDED ANY MORE (Rev S1c). The Rev R2d/R2e - // straight tenon drill existed because the double-D at 0.48c landed at - // X ≈ 57.9 / 65.9, inside the fuselage_root_tab (X 49.5..79.5), and the - // wires dead-ended in solid tenon. At the 27.5 mm station the conduits are - // at X 22.75 / 32.25 — entirely FORWARD of the tenon — so they exit the root - // face in clear air and the tenon crown is left intact. The sensor conduit - // inherits the tenon problem instead; see hall_sensor_cableway(). - for (dx = [-CABLE_BORE_SEP / 2, CABLE_BORE_SEP / 2]) { - // Spanwise conduit: root face (−1 mm) → 1 mm past the wing tip, so it - // breaks cleanly out of the tip face into the nacelle harness port. - hull() { - translate([xc + dx, root_yc, -1.0]) - cylinder(r = CABLE_BORE_D / 2, h = 0.01); - translate([xc + dx, tip_yc + WING_DIHEDRAL, WING_SEMI_SPAN + 1.0]) - cylinder(r = CABLE_BORE_D / 2, h = 0.01); - } + // Root face (−1 mm) → 1 mm past the wing tip, so it breaks cleanly out of + // the tip face clear of the pad (pad forward edge X = 12.0; this bore's + // aft edge X = 10.0). + hull() { + translate([xc, root_yc, -1.0]) + cylinder(r = NAV_BORE_D / 2, h = 0.01); + translate([xc, tip_yc + WING_DIHEDRAL, WING_SEMI_SPAN + 1.0]) + cylinder(r = NAV_BORE_D / 2, h = 0.01); + } +} + + +// ============================================================================= +// ── Module: tilt_shaft_bore — nacelle tilt drive shaft ────────────────────── +// ============================================================================= +// Spanwise running-clearance bore for the Ø4 mm steel tilt drive shaft (plan +// 004 KTD1/KTD4). Runs the full span: bulkhead servo → wingtip pinion. +// +// It exits the tip face UNDER the mount pad (pad spans X 12.0..50.5; this bore +// is at 43.0), which is deliberate — the pad gives the shaft's outboard bushing +// a thick, supported boss to run in instead of a 1.2 mm skin. +// +// Same constant-mm law as every other bore in this wing, so it stays parallel +// to the spar over the whole span and the webs cannot be eroded by taper. +module tilt_shaft_bore() { + xc = SHAFT_BORE_STATION; + root_yc = midline_frac(xc / WING_CHORD_ROOT) * WING_CHORD_ROOT; + tip_yc = midline_frac(xc / WING_CHORD_TIP) * WING_CHORD_TIP; + + hull() { + translate([xc, root_yc, -1.0]) + cylinder(r = SHAFT_BORE_D / 2, h = 0.01); + translate([xc, tip_yc + WING_DIHEDRAL, + WING_SEMI_SPAN + TIP_PAD_PROUD + 1.0]) + cylinder(r = SHAFT_BORE_D / 2, h = 0.01); } } @@ -996,26 +1438,40 @@ module pylon_mount_pocket() { // ============================================================================= // ── Module: wing_tip_nacelle_mount_pad ──────────────────────────────────────── // ============================================================================= -// Minimal boss at the wing tip face, centered on the tilt spar, that houses the -// wingtip bearing seat and the fixed-gear bolt inserts. Kept LOW (TIP_PAD_PROUD) -// so the fixed R14 sector gear nests into the nacelle inboard recess rather than -// standing on a tall block — preserves the canonical nacelle silhouette. -// Additive only; the bearing seat / bore / insert pockets are cut separately. +// Low boss at the wing tip face, centred on the spar, that provides: +// • a flat register face for the nacelle trunnion to butt against, +// • the seat for the AK7455 encoder board (aft lobe), and +// • a thick, supported boss for the tilt drive shaft's outboard bushing, +// which passes through the pad at station 43 rather than through 1.2 mm +// of skin. +// Kept LOW (TIP_PAD_PROUD) — it sits in the nacelle inboard-face footprint and +// does not touch the exposed outer mould line. +// +// REV T1: a THREE-lobe hull, not Rev S1c's two. S1c's teardrop existed to +// keep the pad's forward sweep off the two Ø7 EDF conduits that exited this +// face; those conduits are gone, but the shape survives — and gains a lobe — +// because everything the pad hosts now lies AFT of the spar in a line: +// lobe A X 28.0 r 14.0 trunnion register, concentric with the spar +// lobe B X 44.8 r 7.5 AK7455 board seat (HALL_SENS_R = 16.8) +// lobe C X 53.6 r 5.0 drive-shaft bushing boss +// Hulling them gives a tapering fin that reaches X 59 aft while its FORWARD +// edge stays at X 14.0 — which is the constraint that matters, because the nav +// conduit exits this same face at X 6.4..9.6 and a pad that reached it would +// be a blocked harness. A plain disc large enough to cover lobe C would have +// a forward edge at X = -3 and cap the nav conduit outright. +// Additive only; the spar bore, shaft bore and sensor pocket are cut separately. module wing_tip_nacelle_mount_pad() { spar_x = WING_SWEEP_LE + SPAR_BORE_STATION; // spar chordwise position - spar_y = spar_tip_y(); // spar on the camber midline (not chord line) - spar_z = WING_SEMI_SPAN; // wing tip spanwise station - - // Low TEARDROP pad, proud of the tip face by TIP_PAD_PROUD only (Rev S1c). - // Hull of a forward circle on the spar axis (hosts the bearing flange) and a - // smaller aft circle on the sensor-pocket axis (hosts the AK7455 seat). The - // forward edge stops at spar_x − TIP_PAD_FWD_R so it clears the relocated - // EDF double-D, which exits this same face. + spar_y = spar_tip_y(); // spar on the camber midline + spar_z = WING_SEMI_SPAN; // wing tip spanwise station + hull() { translate([spar_x, spar_y, spar_z]) - cylinder(r = TIP_PAD_FWD_R, h = TIP_PAD_PROUD, $fn = 48); + cylinder(r = TIP_PAD_R, h = TIP_PAD_PROUD, $fn = 64); translate([spar_x + HALL_SENS_R, spar_y, spar_z]) - cylinder(r = TIP_PAD_AFT_R, h = TIP_PAD_PROUD, $fn = 48); + cylinder(r = TIP_PAD_SENS_R, h = TIP_PAD_PROUD, $fn = 48); + translate([SHAFT_BORE_STATION, spar_y, spar_z]) + cylinder(r = TIP_PAD_SHAFT_R, h = TIP_PAD_PROUD, $fn = 48); } } @@ -1023,44 +1479,42 @@ module wing_tip_nacelle_mount_pad() { // ============================================================================= // ── Module: wing_tip_spar_through_bore ──────────────────────────────────────── // ============================================================================= -// Rotating-clearance bore for the 8 mm spar, spanwise through the wing tip and -// pad. The spar ROTATES, so this is a clearance bore (TILT_SPAR_BORE_CLEAR), -// NOT a press-fit. Centered at SPAR_BORE_STATION chordwise, Y = 0. +// Continues the spar bore through the mount pad so the Ø20 spar can pass out of +// the tip face and carry the nacelle trunnion (SPAR_TIP_PROTRUSION). +// +// FIXES A LATENT AXIS BUG. The Rev R2 version of this module wrapped its +// cylinder in `rotate([0, 90, 0])`, which maps +Z to +X — so it cut a +// CHORDWISE hole through the wingtip, not a spanwise continuation of the spar +// bore. It went unnoticed because at Ø8.3 the stray hole was small and +// spar_bore() already ran the full span, so the module's real job (reaching +// through the 2 mm pad) was simply never done and nothing looked wrong. At +// Ø20.4 the same code would cut a 20 mm chordwise gash through the tip. The +// sibling module wing_tip_bearing_seat() had the correct convention all along +// and even documented it: "Spanwise axis is Z, so plain Z-axis cylinders are +// already correct." Corrected to match. module wing_tip_spar_through_bore() { spar_x = WING_SWEEP_LE + SPAR_BORE_STATION; spar_y = spar_tip_y(); - bore_z_start = WING_SEMI_SPAN - 20.0; // well inside the wing - bore_z_end = WING_SEMI_SPAN + TIP_PAD_PROUD + 2.0; // through the pad - - translate([spar_x, spar_y, bore_z_start]) - rotate([0, 90, 0]) - cylinder(r = TILT_SPAR_BORE_CLEAR / 2, - h = bore_z_end - bore_z_start, - $fn = 32); + z0 = WING_SEMI_SPAN - 5.0; // overlap into the wing + z1 = WING_SEMI_SPAN + TIP_PAD_PROUD + 1.0; // clear through the pad + + translate([spar_x, spar_y, z0]) + cylinder(r = TILT_SPAR_BORE_CLEAR / 2, h = z1 - z0, $fn = 64); } // ============================================================================= -// ── Module: wing_tip_bearing_seat ───────────────────────────────────────────── +// ── Module: wing_tip_bearing_seat — SUPERSEDED (Rev T1, 2026-08-29) ─────────── // ============================================================================= -// Press-fit seat for the wingtip bearing (F688ZZ 8×16×5), bored into the pad -// face at the tip. Outer race fixed here; inner race rotates on the spar. A -// shallow counterbore seats the F688ZZ flange flush with the pad face. -// The seat opens toward the nacelle (+Z spanwise), the flange facing outboard. -module wing_tip_bearing_seat() { - spar_x = WING_SWEEP_LE + SPAR_BORE_STATION; - spar_y = spar_tip_y(); - face_z = WING_SEMI_SPAN + TIP_PAD_PROUD; // pad outer face (nacelle side) - - // Spanwise axis is Z, so plain Z-axis cylinders are already correct. - // Bearing bore (OD seat), cut inward (−Z) from the pad face. - translate([spar_x, spar_y, face_z - TIP_BRG_W]) - cylinder(r = TIP_BRG_SEAT_D / 2, h = TIP_BRG_W + 0.1, $fn = 48); - - // Flange counterbore at the very face (flush seat for the F688ZZ flange). - translate([spar_x, spar_y, face_z - TIP_BRG_FLANGE_T]) - cylinder(r = TIP_BRG_FLANGE_OD / 2, h = TIP_BRG_FLANGE_T + 0.1, $fn = 48); -} +// SUPERSEDED and NO LONGER CALLED. The MF128ZZ wingtip bearing existed because +// the Rev R2 spar ROTATED inside the wing. Under Rev T1 the spar is fixed and +// bonded, and the tilt bearing has moved to the NACELLE's trunnion ring (plan +// 003 KTD3). A bearing here would now be actively harmful: it would let the +// spar spin under the drive pinion's gear reaction, which is the one thing the +// fixed spar must not do. +// Body emptied; retained as a marker for traceability, matching the convention +// wing_tip_fixed_gear_inserts() already set in this file. +module wing_tip_bearing_seat() { } // ============================================================================= @@ -1123,19 +1577,16 @@ module hall_sensor_cableway() { cylinder(r = HALL_CABLE_D / 2, h = 0.01); } - // ROOT-TENON PASS-THROUGH (inherited from the EDF pair at Rev S1c). The - // fuselage_root_tab spans X 49.5..79.5, and at the 54.0 mm station this - // conduit (X 52.25..55.75) now lands inside it — the same dead-end the EDF - // double-D used to hit at 0.48c. A SEPARATE STRAIGHT, AXIAL (constant X,Y) - // bore continues it down through the tenon and out the inboard face, so the - // root-face opening and the tenon-exit opening stay perfectly coaxial and - // the wire threads straight. SOUNDNESS: at Ø3.5 on a 30 × 20 mm tenon this - // only grooves the crown (root_yc ≈ +9.8, so the bore reaches ≈ +11.6 vs - // the Y = +10 tenon top); the full-width lower ~15 mm of the tenon — the - // load-bearing spine — is untouched. This is a strictly SMALLER intrusion - // than the Ø7 double-D it replaces there. - translate([xc, root_yc, -(WING_ROOT_TAB_L + 1.0)]) - cylinder(r = HALL_CABLE_D / 2, h = WING_ROOT_TAB_L + 2.0, $fn = 32); + // ROOT-TENON PASS-THROUGH: NO LONGER REQUIRED (Rev T1). + // Rev S1c had to drill this conduit down through the root tenon because + // the tenon then spanned X 49.5..79.5 and the 54.0 mm station landed + // inside it, dead-ending the wire in solid material. Under Rev T1 the + // tenon is a LOCATING feature at its 12 mm width (WING_ROOT_TAB_W), so it + // spans X 58.5..70.5 and this conduit spans 50.75..57.25 — clear by + // 1.25 mm. The wire exits the root face in open air and the tenon crown + // stays intact, which is strictly better than grooving it. + // If TENON_LOAD_PATH is ever set to "enlarged_tenon" (W = 30 → X + // 49.5..79.5) the collision RETURNS and this pass-through must come back. // Chordwise jog at the tip linking the conduit to the sensor pocket. spar_x = WING_SWEEP_LE + SPAR_BORE_STATION; @@ -1225,50 +1676,60 @@ module wing_root_tie_rod_aft_bore() { // ── Module: wing_one_side ───────────────────────────────────────────────────── // ============================================================================= // One wing panel (port or starboard before mirror). -// Builds the solid, then subtracts the spar bore. -// The fuselage root tab is additive (−Z from root face, inboard direction). -// INTEGRATION (Rev R2, 2026-07-18): Rotating 8 mm tilt-spar mechanism. The wing -// tip carries a bearing seat + rotating through-bore + fixed R14 gear mount pad; -// the separate wing_nacelle_pylon_revo.scad component is superseded and archived. -// See docs/TILT_SPAR_ANALYSIS.md. +// +// REV T1 (2026-08-29) — THE SPAR IS NOW A MEMBER OF THIS PART, NOT A SHAFT +// PASSING THROUGH IT. The Ø20 CF tube is epoxy-bonded into spar_bore() over +// the full span and is the wing's primary bending member; the wing skin is a +// stressed fairing bonded to it. Everything else in this module follows from +// that: +// • the wing tip carries a REGISTER PAD, not a bearing seat — the tilt +// bearing is on the nacelle's trunnion ring (plan 003 KTD3); +// • the root tenon LOCATES and does not react moment — the spar does, into +// a fuselage socket (see the WING ROOT LOAD PATH block); +// • the tie-rod couple is not cut at all on the default path. +// +// Spanwise bore inventory, all at CONSTANT chordwise stations so they stay +// mutually parallel over the taper (fail-closed check: +// tools/wing_internal_clearance.py): +// 8.0 Ø3.2 nav_bore() nav-light 3-core +// 28.0 Ø20.4 spar_bore() bonded CF structural spar +// 44.5 Ø6.5 hall_sensor_cableway() AK7455 SPI quad + power pair +// 53.6 Ø4.4 tilt_shaft_bore() nacelle tilt drive shaft module wing_one_side() { difference() { union() { // ── Lofted wing solid ────────────────────────────────────────── wing_solid(); - // ── Fuselage root insertion tab ──────────────────────────────── + // ── Fuselage root insertion tab (locating) ───────────────────── fuselage_root_tab(); - // ── Wing-tip nacelle mount pad (Rev R2) ───────────────────────── - // Low boss at the tip housing the bearing seat + fixed-gear inserts. - // Stays low so the fixed R14 gear nests into the nacelle recess. + // ── Wing-tip nacelle register pad ────────────────────────────── wing_tip_nacelle_mount_pad(); } - // ── CF spar bore (spanwise, 12 mm OD wing structural spar) ──────── + // ── Bonded structural spar bore (Ø20.4, full span) ──────────────── spar_bore(); - // ── Rotating tilt-spar clearance bore (8 mm spar, through wing tip) + // ── Spar continuation through the tip pad, so the stub can reach + // the nacelle trunnion (SPAR_TIP_PROTRUSION) ─────────────────── wing_tip_spar_through_bore(); - // ── Wingtip bearing seat (F688ZZ 8×16×5, press-fit) ────────────── - wing_tip_bearing_seat(); + // ── Nav-light 3-core conduit, forward of the spar ───────────────── + nav_bore(); - // (Fixed sector-gear inserts removed Rev R2b — gear train archived; - // nozzle now pushrod-driven from the rotating spar. No wing gear.) + // ── Nacelle tilt drive shaft, aft of the spar ───────────────────── + tilt_shaft_bore(); - // ── Tilt-feedback Hall sensor seat + its dedicated cableway (Rev R2c) + // ── AK7455 encoder seat + its dedicated shielded cableway ───────── wing_tip_hall_sensor_pocket(); hall_sensor_cableway(); - // ── Harness cableway (2× Ø7 conduits for EDF power + signal) ────── - cableway_bore(); - - // ── Wing-root tie-rod couple (U5, "two_rod" path only) ───────────── - // Replaces the tenon's former structural role; see the module docs - // above and the couple-force derivation in - // airframe/fuselage-mid/WBS.md §1.1.1.2 CARGO-03c. + // ── Wing-root tie-rod couple — SUPERSEDED path only ──────────────── + // Not cut under the Rev T1 default. Under "two_rod" the forward rod + // (station 14.0, Ø8.2 → 9.9..18.1) INTERSECTS the Ø20.4 spar bore + // (17.80..38.20); that path is retained as the design record and is + // geometrically invalid at the Rev T1 spar. See TENON_LOAD_PATH. if (TENON_LOAD_PATH == "two_rod") { wing_root_tie_rod_fwd_bore(); wing_root_tie_rod_aft_bore(); diff --git a/airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl b/airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl index fc9296a..489dbd1 100644 Binary files a/airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl and b/airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl differ diff --git a/airframe/stls/fuselage/generate_wing_root_flange.py b/airframe/stls/fuselage/generate_wing_root_flange.py new file mode 100644 index 0000000..09163ad --- /dev/null +++ b/airframe/stls/fuselage/generate_wing_root_flange.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Generate the two bonded wing-root flanges (WA-R1b, Rev T1c). + +HULL-FRAME STANDARD R1 — X = +port, Y = +aft, Z = +dorsal; origin = +SerenityAssembly.FCStd world origin. Output STLs are already in the hull frame; +do NOT run tools/bake_hull_frame.py on them. + +WHAT THIS PART IS +----------------- +Under Rev T1 the wing spar is a fixed bonded CF tube and the wing-root joint +splits by load type (docs/WING_ATTACH_INTERFACE.md SS3.3): a short socket takes +the shear, and this flange takes the MOMENT. Reacting over wall AREA rather +than socket DEPTH is what makes it work -- the cargo bay caps socket depth at +18.67 mm and a socket's capacity goes as 1/L^2, so at that depth a socket gives +FOS 0.51 on the moment. The flange gives FOS 29.2: + + triangular bearing pressure over height h, arm 2h/3 + F = 3M / (2h) = 3 x 14.60 N.m / (2 x 0.080 m) = 274 N + area = 80 x 60 = 1,600 mm^2 + sigma = 274 / 1600 = 0.17 MPa + FOS = 5.0 MPa / 0.17 = 29.2 (5 MPa bond-limited CF-PETG, + docs/structural_analysis.md SS7.3) + +WHY IT IS A SEPARATE PART AND NOT SHELL GEOMETRY +------------------------------------------------ +Measured 2026-08-30 by ray-casting the baked cargo envelope on a 13 x 17 grid +over the footprint (221/221 hits): the sidewall skin moves through **34.3 mm +(port) / 37.0 mm (stbd)** of hull X across the 80 x 60 window. The wall is not +flat there, not even approximately. Consequently: + + * modelling the flange the way merge_cargo_interior.py models every other + positive -- deep material intersected with the envelope -- would make it up + to 34 mm thick, hundreds of grams for a plate whose specified 5 mm already + clears the FOS 4.0 target by 7x; and + * bounding it with a plane at the nominal inner face instead leaves that plane + TANGENT to the skin inside the footprint. That is a knife edge, and it + measured as one: a 0.46 mm non-manifold edge with 4 incident faces at + (-86.33, +8.6, +52.2), plus a zero-area sliver body, on the first rebuild. + +Subtracting a translated envelope from the whole 900k-face shell to get a +conforming plate was tried and was worse still (4 boundary edges, 2 non-manifold +edges, 4 bodies): a near-coincident boolean over 4,800 mm^2 at that scale does +not resolve cleanly. Done LOCALLY, on a cropped envelope, the same operation is +small and well-conditioned -- which is what this script does. + +So the flange follows the pattern this repository already uses for every other +bonded plate: the CF thwarts (asf.RING_POCKETS) and the three splice collars are +separate parts too, and the shell's only obligation to them is to reserve their +volume. `merge_cargo_interior.wing_keepout_positives()` carries that reservation +as a nominal slab so the landing-gear bay cannot grow into it. + +Run: + /usr/bin/python3 airframe/stls/fuselage/generate_wing_root_flange.py + +Use `/usr/bin/python3`: the repo .venv hides trimesh and manifold3d. + +Author: Steve Griffing, PE(CSE), CISSP-ISSEP, CPP +AI note: Written by Claude (model: Claude Opus 5, Anthropic) under the author's + direction, per `AGENTS.md` SS3 "Attribution and Licensing". +License: CC BY-SA 4.0 - creativecommons.org/licenses/by-sa/4.0 +""" + +import os +import sys + +import trimesh + +REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +sys.path.insert(0, os.path.join(REPO_ROOT, "airframe", "blender-scripts")) +import merge_cargo_interior as mci # noqa: E402 + +OUT_DIR = os.path.dirname(os.path.abspath(__file__)) + +# Crop margin around the footprint. The local envelope must extend past the +# plate on every side, or the subtraction that sets the thickness runs out of +# material at the rim and leaves the plate open there. +CROP_PAD = 15.0 # mm + +# CF-PETG (20% chopped CF), REF-MAT-002 / current-specification bom FIL-CF-PETG. +RHO_G_MM3 = 1.30e-3 # g/mm^3, solid +INFILL = 0.40 # 40% gyroid, the repo's structural-print convention + + +def build(side): + """Return (trimesh, label) for one side's conforming flange plate.""" + src = trimesh.load(mci.BLENDER_SRC, process=False) + src.merge_vertices() + env = mci.extract_envelope(mci.bake(src)) + + y0 = mci.WING_SPAR_Y - mci.ROOT_FLANGE_W / 2.0 + y1 = mci.WING_SPAR_Y + mci.ROOT_FLANGE_W / 2.0 + z0 = mci.WING_SPAR_Z - mci.ROOT_FLANGE_H / 2.0 + z1 = mci.WING_SPAR_Z + mci.ROOT_FLANGE_H / 2.0 + + if side == "port": + x_lo, x_hi = mci.PORT_INB - 20.0, mci.PORT_OUTB + dx = -mci.ROOT_FLANGE_T # inboard is -X on the port side + else: + x_lo, x_hi = mci.STBD_OUTB, mci.STBD_INB + 20.0 + dx = +mci.ROOT_FLANGE_T # inboard is +X on the starboard side + + # Local envelope: the footprint plus CROP_PAD on every side. Everything + # below is a boolean on THIS, not on the full shell -- that locality is the + # whole reason the operation is well-conditioned here and was not in + # merge_cargo_interior.py. + crop = mci.to_man(mci.box(x_lo, x_hi, + y0 - CROP_PAD, y1 + CROP_PAD, + z0 - CROP_PAD, z1 + CROP_PAD)) + env_local = crop ^ mci.to_man(env) + + footprint = mci.to_man(mci.box(x_lo, x_hi, y0, y1, z0, z1)) + plate = (footprint ^ env_local) - env_local.translate([dx, 0.0, 0.0]) + return keep_plate(mci.from_man(plate), side) + + +def keep_plate(mesh, side): + """Keep the plate proper; report and drop anything else the window caught. + + The footprint window is a box, and inside a box the ray from outboard can + strike more than one surface: at the aft-upper corner it also clips an + INTERIOR wall standing behind the skin. The subtraction then lays a second, + disconnected 5 mm layer on that wall. It is real geometry, not mesh noise -- + but it is not part of this plate, and a bonded part cannot be delivered as + two disconnected solids, so it is dropped and its size is printed. + + Measured 2026-08-30: port drops 293.1 mm^3 (1.2 % of the plate) at + X -118.4..-111.7, Y +45.4..+51.0, Z +57.8..+71.9; starboard drops an 18-face + zero-volume sliver. If a future hull change pushes a dropped body past a few + percent, the footprint is straddling a discontinuity and the FOOTPRINT is + what needs revisiting -- not this filter. + """ + bodies = sorted(mesh.split(only_watertight=False), + key=lambda b: abs(b.volume), reverse=True) + if len(bodies) <= 1: + return mesh + kept = bodies[0] + for b in bodies[1:]: + frac = abs(b.volume) / max(abs(kept.volume), 1e-9) + flag = " <-- REVIEW: over 5 % of the plate" if frac > 0.05 else "" + print(f" dropped disconnected body: {abs(b.volume):8.1f} mm^3 " + f"({frac:.2%} of the plate), {len(b.faces)} faces{flag}") + return kept + + +def report(mesh, name): + bodies = mesh.split(only_watertight=False) + vol = abs(mesh.volume) + ok = mesh.is_watertight and len(bodies) == 1 + b = mesh.bounds + print(f" {name}: faces={len(mesh.faces):,} bodies={len(bodies)} " + f"watertight={mesh.is_watertight}") + print(f" bounds X {b[0][0]:8.2f}..{b[1][0]:8.2f} " + f"Y {b[0][1]:7.2f}..{b[1][1]:7.2f} Z {b[0][2]:7.2f}..{b[1][2]:7.2f}") + print(f" volume {vol:9.1f} mm^3 mass {vol * RHO_G_MM3 * INFILL:5.1f} g " + f"at {INFILL:.0%} infill ({vol * RHO_G_MM3:5.1f} g solid)") + print(f" RESULT: {'PASS' if ok else 'FAIL'}") + return ok + + +def main(): + print("=== generate_wing_root_flange.py Rev T1c 2026-08-30 ===") + print(f" footprint {mci.ROOT_FLANGE_W:.0f} (Y) x {mci.ROOT_FLANGE_H:.0f} (Z) mm, " + f"thickness {mci.ROOT_FLANGE_T:.1f} mm, centred on the spar axis " + f"(Y {mci.WING_SPAR_Y:+.2f}, Z {mci.WING_SPAR_Z:+.2f})") + all_ok = True + for side in ("port", "stbd"): + mesh = build(side) + out = os.path.join(OUT_DIR, f"wing_root_flange_{side}.stl") + mesh.export(out) + all_ok &= report(mesh, f"wing_root_flange_{side}") + print(f" written -> {out}") + return 0 if all_ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/airframe/stls/fuselage/wing_root_flange_port.stl b/airframe/stls/fuselage/wing_root_flange_port.stl new file mode 100644 index 0000000..ae641ac Binary files /dev/null and b/airframe/stls/fuselage/wing_root_flange_port.stl differ diff --git a/airframe/stls/fuselage/wing_root_flange_stbd.stl b/airframe/stls/fuselage/wing_root_flange_stbd.stl new file mode 100644 index 0000000..4223234 Binary files /dev/null and b/airframe/stls/fuselage/wing_root_flange_stbd.stl differ diff --git a/airframe/stls/wings/wing_port_s1223_revo.stl b/airframe/stls/wings/wing_port_s1223_revo.stl index ff4aa6c..2e01a4e 100644 Binary files a/airframe/stls/wings/wing_port_s1223_revo.stl and b/airframe/stls/wings/wing_port_s1223_revo.stl differ diff --git a/airframe/stls/wings/wing_stbd_s1223_revo.stl b/airframe/stls/wings/wing_stbd_s1223_revo.stl index 7884715..716c500 100644 Binary files a/airframe/stls/wings/wing_stbd_s1223_revo.stl and b/airframe/stls/wings/wing_stbd_s1223_revo.stl differ diff --git a/airframe/wings-nacelles/WBS.md b/airframe/wings-nacelles/WBS.md index ffed16e..3986f7c 100644 --- a/airframe/wings-nacelles/WBS.md +++ b/airframe/wings-nacelles/WBS.md @@ -1347,35 +1347,60 @@ clearance and costs only thickness. 22.0 minimised thickness; 28.0 buys 7.2 mm of clearance for 1.8 points of tip t/c. Holding the spar at today's *height* instead of riding the midline is worse than either (tip t_scale → 2.711). -**`PIVOT_Z` STAYS 111.5.** An earlier draft moved it to 79.0 to keep the nacelle -canonical; **withdrawn** on two independent grounds — (1) owner-directed: the -pivot must sit at the CG so the centre of thrust does not move with tilt (an +**`PIVOT_Z` = 116.1, AT THE CG.** An earlier draft moved it to 79.0 to keep the +nacelle canonical; **withdrawn** on two independent grounds — (1) owner-directed: +the pivot must sit at the CG so the centre of thrust does not move with tilt (an off-CG pivot of 32.5 mm swings the *aircraft* CG ~9.2 mm through transition, -with the two nacelles at 28.4 % of AUW), and (2) hover ground clearance, below. -**OQ5 is therefore reopened**: with the pivot at the CG and the spar at station -22, the nacelle sits −32.5 mm off canonical unless the tilt axis is decoupled -from the spar axis and carried on a tip-rib bracket. +with the two nacelles at 28.4 % of AUW), and (2) hover ground clearance. The CG +itself then moves +6.0 mm when ESC1 relocates aft alongside ESC2 at zero added +mass, so 111.5 → **116.1**. (An earlier revision of this paragraph still read +"STAYS 111.5" and quoted the withdrawn station-22 pick; both are superseded by +the FROZEN block above.) + +**OQ5 is RESOLVED, not reopened** (2026-08-29, on implementation): the trunnion +is concentric with the spar and the nacelle keeps its canonical station because +the pivot moved *inside* it. No tip-rib offset bracket is needed, and the tilt +axis is not decoupled from the spar axis. Measured 2026-08-29 (`tools/wing_spar_station_fit.py`, exact 4-circle packing R/r = 1+√2): the bundle circumscribes **13.28 mm**, so the 16 mm tube named in the source conversation fits it only as 16 × 14 (1 mm wall, 0.72 mm total clearance) and its stated `SPAR_BORE_D = 11.0` does not fit at all. Free twist -needs ~16.3 mm bore → **20 mm OD**. At 20 mm the spar must move to 22 mm aft of -LE (root `t_scale` 1.464, tip 2.039); holding 45.15 mm would cost a 40 % t/c tip. - -- [ ] **SPAR-20-1 (U1)** — Freeze the spar/station/airfoil trade. Add - `tools/spar_bundle_fit.py`; extend `wing_spar_station_fit.py` to solve the - **root** `t_scale` as well as the tip; add `TILT_SPAR_ANALYSIS.md` §3.6 - re-deriving the section for a *fixed* spar and marking §3.2/§3.5's torsion - and keyability discriminators superseded. -- [ ] **SPAR-20-2 (U2)** — Re-loft the wing: `SPAR_BORE_STATION` 45.15 → **28.0**, - `SPAR_BORE_OD` 8.3 → 20.4, `THICKNESS_SCALE` 1.00 → **1.456** (**root OML - changes for the first time**), `THICKNESS_SCALE_TIP` 1.56 → **2.190**, and - re-derive `SPAR_Z` → **66.85** (do NOT carry 68.42 forward). - Re-purpose or delete the now-redundant power half of the Ø7 double-D. -- [ ] **SPAR-20-3 (U3)** — Wingtip trunnion, split-collar pinch clamp (no set - screws — CF crushes), and the field-maintainable garage with separated - power/signal disconnects. +needs ~16.3 mm bore → **20 mm OD**. At 20 mm the spar must move forward; the +selected station is **28.0** (root `t_scale` 1.456, tip 2.190 — see the FROZEN +block), superseding an earlier 22.0 pick made before the station's effect on +hover clearance was measured. Holding 45.15 mm would cost a 40 % t/c tip. + +- [x] **SPAR-20-1 (U1)** — DONE 2026-08-29. `tools/spar_bundle_fit.py` added + (exact packing ratios, REF-MATH-001, refuses to interpolate); + `wing_spar_station_fit.py` now solves the **root** `t_scale` too, reports + solved scales as actual t/c, and reports the UNSCALED camber midline the + fuselage must derive `SPAR_Z` from. `TILT_SPAR_ANALYSIS.md` §3.6 added. + Verified: `--bore 20.4 --station 28` → root 1.456 / tip 2.190; + regression `--bore 8.3 --station 45.15` still reproduces the as-built 1.550. +- [x] **SPAR-20-2 (U2)** — DONE 2026-08-29 (Rev T1). Station 45.15 → **28.0**, + bore 8.3 → **20.4**, `THICKNESS_SCALE` 1.00 → **1.46**, + `THICKNESS_SCALE_TIP` 1.56 → **2.20** (both the solved 1.456/2.190 rounded + UP per this file's own "not sitting on its own limit" convention — built + walls 1.19 / 1.21 mm). `SPAR_Z` = **66.85**, derived from the UNSCALED + camber midline. The Ø7 double-D is **retired entirely**, not re-purposed: + it could not carry two 10 AWG conductors at any realistic wire OD, and its + 27.5 mm station now falls inside the spar bore. A Ø3.2 nav conduit at + station 8.0 replaces it. Both wing STLs re-rendered, re-baked, watertight. +- [x] **SPAR-20-3 (U3)** — DONE 2026-08-29, **with two documented relocations.** + Wing tip built: register pad (three-lobe hull — spar / AK7455 / drive-shaft + bushing boss), spar through-bore corrected (the Rev R2 module carried + `rotate([0,90,0])` and was cutting a CHORDWISE hole), bearing seat retired. + - **The pinch clamp moves to the ROOT.** A Ø30 collar cannot exist in a + 22.83 mm section, and it would have nothing to do against a 5,492 mm² + bond (~27 kN axial). Wing + bonded spar are one serviceable assembly that + separates at the fuselage socket. Requirement published as WA-R3. + - **The garage moves to the NACELLE.** A 10 AWG disconnect needs ~6 mm of + height; aft of the spar the tip section falls 17.50 → 3.43 mm and that + depth is already claimed. Requirement published as WA-R10. No hatch is + needed — the nacelle is the cover. + Both relocations are recorded in + `docs/WING_ATTACH_INTERFACE.md` §3.4 / §4.4 with their measured causes. - [ ] **SPAR-20-4 (U4)** — `PIVOT_Z` **116.1** (pivot = CG stands; the CG itself moves +6.0 mm when ESC1 relocates aft alongside ESC2, at zero added mass); nacelle trunnion ring at ring-plane X ≈ 28 mm, measured envelope 53.4 mm OD, @@ -1383,18 +1408,62 @@ LE (root `t_scale` 1.464, tip 2.039); holding 45.15 mm would cost a 40 % t/c tip the through-duct spar bore, outboard hub, D-flat, and duct-wall collars; **restore the canonical 11-fin stator** (the spar tunnel and 2-fin re-index existed only to pass the shaft). -- [ ] **SPAR-20-5 (U5)** — Wire routing: power coaxial through spar → trunnion → - nacelle annulus → ESCs; nav 3-core crosses the joint in a separated - micro-channel; AK7455 pigtail stays in the fixed harness. **Closes the open - AK7455 pocket/cableway item in §1.1.3.6** (still sized for MT6701). -- [ ] **SPAR-20-6 (U6)** — Belt tilt drive. Size for **travel first** (145° - output is the binding constraint, not torque): 47T/25T at 270° servo range - gives 1.86× multiplication and 4.47 N·m against a 0.177 N·m requirement. - Belt channel, tensioner, and a re-derived torque budget including belt - pretension radial load. -- [ ] **SPAR-20-7 (U7)** — Fuselage/cargo-shell re-cut to the new station; the - F688ZZ root bearing becomes a **clamped** mount (a bearing there would now - let the spar spin under belt reaction); re-run the CF thwart couple. +- [x] **SPAR-20-5 (U5)** — WING HALF DONE 2026-08-29; nacelle half is WA-R10/R11. + Wing bore set, all constant-mm so taper cannot erode the webs: + 8.0 Ø3.2 nav 3-core · 28.0 Ø20.4 spar (carries the 4 × 10 AWG on the tilt + axis) · 44.5 Ø6.5 AK7455 · 54.0 Ø4.4 tilt drive shaft. + **Closes the open AK7455 item in §1.1.3.6**: the pocket was still 7 × 7 mm, + dimensioned around the MT6701 3 × 3 QFN rejected on 2026-07-19, with a Ø3.5 + conduit labelled 4-wire I²C. Now AK7455 QFN24 4×4 on a 10 × 8 board; + conduit Ø6.5 for the shielded SPI quad + shielded power pair per + `TILT_ENCODER_WIRING_EMI_SPEC.md` §2.1; ring magnet ID 26 / OD 41.2 (ID 10 + could not pass over a Ø20 spar); `HALL_SENS_R` 11 → **16.8** so the IC still + reads mid-annulus. §2.3's ≥ 15 mm intra-conduit separation is recorded as a + **documented deviation** — unachievable in one printed bore, and both groups + are 100 % braid-shielded, which is the actual mitigation. +- [~] **SPAR-20-6 (U6)** — SUPERSEDED as a *belt* drive by §1.1.5 (plan 004 + KTD1: a spanwise belt cannot reach an X-axis pivot without an added + right-angle stage, so a shaft + spur pair does the same job with fewer + parts). WING HALF DONE: Ø4.4 drive-shaft bore at station **53.6**. + **THE STAGE IS A REDUCTION, NOT A STEP-UP** (owner direction 2026-08-29): + the shaft turns **more than one revolution** to sweep the nacelle 140°. + That inverts the ratio and dissolves the impossibility found under the + step-up reading — plan 004 KTD4's C = 15 mm returned a ring PD of 10.5 mm, + smaller than the Ø20 spar it must encircle. As a reduction the ring is the + LARGER member and the geometry closes easily. + **BUILT: module 0.8, 14T pinion (PD 11.2) / 50T ring (PD 40.0), i = 3.571, + shaft 1.389 rev per 140°, C = 25.6 → station 53.6.** 36T is exactly one + revolution (not "more than"); 54T pushes the bore to 55.2 and leaves 1.1 mm + to the root tenon, under the 1.16 mm floor. + **OQ1 (servo 180 vs 270) is CLOSED — the question is void.** A multi-turn + output means the actuator is no longer a limited-rotation servo but a + continuous-rotation gearmotor or stepper, closed on the AK7455's absolute + angle. That also retires "travel is the binding constraint" (plan 004 KTD5) + and makes the encoder load-bearing for CONTROL, not telemetry. + **NEW OPEN ITEM: actuator re-select** — the DS3225 is ~17× oversized on + torque and now also the wrong kind of device. +- [ ] **SPAR-20-7 (U7)** — Fuselage/cargo-shell re-cut. **OPEN, and it is now + the gating item**: `tools/wing_root_deconflict.py` FAILS at Rev T1 with three + findings, all one cause — the fuselage still carries `WING_SPAR_Y = +38.15` + and `WING_SPAR_BORE_D = 8.3`. + **THE JOINT SPLITS BY LOAD TYPE (revised 2026-08-29, owner: the centre of + the cargo bay must stay clear).** An earlier revision asked for a 55 mm + bonded socket; the bay's clear span starts at hull X −100 and the wall skin + is at −81.33, so only **18.67 mm** of socket depth exists. At that depth the + socket gives FOS 0.51 on the moment, and since capacity goes as 1/L² no + socket the bay permits can recover it. So: + - **socket Ø20.4 × 18.5 mm at hull Y +21.00 / Z +66.85 — SHEAR only**, + σ 0.31 MPa, **FOS 16**; + - **bonded root flange 80 (Z) × 60 (Y) mm on the inner sidewall — MOMENT**, + σ 0.17 MPa, **FOS 29.2**, protruding only its own ~5 mm to X ≈ −86. + This is **better** than the socket it replaces (FOS 29.2 vs 4.02), because a + flange reacts over wall AREA instead of socket DEPTH. The bay requirement + forced a better joint rather than a compromise, and **the bay is no longer + intruded at all** — the owner-decision item that stood here is closed. + F688ZZ deleted — a bearing there would let the spar spin under the pinion's + gear reaction. The LG-11 coupon is **demoted here** from a gate to a + packaging convenience (flange FOS 29.2 / 87.6 / 274.6 at 5 / 15 / 47 MPa). + Full spec: `docs/WING_ATTACH_INTERFACE.md` §3, WA-R1/R1b/R2..R6. - [ ] **SPAR-20-8 (U8)** — Re-datum the nozzle drive onto the fixed trunnion (a fixed datum is better than the retired rotating one); delete the spar crank; re-verify full iris travel. diff --git a/current-specification/bom_revS.csv b/current-specification/bom_revS.csv index 05a6d84..8000965 100644 --- a/current-specification/bom_revS.csv +++ b/current-specification/bom_revS.csv @@ -3,13 +3,13 @@ EDF-50-6S,50mm EDF unit @ 6S (6- or 12-blade),Propulsion,4,70,280,AliExpress / B EDF-55-6S,55mm EDF unit @ 6S — Phase 11 DEFERRED,Propulsion,0,95,0,HobbyKing / AliExpress,55mm 6S ducted fan EDF ~1500g thrust,45.00,0.00,"*** PHASE 11 DEFERRED — rear fuselage EDF; not installed in Phases 5-10 (CLAUDE.md: ""optional addition once everything else works""); ~1500g fan thrust; exhausts aft through fixed canonical elliptical nozzle (2.06x1.76 in); also feeds 4 RCS bleed jets (~15% flow); FORWARD (cruise) thrust only — not counted in hover T/W ***" ESC-40A-6S,"40A 6S BLHeli32 ESC, BDSHOT600",ESC,4,25,100,AliExpress / HobbyKing,BLHeli32 40A 6S DSHOT600 ESC,18.00,72.00,One per 50mm EDF; mount in nacelle hub bore ID=20mm; 55A burst ESC-50A-6S,50A 6S BLHeli32 ESC — Phase 11 DEFERRED,ESC,0,35,0,AliExpress / HobbyKing,BLHeli32 50A 6S ESC ~65A burst,22.00,0.00,*** PHASE 11 DEFERRED — not installed in Phases 5-10; for 55mm rear EDF only; ~65A burst; XT60; F_ESC5 = 60A MIDI fuse *** -SERVO-TILT,"Nacelle tilt servo — DS3225 converted with LibreServo_v4, 21-24.5 kgf·cm stall (4.8-6.8V), rotation-limit pin removed",Servo,2,60,120,"Amazon / AliExpress (servo) + github.com/Stab-Rabbit-coding/LibreServo_v4 (board, self-build)",DS3225 25kg digital servo metal gear,13.00,26.00,"2026-08-23 (Rev S1d, owner): DS3225 bodies + LibreServo_v4 PCB (TI MSPM0G3518-Q1 + OPTIGA Trust M). Supersedes DS3218 (same session) and the 2026-08-02 SPT5425LV+LibreServo v2. DIMENSIONS AUTHORITATIVE from avionics/datasheets/DS3225 datasheet.pdf (Dongguan City Dsservo): 40x20x40.5 mm, 60 g, 54.5 mm flange span, 49.5x10 mm bolt pattern, flange 27.7 mm above base, IP66, double bearing, 275:1 — DIMENSIONALLY IDENTICAL to the DS3218 datasheet, so the Rev S1d cargo-shell pads (61.5x27.0 mm at hull Z +98.89, bores 49.5x10) are unchanged by this swap. LibreServo_v4 keeps the stock case, so the external envelope is unchanged. ** TORQUE, still marginal ** datasheet stall 21 kgf·cm @5V / 24.5 @6.8V — the '25kg' in the product name is the marketing figure, the spec table maxes at 24.5. Against the >=25 kgf·cm (2.45 N·m) tilt requirement (docs/TILT_SPAR_ANALYSIS.md §2, from serenity-rev-r.jsx L383) that is 98% at 6.8V, ~92% at a 6V rail, 84% at 5V. Much closer than DS3218's 86%, but not cleared on datasheet figures alone. LibreServo_v4 re-drives the motor from 4.5-18V and may close it; no converted-unit figure exists. RESOLVED 2026-08-25 (docs/TILT_SPAR_ANALYSIS.md §2.1): the >=25 kgf·cm requirement was itself the stale artifact -- it is a spec pick (archives/serenity-rev-r.jsx L383), not a load derivation. Pivot-at-CG nulls gravity to within a 0.019 kgf·cm bound (two off-axis parts: spar crank + pushrod); inertia from the nacelle's own mass-moment table and the repo's only cited tilt-transition figure is ~1.78 kgf·cm ultimate. Grounded total ~1.80 kgf·cm = 7.3% of DS3225's cited 24.5 kgf·cm @ 6.8V -- wide margin. Aero moment not grounded (no nacelle Cd/frontal-area data in repo) -- explicit open item, not assumed zero. DS3225 STANDS, no part change. ** CURRENT ** stall 1.9A @5V / 2.3A @6.8V is the 6V servo bus figure (docs/POWER_DISTRIBUTION.md §3.3), now resized there to 2.3A/servo -- NOT RAIL-2 (RAIL-2/5V_OBS is the separate Observer/winch-servo rail; the 'RAIL-2' language previously here was a citation error, corrected 2026-08-25). See REFERENCES.md and airframe/wings-nacelles/WBS.md SPAR-02." +SERVO-TILT,"Nacelle tilt servo — DS3225 converted with LibreServo_v4, 21-24.5 kgf·cm stall (4.8-6.8V), rotation-limit pin removed",Servo,2,60,120,"Amazon / AliExpress (servo) + github.com/Stab-Rabbit-coding/LibreServo_v4 (board, self-build)",DS3225 25kg digital servo metal gear,13.00,26.00,"** REV T1c 2026-08-30 -- OPERATING MODE CHANGED, PART UNCHANGED (WA-R15). ** The tilt stage is now a REDUCTION (tip 14T/50T, i=3.571) so the drive shaft turns 1.438 rev over the 145 deg sweep; the actuator is therefore run CONTINUOUS-ROTATION (multi-turn) and closed on the AK7455's absolute nacelle angle, not soft-limited on its own travel -- the same architecture the cargo winch already uses. The rotation-limit pin removal this row already specifies is now REQUIRED rather than a commonality choice. The body is unchanged, so the pad footprint (61.5 x 27.0), the 49.5 x 10 bolt pattern and the 60 g mass all carry over from Rev S1d; what is new is the MOUNT POSITION (hull Y +46.60, Z +99.49, shaft-relative, was spar-relative) and an 18 mm standoff so the output-shaft gear clears the wing root tenon. Torque margin at the built reduction is 0.050 N.m required vs 2.402 N.m stall = 48x, not the 17x previously recorded. NO SPEED FIGURE IS PUBLISHED FOR THE DS3225 IN THIS REPO -- the 0.18 s/60 deg in REFERENCES.md belongs to the superseded SPT5425LV and must not be reused; nacelle rate = actuator rate / 3.571, and the actuator rate is unmeasured (docs/TILT_DRIVE_CONTROL_SPEC.md SS7.2, TILT-CTL-05). See docs/TILT_DRIVE_CONTROL_SPEC.md for the loop, sensors and fail states. 2026-08-23 (Rev S1d, owner): DS3225 bodies + LibreServo_v4 PCB (TI MSPM0G3518-Q1 + OPTIGA Trust M). Supersedes DS3218 (same session) and the 2026-08-02 SPT5425LV+LibreServo v2. DIMENSIONS AUTHORITATIVE from avionics/datasheets/DS3225 datasheet.pdf (Dongguan City Dsservo): 40x20x40.5 mm, 60 g, 54.5 mm flange span, 49.5x10 mm bolt pattern, flange 27.7 mm above base, IP66, double bearing, 275:1 — DIMENSIONALLY IDENTICAL to the DS3218 datasheet, so the Rev S1d cargo-shell pads (61.5x27.0 mm at hull Z +98.89, bores 49.5x10) are unchanged by this swap. LibreServo_v4 keeps the stock case, so the external envelope is unchanged. ** TORQUE, still marginal ** datasheet stall 21 kgf·cm @5V / 24.5 @6.8V — the '25kg' in the product name is the marketing figure, the spec table maxes at 24.5. Against the >=25 kgf·cm (2.45 N·m) tilt requirement (docs/TILT_SPAR_ANALYSIS.md §2, from serenity-rev-r.jsx L383) that is 98% at 6.8V, ~92% at a 6V rail, 84% at 5V. Much closer than DS3218's 86%, but not cleared on datasheet figures alone. LibreServo_v4 re-drives the motor from 4.5-18V and may close it; no converted-unit figure exists. RESOLVED 2026-08-25 (docs/TILT_SPAR_ANALYSIS.md §2.1): the >=25 kgf·cm requirement was itself the stale artifact -- it is a spec pick (archives/serenity-rev-r.jsx L383), not a load derivation. Pivot-at-CG nulls gravity to within a 0.019 kgf·cm bound (two off-axis parts: spar crank + pushrod); inertia from the nacelle's own mass-moment table and the repo's only cited tilt-transition figure is ~1.78 kgf·cm ultimate. Grounded total ~1.80 kgf·cm = 7.3% of DS3225's cited 24.5 kgf·cm @ 6.8V -- wide margin. Aero moment not grounded (no nacelle Cd/frontal-area data in repo) -- explicit open item, not assumed zero. DS3225 STANDS, no part change. ** CURRENT ** stall 1.9A @5V / 2.3A @6.8V is the 6V servo bus figure (docs/POWER_DISTRIBUTION.md §3.3), now resized there to 2.3A/servo -- NOT RAIL-2 (RAIL-2/5V_OBS is the separate Observer/winch-servo rail; the 'RAIL-2' language previously here was a citation error, corrected 2026-08-25). See REFERENCES.md and airframe/wings-nacelles/WBS.md SPAR-02." SERVO-RCS-VALVE,"RCS bleed-jet proportional valve servo — SG90 class, OpenServoCore control board — Phase 11 DEFERRED",Servo,0,9,0,"Amazon / AliExpress (servo) + github.com/OpenServoCore/open-servo-core (board, self-build)",SG90 9g micro servo 1.8kg,3.00,0.00,"*** PHASE 11 DEFERRED — 4 per aircraft; modulates the 4 EDF-fed RCS bleed jets for pitch/yaw attitude authority *** 2026-08-02: standardises on the OpenServoCore ""OSC SG90 M007"" swap board (REFERENCES.md REF-SENSOR-015) when this phase is implemented; inherits the pre-production-hardware caveat from SERVO-CARGO below." SERVO-CARGO,"SG90 micro servo — cargo door actuation (1×) + payload release (1×), OpenServoCore control board",Cargo,2,9,18,"Amazon / AliExpress (servo) + github.com/OpenServoCore/open-servo-core (board, self-build)",SG90 9g micro servo 1.8kg cm,3.00,6.00,"Rev P new; Ch-A = door cam via DRV8833 H-bridge; Ch-B = latch release pull-wire; both on Cape-B GPIO PWM. 2026-08-02: control board = OpenServoCore ""OSC SG90 M007"" (REFERENCES.md REF-SENSOR-015), swap board replacing the SG90 factory PCB in place — same body envelope, no bracket change. Upstream hardware is pre-production (""nothing here is shippable yet""); do not procure in flight-article quantity until upstream reaches a shippable release." BRG-MF104ZZ,MF104ZZ flanged bearing 4×10×4mm,Bearing,4,2,8,Amazon / AliExpress,MF104ZZ flanged ball bearing 4x10x4mm,1.50,6.00,2 per nacelle pivot housing; press-fit into clevis ear bore OD=16mm; Rev O: confirmed selection over 686ZZ CF-ROD-4MM,4mm OD solid CF pivot rod (cut to length per pivot housing),Structural Hardware,2,8,16,Amazon / AliExpress,4mm solid carbon fiber rod 300mm,6.00,12.00,One per nacelle; cut length per pivot housing drawing; pivot at Z=83mm (CG) in Rev O -CF-ROD-8MM,8mm OD solid CF wing-root fwd tie-rod (bonded, cut to ~85mm per side),Structural Hardware,2,7,14,Amazon / AliExpress,8mm solid carbon fiber rod (same generic pultruded-CF-rod-stock as CF-ROD-4MM; not part-number-specific),7.00,14.00,"U5/KTD1 wing-root couple: forward rod, Ø8.2mm clearance bore (0.1mm/side), station 14.0mm from LE, 40mm wing-side embed + 41mm fuselage-side embed (West System 105/206 epoxy, docs/structural_analysis.md §6/§7); one per side (port+stbd)" -CF-ROD-6MM,6mm OD solid CF wing-root aft tie-rod (bonded, cut to ~90mm per side),Structural Hardware,2,4,8,Amazon / AliExpress,6mm solid carbon fiber rod (same generic pultruded-CF-rod-stock as CF-ROD-4MM; not part-number-specific),5.00,10.00,"U5/KTD1 wing-root couple: aft rod, Ø6.2mm clearance bore (0.1mm/side), station 62.0mm from LE (root-only embed, does not run to tip), 42mm wing-side embed + 42mm fuselage-side embed (West System 105/206 epoxy); one per side (port+stbd)" +CF-ROD-8MM,8mm OD solid CF wing-root fwd tie-rod — SUPERSEDED Rev T1,0,0,7,0,Amazon / AliExpress,8mm solid carbon fiber rod (same generic pultruded-CF-rod-stock as CF-ROD-4MM; not part-number-specific),7.00,0.00,"U5/KTD1 wing-root couple: forward rod, Ø8.2mm clearance bore (0.1mm/side), station 14.0mm from LE, 40mm wing-side embed + 41mm fuselage-side embed (West System 105/206 epoxy, docs/structural_analysis.md §6/§7); one per side (port+stbd) ** SUPERSEDED Rev T1 (2026-08-29/30) -- QTY 0. ** The wing-root tie-rod couple existed only because a spar on BEARINGS cannot react a moment", so the couple had to be closed by something else (U5/KTD1, FOS 4.14). Under Rev T1 the spar is bonded and FIXED, carries its own moment into the bonded root flange (PRINT-WING-ROOT-FLANGE, FOS 29.2)," and the rods have no remaining job. They are also no longer BUILDABLE: the wing gates them off (wings_s1223_revo.scad TENON_LOAD_PATH = ""spar_carrythrough""", whose own comment records that the forward rod now INTERSECTS the O20.4 spar bore), and merge_cargo_interior.py no longer drills the fuselage side. Retained at qty 0 for traceability. +CF-ROD-6MM,6mm OD solid CF wing-root aft tie-rod — SUPERSEDED Rev T1,0,0,4,0,Amazon / AliExpress,6mm solid carbon fiber rod (same generic pultruded-CF-rod-stock as CF-ROD-4MM; not part-number-specific),5.00,0.00,"U5/KTD1 wing-root couple: aft rod, Ø6.2mm clearance bore (0.1mm/side), station 62.0mm from LE (root-only embed, does not run to tip), 42mm wing-side embed + 42mm fuselage-side embed (West System 105/206 epoxy); one per side (port+stbd) ** SUPERSEDED Rev T1 (2026-08-29/30) -- QTY 0. ** The wing-root tie-rod couple existed only because a spar on BEARINGS cannot react a moment", so the couple had to be closed by something else (U5/KTD1, FOS 4.14). Under Rev T1 the spar is bonded and FIXED, carries its own moment into the bonded root flange (PRINT-WING-ROOT-FLANGE, FOS 29.2)," and the rods have no remaining job. They are also no longer BUILDABLE: the wing gates them off (wings_s1223_revo.scad TENON_LOAD_PATH = ""spar_carrythrough""", whose own comment records that the forward rod now INTERSECTS the O20.4 spar bore), and merge_cargo_interior.py no longer drills the fuselage side. Retained at qty 0 for traceability. PIN-3X18,3mm OD × 18mm stainless dowel pin — nacelle nozzle flap tangential hinge (Rev S),Hardware,16,0.9,14.4,McMaster-Carr / Amazon,"Stainless dowel pin 3mm x 18mm, McMaster 91585A476 or equiv",0.60,9.60,8 per nacelle × 2; tangential hinge for the Rev R2/S overlapping conical flaps (nacelle_nozzle_iris.scad). Rear nozzle is the fixed canonical elliptical exit (no petals) so it needs no hinge pins. PIN-2X4,2mm OD × 4mm stainless dowel pin — nacelle nozzle flap cam follower (Rev S),Hardware,16,0.1,1.6,McMaster-Carr / Amazon,"Stainless dowel pin 2mm x 4mm, McMaster 91585A302 or equiv",0.40,6.40,8 per nacelle × 2; follower pin rides the unison-ring spiral face-cam slot that drives each flap — REPLACES the retired Rev R1 0.8mm piano-wire iris link ring (the Rev R2/S drive is a rotating spiral cam, no bent wire). SECTOR-GEAR-M05,M0.5 sector gear R=22mm — SUPERSEDED in Rev O by SECTOR-M1-R22 (M=1.0); retain row for Rev N back-compat,Gear,0,5,0,AliExpress / Shapeways / in-house resin,M0.5 sector gear 22mm pitch radius — see SECTOR-M1-R22 for Rev O replacement,8.00,0.00,Rev O: replaced by SECTOR-M1-R22 (M=1.0 module). Qty=0 in Rev O/P build. @@ -53,7 +53,7 @@ FOAM-GASKET-CARGO,3M foam gasket tape 2×2mm — cargo door mating flange seal,C CF-BAR-6X3,CF flat bar 6x3mm — hull keel (TBD length + form),Structural,1,30,30,Amazon / AliExpress,6mm x 3mm carbon fiber flat bar 700mm,12.00,12.00,*** COMPLETE RE-EVALUATION REQUIRED (Rev R1) *** A continuous bow-to-stern keel is structurally justified, but the canonical Serenity hull geometry makes a straight flat bar infeasible: Head_Shell Z_min=+61.2mm vs Cargo Z_min=0mm (≈61mm vertical step at head/cargo joint); Middle_Shell horseshoe is open at −Z (no belly floor to bond to at Y=+130..+204mm). Keel form (straight / pre-bent / segmented-lap / tube / CF-tow-in-foam), cross-section, span (full 620mm or cargo-to-rear-only ≈455mm), Z routing, and RF counterpoise integration must all be re-evaluated against the baked canonical hull cross-sections. Datum marks (91/165/251/320/388mm from nose) are stale — tied to pre-Rev N ring plate stations. Mass and length TBD. BLOCKS keel fabrication + ring plate + foam pour — see TODO §1.1.1 keel re-evaluation task. CF-TUBE-12MM,CF tube 12mm OD x 1.5mm wall x 350mm — wing spar — SUPERSEDED by SPAR-TILT-4130,Structural,0,35,0,Amazon / AliExpress,SUPERSEDED — see SPAR-TILT-4130,10.00,0.00,CARGO-02 (closed 2026-08-24): the fixed 12 mm CF tube through-spar is retired — wings_s1223_revo.scad Rev R2 (2026-07-18) replaced it with an 8 mm OD AISI 4130 rotating tilt-spar per side (SPAR-TILT-4130, already carried in this BOM), which is simultaneously the wing structural spar and the nacelle tilt axis and terminates at the fuselage wall on an F688ZZ bearing (REF-SENSOR-019) rather than crossing the fuselage. Qty=0; retained for traceability per this file's SUPERSEDED convention. Do not re-add a second spar line — SPAR-TILT-4130 is the single active row for this part. CF-PLATE-2MM,2mm CF plate — ring/thwart profiles (3 stations: cargo Y=-40mm fore thwart, Y=+118mm aft thwart, rear Y=+290mm ring),Structural,3,21.4,64.2,Amazon / AliExpress,2mm carbon fiber sheet 200x300mm,20.00,40.00,SUPERSEDED 2026-08-25 (U4, SPAR-01, docs/plans/2026-08-24-001-fix-wing-repair-root-joint-plan.md): the cargo Y=+30mm ring (ring_cargo_Y30_plate_PROVISIONAL.dxf, 77.7 g) is RETIRED -- the wing spar no longer carries through the fuselage (SPAR-01) and this station's own bottom chord is cut away by the clamshell aperture, so it never closed the couple it was sized for; its DXF was already PROVISIONAL (blocked on the MESH-01 fragmented-mesh defect) so no finalized part is lost. Replaced by two CF thwarts (fore Y=-40mm, aft Y=+118mm), sized in tools/wing_spar_carrythrough.py report_thwart() at FOS 8.5/8.7 against the same 300 MPa cross-ply stand-in this file's ring analysis used (NOT a verified allowable -- ASTM D3039/D695 certificates required before fabrication, root TODO.md §0.8): 2mm x 25mm CF plate section, ~14 g each (28 g/0.062 lbm the pair)," locating-groove pockets cut in add_structural_features.py RING_POCKETS[""cargo_Yn40""]/[""cargo_Y118""] (same 4-slab pattern as the existing rings) and wired into merge_cargo_interior.py (the retired Y=+30 pocket is gated off via RING_Y30_ENABLED=False", not deleted). Rear Y=+290mm ring UNCHANGED from the original Rev R1 evaluation (docs/structural_analysis.md §5): ring_rear_Y290_plate.dxf, FINAL, net area 113.2 cm^2, mass 36.2 g @ rho=1.6 g/cm^3 (density per structural_analysis.md §5.6, verified precedent in this repo). Combined mass 64.2 g (Unit_Mass_g/Total_Mass_g are an average across three different-size parts," same convention this row has always used). Thwart DXF plate profiles not yet extracted/exported -- same ""not yet cut"" status the retired ring carried; do not procure until exported." -FOAM-PU-2LB,"Two-part closed-cell PU foam — 2 lb/ft³, 4x expansion",Structural,1,900,900,Amazon / Foam Factory,2 lb/cu ft two-part expanding foam kit (A+B),25.00,25.00,~0.7L mixed fill; place EPS void formers for avionics bays + cargo before pouring; do NOT foam nacelle or open cargo bay +FOAM-PU-2LB,"Two-part closed-cell PU foam — 2 lb/ft³, 4x expansion",Structural,1,22,22,Amazon / Foam Factory,2 lb/cu ft two-part expanding foam kit (A+B),25.00,25.00,"** MASS CORRECTED 2026-08-30 (weight audit): 900 -> 22 g. ** 900 g was the KIT mass (the A+B cans as purchased), not the mass of foam that ends up in the aircraft. At 2 lb/ft^3 (32.04 kg/m^3) the stated ~0.7 L mixed fill is 22.4 g INSTALLED. Measured upper bound for the cargo section alone: enclosed cavity 3.183 L minus the 2.135 L cargo bay (which is explicitly not foamed) = 1.048 L, i.e. 33.6 g before the EPS void formers displace any of it -- so 0.7 L across the whole airframe is the right order. PROCUREMENT quantity is unchanged (1 kit); only the mass column is corrected. The same kit-vs-installed confusion applies to FIL-CF-PETG (2,000 g of spool stock) and the epoxy rows -- see docs/MASS_AUDIT_CARGO_WING_ROOT.md SS4. ~0.7L mixed fill; place EPS void formers for avionics bays + cargo before pouring; do NOT foam nacelle or open cargo bay" WIRE-6AWG,6AWG silicone wire — battery XT60 to Flight Engineer XT60 (main bus),Wiring,1,60,60,Amazon / AliExpress,6AWG silicone wire red/black 200mm each,12.00,12.00,Rev R1: 4AWG→6AWG; main battery positive + negative; 200mm per polarity; 6AWG rated 120A continuous / 200A burst at this length; R=0.84 mΩ ΔV=0.13V at 160A peak — negligible; saves 30g vs 4AWG WIRE-10AWG,10AWG silicone wire — Flight Engineer XT30 outputs to ESC inputs,Wiring,1,80,80,Amazon / AliExpress,10AWG silicone wire red/black 1m each,15.00,15.00,ESC branch wiring only (Flight Engineer XT30 to ESC XT30); 55A rated free air; 1 pair per ESC x4 = ~400mm per polarity total; NOT for main battery bus (use WIRE-4AWG) WIRE-16AWG,16AWG silicone wire — ESC to EDF motor phase leads,Wiring,1,40,40,Amazon / AliExpress,16AWG silicone wire 3m assorted,10.00,10.00,5 ESCs x 3 phases x ~200mm each @@ -64,7 +64,7 @@ FIL-CF-PETG,"CF-PETG filament 1kg spool — 20% chopped carbon fiber, structural FIL-TPU-95A,TPU 95A flexible filament 0.75kg — landing gear feet,Filament,1,250,250,Amazon / Polymaker,Polymaker PolyFlex TPU95 0.75kg 1.75mm,30.00,30.00,~100g required; 0.25mm layer / 40% infill PRINT-HEAD-SHELL,head_shell24_2mm_repaired.stl — Cockpit / nose section (Blender-canonical),Printed Part,1,83,83,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"PRINT FROM: airframe/stls/fuselage/head_shell24_2mm_repaired.stl (baked hull-frame R1; 790036 tri; extents X -232.9..-103.5 / Y -305.7..-70.7 / Z +61.1..+201.5 mm). BLENDER SOURCE: airframe/blender-scripts/files-hollowed-24in/head_shell24_2mm_repaired.stl. SCAD s_head_shell24.scad is secondary reference only. Rev R1: 0.20mm / 5% gyroid (reduced from 8%); saves ~12g. FABRICATION SPLIT at head/cargo joint hull Y≈−71mm — print-split for fabrication access MATERIAL CHANGED 2026-08-25 (owner-directed): plain PETG -> 20% CF-PETG (was PETG — see FIL-PETG), per docs/structural_analysis.md SS6.4 nose-high skid re-derivation and REF-MAT-002 (20% CF-PETG: 77 MPa flexural / 6.67 GPa, verified commercial product).", not a structural joint. STRUCTURAL CONTINUITY REQUIRES ENGINEERING: the Thingiverse reference model was a decorative display piece — no structural provision for UAV flight loads (bending, torsion, shear, vibration). Inter-section load paths must be designed from first principles. See CF-BAR-6X3 keel re-evaluation, CF-PLATE-2MM ring plate re-evaluation, and TODO §1.1.1.0b joint boss design task. Joint face requires minimum 2-wall contact annulus + positive-stop shoulder per CLAUDE.md once structural design is complete. PRINT-MIDDLE-CANONICAL,middle_shell24_2mm_repaired.stl — Fuselage mid-section (Blender-canonical: horseshoe ring + inner neck — ONE piece),Printed Part,1,135,135,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"PRINT FROM: airframe/stls/fuselage/middle_shell24_2mm_repaired.stl (baked hull-frame R1; 855328 tri; extents X -258.5..-81.6 / Y +130.4..+203.6 / Z +1.3..+166.1 mm). BLENDER SOURCE: airframe/blender-scripts/files-hollowed-24in/middle_shell24_2mm_repaired.stl. SCAD s_middle_canonical_shell24.scad superseded. GEOMETRY: ONE printed piece — (1) outer horseshoe ring (U-shaped exterior frame open at −Z/ventral); (2) inner neck (closed tube passing through horseshoe centre MATERIAL CHANGED 2026-08-25 (owner-directed): plain PETG -> 20% CF-PETG (was PETG — see FIL-PETG), per docs/structural_analysis.md SS6.4 nose-high skid re-derivation and REF-MAT-002 (20% CF-PETG: 77 MPa flexural / 6.67 GPa, verified commercial product).", connecting cargo bay interior to rear engine room interior). FLIGHT ENGINEER'S ROOM: the Flight Engineer PDB bay is located inside the inner neck, accessible through the open ventral face of the horseshoe. Mounting boss features for Flight Engineer must be added to the inner neck before printing — see TODO §1.1.1.3. PHASE 5-10: inner neck is a closed tube — aft EDF intake scoops (4 radial cuts for 120mm EDF) are DEFERRED to Phase 11. Print as-is with no intake modifications. 0.20mm / 8% gyroid. Rev R1 skid reinforcement: add 4.2mm bore CF rod channel running full length of each horseshoe-to-skid section (2 channels, coaxial with PRINT-REAR-NECK-INTAKE) — see TODO §1.1.5 skid task. FABRICATION SPLITS at cargo/middle hull Y≈+131mm (fwd face) and middle/rear hull Y≈+203mm (aft face) — print-splits for fabrication access only. Skids are integral extensions of this horseshoe ring; the middle/rear cut was for printability only. STRUCTURAL CONTINUITY REQUIRES ENGINEERING: the Thingiverse model was a decorative display piece — no structural provision for UAV flight loads. The inner neck and horseshoe ring cross-sections were not designed for bending, torsion, or shear. Inter-section and intra-section structural solutions must be designed from first principles. See CF-BAR-6X3 keel re-evaluation, CF-PLATE-2MM ring plate re-evaluation, and TODO §1.1.1.0b joint boss task. The 4mm CF skid rods address skid bending specifically; all other joint load paths remain open design items. -PRINT-CARGO-SECT,cargo_sect_shell24_2mm_repaired.stl — Cargo section (Blender-canonical; interior features pending merge),Printed Part,1,165,165,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"PRINT FROM: airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl (baked hull-frame R1; 1414068 tri; extents X -267.0..-72.7 / Y -71.5..+132.0 / Z 0.0..+163.2 mm). BLENDER SOURCE: airframe/blender-scripts/files-hollowed-24in/cargo_sect_shell24_2mm_repaired.stl. SCAD cargo_sect_shell24.scad (Rev S3) is secondary — interior boss features (door opening MATERIAL CHANGED 2026-08-25 (owner-directed): plain PETG -> 20% CF-PETG (was PETG — see FIL-PETG), per docs/structural_analysis.md SS6.4 nose-high skid re-derivation and REF-MAT-002 (20% CF-PETG: 77 MPa flexural / 6.67 GPa, verified commercial product).", hinge blocks, servo pads, spar bore, avionics standoffs) from SCAD Rev S/S1/S2/S3 must be merged into the Blender mesh before printing; see TODO §1.1.1.2. FABRICATION SPLITS at head/cargo hull Y≈−71mm (fwd face) and cargo/middle hull Y≈+131mm (aft face) — print-splits for fabrication access, not structural joints. STRUCTURAL CONTINUITY REQUIRES ENGINEERING: the Thingiverse model was a decorative display piece — no structural provision for UAV flight loads. Inter-section load paths must be designed from first principles. See CF-BAR-6X3 keel re-evaluation, CF-PLATE-2MM ring plate re-evaluation, and TODO §1.1.1.0b joint boss task. Joint faces require minimum 2-wall contact annulus + positive-stop shoulder per CLAUDE.md once structural design is complete. +PRINT-CARGO-SECT,cargo_sect_shell24_2mm_repaired.stl — Cargo section (Blender-canonical; interior features pending merge),Printed Part,1,372,372,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"** MASS CORRECTED 2026-08-30 (weight audit): 165 -> 372 g, +207 g. ** Measured from the published STL, 354,486 mm^3 x RHO_PRINT 1.05e-3. The 165 g figure predates the Rev R6 landing-gear bay features (~126 g raw), the nacelle-tilt actuator pads (109.5 g at Rev T1c, 47.4 g before the standoff), the Ø30.1 spar socket bosses (24.0 g) and the CF thwart pockets. IT IS NOT A REV T1c REGRESSION -- the pre-Rev-T1c shell already measured 316 g against the same 165 g row. This is the largest single mass error found in the audit; see docs/MASS_AUDIT_CARGO_WING_ROOT.md. PRINT FROM: airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl (baked hull-frame R1; 1414068 tri; extents X -267.0..-72.7 / Y -71.5..+132.0 / Z 0.0..+163.2 mm). BLENDER SOURCE: airframe/blender-scripts/files-hollowed-24in/cargo_sect_shell24_2mm_repaired.stl. SCAD cargo_sect_shell24.scad (Rev S3) is secondary — interior boss features (door opening MATERIAL CHANGED 2026-08-25 (owner-directed): plain PETG -> 20% CF-PETG (was PETG — see FIL-PETG), per docs/structural_analysis.md SS6.4 nose-high skid re-derivation and REF-MAT-002 (20% CF-PETG: 77 MPa flexural / 6.67 GPa, verified commercial product).", hinge blocks, servo pads, spar bore, avionics standoffs) from SCAD Rev S/S1/S2/S3 must be merged into the Blender mesh before printing; see TODO §1.1.1.2. FABRICATION SPLITS at head/cargo hull Y≈−71mm (fwd face) and cargo/middle hull Y≈+131mm (aft face) — print-splits for fabrication access, not structural joints. STRUCTURAL CONTINUITY REQUIRES ENGINEERING: the Thingiverse model was a decorative display piece — no structural provision for UAV flight loads. Inter-section load paths must be designed from first principles. See CF-BAR-6X3 keel re-evaluation, CF-PLATE-2MM ring plate re-evaluation, and TODO §1.1.1.0b joint boss task. Joint faces require minimum 2-wall contact annulus + positive-stop shoulder per CLAUDE.md once structural design is complete. PRINT-REAR-NECK-INTAKE,rear_shell24_2mm_repaired.stl — Rear fuselage / engine bell (Blender-canonical; no EDF hardware Phases 5-10),Printed Part,1,200,200,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"PRINT FROM: airframe/stls/fuselage/rear_shell24_2mm_repaired.stl (baked hull-frame R1; 1095972 tri; extents X -246.1..-105.5 / Y +203.2..+384.3 / Z +3.3..+161.1 mm). BLENDER SOURCE: airframe/blender-scripts/files-hollowed-24in/rear_shell24_2mm_repaired.stl. SCAD s_rear_neck_intake_shell24.scad is secondary. NOTE: the ""neck"" in this part name refers to the rear section transition toward the aft EDF intake (deferred Phase 11) — the inner neck of the MIDDLE section is a separate feature of PRINT-MIDDLE-CANONICAL. PHASE 5-10: print as-is from baked canonical mesh; no EDF intake hardware MATERIAL CHANGED 2026-08-25 (owner-directed): plain PETG -> 20% CF-PETG (was PETG — see FIL-PETG), per docs/structural_analysis.md SS6.4 nose-high skid re-derivation and REF-MAT-002 (20% CF-PETG: 77 MPa flexural / 6.67 GPa, verified commercial product).", thrust tube, or petal nozzle. Rev R1: 0.20mm / 5% gyroid throughout. Skid reinforcement: skid arms are extensions of the PRINT-MIDDLE-CANONICAL horseshoe ring continuing through this section — the middle/rear print joint is a fabrication split for printability only. Add 4.2mm bore CF rod channels coaxially aligned with matching channels in PRINT-MIDDLE-CANONICAL; insert 4mm CF rod (~250mm per skid from CF-ROD-4MM stock) from aft; epoxy; rod provides skid bending stiffness and joint alignment. See TODO §1.1.5. FABRICATION SPLIT at middle/rear joint hull Y≈+203mm — print-split for fabrication access only. STRUCTURAL CONTINUITY REQUIRES ENGINEERING: the Thingiverse model was a decorative display piece — no structural provision for UAV flight loads. The rear cone geometry, skid arms, and dorsal pod were not designed for bending, torsion, landing impact, or vibration loads. Inter-section and local structural solutions must be designed from first principles. The 4mm CF skid rods address skid bending specifically; all other load paths remain open design items. See CF-BAR-6X3 keel re-evaluation, CF-PLATE-2MM ring plate re-evaluation, and TODO §1.1.1.0b joint boss task. PRINT-HEAD-CARGO-COLLAR,head_cargo_splice_collar.stl — Internal head/cargo joint splice collar (Rev R1),Printed Part,1,14,14,In-house 3D print,CF-PETG (20% CF) + hardened nozzle — see FIL-CF-PETG,0.00,0.00,"0.15mm / 100% infill / 4 walls (thin 2mm ring). PRINT FROM airframe/stls/fuselage/head_cargo_splice_collar.stl (hull-frame, spans head/cargo joint Y -79..-63mm; ~13.4g; watertight). GENERATOR airframe/stls/fuselage/generate_head_cargo_splice_collar.py. FUNCTION: secures head (~0.59kg forward cantilever) to cargo — converts the peel-prone thin-shell butt joint into a SHEAR-loaded bonded double-lap with full-perimeter 2-wall contact annulus + anti-ovalisation ring, satisfying the CLAUDE.md joint standard. Joint is NOT strength-limited (worst-case 9g crash <1MPa); collar sized by handling/printability. BOND with West System 105/206 thickened with 406 colloidal silica (bridges the 1-2mm bondline); cure 24h before foam-fill. The 3 boss dowel pins (BOSS_PIN_BORES joint1) are retained for assembly registration only — re-roled from structural. See docs/structural_analysis.md section 4a, TODO 1.1.0." PRINT-CARGO-MIDDLE-COLLAR,cargo_middle_splice_collar.stl — Internal cargo/middle joint splice collar (Rev R1),Printed Part,1,17,17,In-house 3D print,CF-PETG (20% CF) + hardened nozzle — see FIL-CF-PETG,0.00,0.00,"0.15mm / 100% infill / 4 walls (thin 2mm ring). PRINT FROM airframe/stls/fuselage/cargo_middle_splice_collar.stl (hull-frame, spans cargo/middle joint Y +123.1..+139.1mm; ~17.0g; watertight, 1 body). GENERATOR airframe/stls/fuselage/generate_cargo_middle_splice_collar.py. FUNCTION: secures cargo to middle — converts the peel-prone thin-shell butt joint into a SHEAR-loaded bonded double-lap with full-perimeter 2-wall contact annulus + anti-ovalisation ring, satisfying the CLAUDE.md joint standard. Profile taken from the MIDDLE inner wall at Y=+137mm (narrower/cleaner shell at this joint); fit-verified OK against middle over Y 133-139mm. Cargo-side fit checked only against the nearest clean cargo station (Y=+122mm) — the true cargo-side bonding span overlaps the documented cargo MESH-01 defect band; re-verify once that mesh defect is resolved. Joint is NOT strength-limited (real section modulus S_x=31984mm^3 from digitized middle profile; worst-case 9g crash ultimate load 20260 N*mm gives peak fibre stress 0.63MPa, well below the ~5MPa CF-PETG/epoxy allowable); collar sized by handling/printability. BOND with West System 105/206 thickened with 406 colloidal silica; cure 24h before foam-fill. The 3 boss dowel pins (BOSS_PIN_BORES joint2) are retained for assembly registration only. See docs/structural_analysis.md section 7.4, TODO 1.1.1.0b." @@ -74,7 +74,7 @@ PRINT-AFT-PLENUM,s_aft_edf_plenum.stl — PETG plenum manifold (55mm EDF inlet + PRINT-EDF-MOTOR-MOUNT,s_edf_55_motor_mount.stl — CF-PETG 55mm EDF motor mount spider — Phase 11 DEFERRED,Printed Part,0,25,0,In-house 3D print,CF-PETG (20% CF) + hardened nozzle — see FIL-CF-PETG,0.00,0.00,*** PHASE 11 DEFERRED — 3-arm spider motor mount for 55mm rear EDF; REQUIRES REGEN (120mm->55mm, Rev R1); not printed in Phases 5-10 *** PRINT-THRUST-TUBE,s_edf_55_thrust_tube.stl — PETG 55mm EDF thrust tube — Phase 11 DEFERRED,Printed Part,0,20,0,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"*** PHASE 11 DEFERRED — inner duct for 55mm rear EDF; REQUIRES REGEN (120mm->55mm MATERIAL CHANGED 2026-08-25 (owner-directed): plain PETG -> 20% CF-PETG (was PETG — see FIL-PETG), per docs/structural_analysis.md SS6.4 nose-high skid re-derivation and REF-MAT-002 (20% CF-PETG: 77 MPa flexural / 6.67 GPa, verified commercial product).", Rev R1); not printed in Phases 5-10 *** PRINT-WINGS,s_wings_both_shell24.stl — Wing pair (SUPERSEDED by PRINT-WINGS-S1223 in Rev O),Printed Part,0,110,0,In-house 3D print,SUPERSEDED — see PRINT-WINGS-S1223,0.00,0.00,Retained for traceability; flat-plate cross-section replaced by Selig S1223 profile in Rev O -PRINT-WINGS-S1223,s_wing_port/stbd_s1223_revo.stl — Wing pair with Selig S1223 airfoil (Rev O),Printed Part,2,118,236,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,0.15mm / 40% gyroid / 4 walls; S1223 t/c=12.14% max camber=8.65%; SCAD: serenity/stl/s_wings_s1223_revo.scad; CL=1.55 at 3deg AoA +PRINT-WINGS-S1223,wing_port/stbd_s1223_revo.stl — Wing pair; section S1223/t17.7 (root) / S1223/t26.7 (tip) — Rev T1,Printed Part,2,124,248,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,"Rev T1 (2026-08-29): 0.15mm / 40% gyroid / 4 walls. THE SECTION IS NOT AN S1223 — it is the S1223 CAMBER LINE with the thickness envelope scaled x1.46 (root) and x2.20 (tip) to swallow the D20.4 fixed CF spar. Re-derived from the tabulated UIUC coordinates (REF-CAD-006): max t/c 17.72%/26.71% at 19.8% chord; max camber 8.67% at 49.0% chord (UNSCALED — identical to baseline); LE radius 0.0533c/0.1211c. The Rev O note this replaces (t/c=12.14% max camber=8.65%; CL=1.55 at 3deg AoA) describes the TRUE S1223 and DOES NOT apply to this part: CL_max, L/D and the cruise-lift figure are all UNVERIFIED for the built section pending XFOIL/RANS at Re 1.3-1.8e5. Mass 118->124 g/side from the thicker skins. SCAD: airframe/openscad/wings/wings_s1223_revo.scad; spec: docs/WING_ATTACH_INTERFACE.md S4A" PRINT-NACELLE-PORT,s_eng_left_stator_shell24_revo.stl — Port nacelle + CW stator + nav-wire conduit + harness port (Rev O),Printed Part,1,132,132,In-house 3D print,CF-PETG (20% CF) + hardened nozzle — see FIL-CF-PETG,0.00,0.00,0.15mm / 25% gyroid / 4 walls; SWIRL_DIR=+1 PYLON_SIDE=+1; SCAD: nacelle_pod_50mm_tandem.scad; pivot Z=83mm PRINT-NACELLE-STBD,s_eng_right_stator_shell24_revo.stl — Stbd nacelle + CCW stator + nav-wire conduit + harness port (Rev O),Printed Part,1,132,132,In-house 3D print,CF-PETG (20% CF) + hardened nozzle — see FIL-CF-PETG,0.00,0.00,0.15mm / 25% gyroid / 4 walls; SWIRL_DIR=-1 PYLON_SIDE=-1; SCAD: nacelle_pod_50mm_tandem.scad -D SWIRL_DIR=-1 PRINT-PYLON,s_wing_nacelle_pylon_port/stbd_revo.stl — Integrated wing nacelle tilt pylon (Rev O),Printed Part,2,52,104,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,0.15mm / 40% gyroid / 4 walls; hollow harness channel; spar press-fit bore 3.98mm; 4x M2.5 sector insert + 4x M3 wing insert; SCAD: serenity/stl/s_wing_nacelle_pylon_revo.scad @@ -160,13 +160,18 @@ FAR-FT-PANEL,EMI-filtered signal feed-through panel PCB 55×35 mm — all cage c FAR-FERRITE-4MM,Split ferrite clamp 4 mm bore type-31 NiZn — cable EMI at cage wall,EMC/Shielding,16,5,80,DigiKey / Mouser,Würth 742 716 1 series or Fair-Rite 0431167281,1.50,24.00,Rev R1: 4 per cage × 4 cages (reduced from 8); covers all primary cable penetrations: CAN FD ×2 + RS-485 + Ethernet; power and XCVR leads un-clamped (shorter runs inside foam void, low EM coupling risk); ~200 Ω impedance at 100 MHz SKIPPER-FAR-FAN,40×40×10 mm axial fan 5 V DC — Skipper Hammond enclosure cooling,GCS,1,17,17,DigiKey / Mouser,Sunon ME40101V1-000U-A99 (same PN as FAR-FAN-40),6.00,6.00,Rev R new; mount in existing 40 mm fan cutout of Hammond 1455N1601; powered from SKIPPER-PWR-5V-BEC 5 V rail SKIPPER-FAR-GASKET,EMI spring-contact gasket 470 mm × 8 mm — Skipper Hammond 1455N1601 lid seal,GCS,1,5,5,DigiKey / Mouser,Laird Soft Shield 325 or Leader Tech F-100-B wide grade,10.00,10.00,Rev R new; replaces EPDM weatherseal for RF attenuation at lid seam; 470 mm = 2×(145+90) mm perimeter; ≥50 dB attenuation at lid interface -SPAR-TILT-4130,8mm OD x 1.5mm wall AISI 4130 steel tube — rotating tilt spar (hollow 5mm ID for nav wires),Structural Hardware,2,48,96,McMaster-Carr / Aircraft Spruce,4130 chromoly tube 8mm OD 1.5mm wall,9.00,18.00,Rev R2: unified rotating tilt-spar (wing structural spar AND nacelle tilt axis); servo-driven from cargo bay, keyed to nacelle at CG; also carries the Option B nozzle pushrod crank. ~200mm/side. FOS ~13 bend / ~9 torsion (docs/TILT_SPAR_ANALYSIS.md). Hollow 5mm ID routes nav 3-core to outboard nav light. MATERIAL TRADE (docs §3.5): 4130 baseline REQUIRES corrosion finish (zinc/cadmium plate; journals ground — bare tube rusts at bearing journals); 17-4 PH H1075 is the qualified plating-free alternative (same section/mass/stiffness, higher FOS, higher cost/make-from-bar); 7075-T6 is the mass-critical life-limited fallback. FERROMAGNETIC (4130/17-4) — see Hall-sensor rows + EMI WBS §1.4.6. 4130/17-4/7075 allowables require MMPDS/AMS verification — REFERENCES.md / TODO §0.8. -BRG-F688ZZ,F688ZZ flanged/shielded ball bearing 8x16x5mm — tilt-spar ROOT (cargo) bearing,Bearing,2,5,10,Amazon / AliExpress,F688ZZ 8x16x5 flanged shielded,1.80,3.60,Rev R2d: 1 per side — the ROOT (cargo-bay) bearing only; carries the main structural reaction (~9.4 N; x2 dyn ~19 N). The rotating 8mm spar turns in it. Wingtip bearing downsized to BRG-MF128ZZ (see below). Replaces BRG-MF104ZZ. CARGO-01/CARGO-02 (closed 2026-08-24): seats in the cargo shell's re-cut bearing seat (merge_cargo_interior.py ROOT_BRG_*, boss WING_SPAR_BOSS_OD=27.7mm) which now terminates at the fuselage wall (X -100 port / -240 stbd) instead of the old full-span 12.3mm bore. Dimensions verified against REF-SENSOR-019 (SMB Bearings F688ZZ datasheet: bore 8mm/OD 16mm/width 5mm/flange OD 18mm/flange width 1.1mm). -BRG-MF128ZZ,MF128ZZ flanged/shielded ball bearing 8x12x3.5mm — tilt-spar WINGTIP bearing,Bearing,2,2,4,Amazon / AliExpress / VXB,MF128ZZ 8x12x3.5 flanged shielded,2.00,4.00,Rev R2d (2026-07-19): 1 per side — WINGTIP bearing. DOWNSIZED from F688ZZ: the Ø16 seat radius (7.975mm) exceeded the S1223 tip half-thickness (7.80mm) and broke through BOTH airfoil skins by ~0.21mm (wings_s1223_revo.scad TIP_BRG_*, docs/TILT_SPAR_ANALYSIS.md §8). MF128ZZ (Ø12, flange Ø13.5) seats with ~1.79mm skin margin. Wingtip radial reaction ~19 N (dyn) << MF128 dynamic capacity (~700 N) — load-safe. -PRINT-PUSHROD-CRANK,nacelle_pushrod_crank.stl — Option B nozzle-drive crank, clamps the 8mm tilt spar,Printed Part,2,3,6,In-house 3D print,CF-PETG — see FIL-CF-PETG,0.00,0.00,Rev T (2026-07-18): clamps the rotating 8mm spar (SPAR_OD), carries an M3 ball stud at CRANK_R=8.5mm; tilt directly strokes the nozzle unison-ring lever via the pushrod. nacelle_nozzle_pushrod.scad. 0.15mm/40%/4 walls. +SPAR-TILT-4130,8mm OD x 1.5mm wall AISI 4130 steel tube — rotating tilt spar — SUPERSEDED Rev T1 by SPAR-CF-20X16,Structural Hardware,0,48,0,McMaster-Carr / Aircraft Spruce,4130 chromoly tube 8mm OD 1.5mm wall,9.00,0.00,"** SUPERSEDED Rev T1 (2026-08-29/30) -- QTY 0. ** The spar no longer rotates and no longer carries tilt torque: it is a FIXED 20 x 16.3 mm CF tube bonded through the wing over its full span and is the wing's primary bending member (docs/WING_ATTACH_INTERFACE.md SS1, SS2.2). Tilt torque moved to a separate Ø4 mm shaft (SHAFT-TILT-4MM). Two independent facts killed this part: (1) the 8 mm bore cannot carry the four 10 AWG ESC feeds, which need a 13.28 mm circumscribed bundle (SS2.3, REF-MATH-001) -- this is what started the Rev T revision; and (2) a rotating spar cannot react the wing root moment, which is why it needed the tie-rod couple that is also now retired. REPLACEMENT: SPAR-CF-20X16. Retained at qty 0 for traceability per this file's SUPERSEDED convention. Rev R2: unified rotating tilt-spar (wing structural spar AND nacelle tilt axis); servo-driven from cargo bay", keyed to nacelle at CG; also carries the Option B nozzle pushrod crank. ~200mm/side. FOS ~13 bend / ~9 torsion (docs/TILT_SPAR_ANALYSIS.md). Hollow 5mm ID routes nav 3-core to outboard nav light. MATERIAL TRADE (docs §3.5): 4130 baseline REQUIRES corrosion finish (zinc/cadmium plate; journals ground — bare tube rusts at bearing journals); 17-4 PH H1075 is the qualified plating-free alternative (same section/mass/stiffness, higher FOS, higher cost/make-from-bar); 7075-T6 is the mass-critical life-limited fallback. FERROMAGNETIC (4130/17-4) — see Hall-sensor rows + EMI WBS §1.4.6. 4130/17-4/7075 allowables require MMPDS/AMS verification — REFERENCES.md / TODO §0.8. +BRG-F688ZZ,F688ZZ flanged/shielded ball bearing 8x16x5mm — tilt-spar ROOT bearing — SUPERSEDED Rev T1,Bearing,0,5,0,Amazon / AliExpress,F688ZZ 8x16x5 flanged shielded,1.80,0.00,"** SUPERSEDED Rev T1c (2026-08-30) -- QTY 0, and its deletion is not a tidy-up. ** WA-R2: a bearing at the wing root would let the FIXED spar spin under the tilt pinion's gear reaction, which is the one thing this joint must not allow. The seat is replaced by a bonded/clamped socket (merge_cargo_interior.py spar_socket_cuts, Ø20.4 x 18.5 mm deep, shear only at FOS 16) plus the bonded root flange (PRINT-WING-ROOT-FLANGE) that takes the moment at FOS 29.2. Retained at qty 0 for traceability of the Rev R2d/CARGO-01 history. Rev R2d: 1 per side — the ROOT (cargo-bay) bearing only; carries the main structural reaction (~9.4 N; x2 dyn ~19 N). The rotating 8mm spar turns in it. Wingtip bearing downsized to BRG-MF128ZZ (see below). Replaces BRG-MF104ZZ. CARGO-01/CARGO-02 (closed 2026-08-24): seats in the cargo shell's re-cut bearing seat (merge_cargo_interior.py ROOT_BRG_*", boss WING_SPAR_BOSS_OD=27.7mm) which now terminates at the fuselage wall (X -100 port / -240 stbd) instead of the old full-span 12.3mm bore. Dimensions verified against REF-SENSOR-019 (SMB Bearings F688ZZ datasheet: bore 8mm/OD 16mm/width 5mm/flange OD 18mm/flange width 1.1mm). +BRG-MF128ZZ,MF128ZZ flanged/shielded ball bearing 8x12x3.5mm — SUPERSEDED Rev T1,Bearing,0,2,0,Amazon / AliExpress / VXB,MF128ZZ 8x12x3.5 flanged shielded,2.00,0.00,"SUPERSEDED Rev T1 (2026-08-29) — QTY 0. There is no wingtip bearing any more: the spar is FIXED and bonded into the wing, and the tilt bearing moved to the nacelle trunnion ring. A bearing here would now be actively WRONG — it would let the spar spin under the drive pinion gear reaction. Retained for traceability of the Rev R2d F688ZZ->MF128ZZ downsize. REPLACEMENT: 2x SKIPPER-BRG-6804 per side at the nacelle trunnion (20x32x7); the pair totals 14.0 mm and MUST fit inside the 15 mm duct-bounded spar stub (docs/WING_ATTACH_INTERFACE.md S4.3a)." +PRINT-PUSHROD-CRANK,nacelle_pushrod_crank.stl — Option B nozzle-drive crank — SUPERSEDED Rev T1, clamps the 8mm tilt spar,0,2,0,6,In-house 3D print,CF-PETG — see FIL-CF-PETG,0.00,"** SUPERSEDED Rev T1 -- QTY 0. ** It clamps an 8 mm ROTATING spar that no longer exists or rotates. The nozzle drive re-datums onto the FIXED trunnion instead (a genuine ground reference), per docs/WING_ATTACH_INTERFACE.md SS4.1 and plan 004 KTD3/U5. The tilt-servo horn and pushrod it worked with are retired with it. 0.00",Rev T (2026-07-18): clamps the rotating 8mm spar (SPAR_OD), carries an M3 ball stud at CRANK_R=8.5mm; tilt directly strokes the nozzle unison-ring lever via the pushrod. nacelle_nozzle_pushrod.scad. 0.15mm/40%/4 walls. PUSHROD-BALL-M3,M3 ball-link pushrod ~45mm (threaded rod + 2 ball cups) — nozzle drive,Hardware,2,3,6,Amazon / AliExpress,M3 ball link pushrod RC 45mm,2.00,4.00,Rev T: 1 per nacelle; connects the spar crank ball stud to the nozzle unison-ring lever ball stud (PUSHROD_LEN 45mm; ball-cup OD 7mm). Length trim at assembly for the -5deg..140 deg sweep. BALLSTUD-M3,M3 ball stud, Ø3mm ball — pushrod ends (crank + ring lever),Hardware,4,1,4,Amazon / AliExpress,M3 ball stud 3mm ball RC,0.50,2.00,Rev T: 2 per nacelle (one on the spar crank, one on the nozzle ring lever); M3 shank into tapped 2.5mm pilot bores. SKIPPER-TILT-ENC-PCB,Tilt-encoder PCB — AKM AK7455 off-axis magnetic angle IC (SPI) — wing/nacelle true-tilt feedback,Sensor,2,2,4,JLCPCB (fab) + Mouser/Digi-Key (AK7455),in-house 7x7mm PCB; AKM AK7455 14-bit angle sensor QFN24 4x4,6.00,12.00,Rev S (2026-07-19): 1 per nacelle joint; closes the tilt-servo loop on ACTUAL nacelle angle so it is independent of spar torsional wind-up (docs/TILT_SPAR_ANALYSIS.md §1/§3.5). SENSOR = AKM AK7455 (REF-SENSOR-008) — explicitly supports the OFF-AXIS (side-of-shaft) config the through-shaft spar requires (on-axis AS5600/MT6701 will NOT work) + anomaly-magnetic-field detection/reduction + EEPROM INL calibration (suits the ferromagnetic 4130/17-4 spar). 4-wire SPI + ERROR on a 7-wire direct-solder pigtail (GND/+3V3/SCLK/MOSI/MISO/CSN/ERROR); both nacelles SHARE the SPI bus via separate CSN (no I2C address conflict). Seats in the wing-tip pad pocket (wings_s1223_revo.scad wing_tip_hall_sensor_pocket). Pinout/interface VERIFIED vs datasheet 200800064-E-00; schematic ERC 0-error. OPEN bench/layout items: off-axis flux 10-70mT at the IC; INL cal over -5..90 (AKM app support); ERROR push-pull vs open-drain; QFN24 EP left OPEN; wing pocket resize 3x3->4x4. Cost est. — verify AK7455 pricing. Supersedes MT6701 (rejected: on-axis) / AS5600 Rev Q. HALL-RING-MAG,NdFeB ring magnet Ø22 OD x Ø10 ID x 2.5mm — Hall tilt-sensor rotor,Magnet,2,3,6,Amazon / K&J Magnetics,ring magnet 22x10x2.5mm NdFeB,2.00,4.00,Rev S: 1 per nacelle; rides the NON-FERROUS printed hub (PRINT-HALL-HUB) keyed to the rotating spar at the nacelle inboard face; presents the rotating field to SKIPPER-TILT-ENC-PCB (AKM AK7455) OFF-AXIS. Field at the IC must fall in the AK7455 off-axis window 10-70mT (low-flux alarm <~15mT) — magnetization (diametric vs radial) + exact air-gap/offset to be RE-VALIDATED on the bench for the AK7455 off-axis read (set sense plane via R_FIELDSEL; run INL cal). Ø10 ID keeps ≥1mm non-ferrous wall off the 8mm steel spar. OD Ø22 sized for the off-axis IC. _export_pivot_slab.scad nacelle_hall_ring_hub. PRINT-HALL-HUB,nacelle_hall_ring_hub.stl — non-ferrous ring-magnet carrier hub keyed to tilt spar,Printed Part,2,3,6,In-house 3D print,CF-PETG (20% CF) — see FIL-CF-PETG,0.00,0.00,Rev R2e: 1 per nacelle; OD 24mm (carries the Ø22 ring). CF-PETG is non-ferromagnetic (carbon fibre) — stands the ring magnet ID off the ferromagnetic 4130/17-4 spar and proud of the steel bearing to limit field distortion (docs §3.5 / EMI WBS §1.4.6). Keyed/bonded to the 8mm spar. 0.15mm/40%/4 walls. To be merged into nacelle_pod_50mm_tandem.scad. HALL-SCR-M2-BRASS,M2 x 6mm brass pan-head screw — NON-FERROUS Hall-sensor PCB + hub fasteners,Hardware,8,0.3,2.4,Amazon / McMaster-Carr,M2 6mm brass machine screw,0.15,1.20,Rev R2c: 2 per sensor PCB + 2 spare per side; MUST be brass/316/nylon (NOT steel) within the ~10mm keep-out of the encoder IC — ferrous fasteners corrupt the angle read (docs §3.5 / EMI WBS §1.4.6). +SPAR-CF-20X16,20mm OD x 16.3mm ID roll-wrapped CF tube — FIXED wing spar and nacelle tilt axis,Structural Hardware,2,29.1,58.2,Rock West Composites / DragonPlate / Aircraft Spruce,roll-wrapped carbon fibre tube 20mm OD 16.3mm ID,22.00,44.00,"Rev T1 (2026-08-29). Supersedes SPAR-TILT-4130 (8mm rotating steel) and CF-TUBE-12MM. One per side, ~173 mm installed (85.7 mm bonded through the wing + 15.0 mm stub proud of the tip face + the fuselage socket). SECTION I=4,389 mm^4, Z=438.9 mm^3, A=105.5 mm^2; 14.5 g over the wing span, 29.1 g over the full installed run (docs/WING_ATTACH_INTERFACE.md SS2.2). BENDING at the 14.60 N.m ultimate root moment, cantilever bound: sigma=33.28 MPa, FOS 9.0. ** THE ALLOWABLE IS NOT VERIFIED ** -- 300 MPa is the same conservative cross-ply stand-in tools/wing_spar_carrythrough.py uses for the CF thwart plate, carried over because this repo holds NO ASTM D3039/D695 certificate for any CF stock (plan 003 DEP-1, OI-2). Obtain supplier certificates before fabrication. BORE IS SIZED BY WIRE VOLUME, NOT TORQUE: four 10 AWG conductors at Ø5.5 circumscribe 13.28 mm (the exact 1+sqrt(2) four-circle packing ratio, REF-MATH-001, tools/spar_bundle_fit.py) and 1.5 mm of radial clearance lets the bundle twist through the tilt sweep, giving a 16.28 mm minimum bore. ** THE 5.5 mm WIRE OD IS AN ASSUMPTION ** -- WIRE-10AWG records no OD (OI-1). MEASURE THE PROCURED WIRE BEFORE CUTTING CF: a larger real OD scales the whole bore chain and re-opens the airfoil trade. Bonded into the wing with West System 105/206 (0.2 mm/side gap); CLAMPED, not bonded, at the fuselage so wing+spar come off as one assembly (WA-R3)." +SHAFT-TILT-4MM,"4mm OD x ~250mm ground steel shaft — nacelle tilt drive, fuselage to wingtip",Structural Hardware,2,24.7,49.4,McMaster-Carr / Misumi,ground shaft 4mm OD 250mm precision steel,6.00,12.00,"Rev T1 (2026-08-30). Carries tilt torque along chord station 53.6, parallel to the spar, from the fuselage gear pair (GEAR-TILT-FUS-38T) to the tip pinion (WA-R8). Spur pairs at both ends, no right-angle stage -- the shaft is already parallel to the tilt axis, which is the kinematic argument that selected it over a spanwise belt (plan 004 KTD1). LOADS: 0.050 N.m (0.44 lbf.in), from the 0.177 N.m grounded nacelle requirement divided by the 3.571 tip reduction. Wind-up 0.27 deg over the installed length (J=25.13 mm^4, G=79 GPa) = 0.076 deg referred to the nacelle. Runs in BUSH-TILT-4MM at the fuselage wall and at the wing tip rib. FERROMAGNETIC and it moves -- it sits inside the AK7455 non-ferrous keep-out envelope, so the in-situ INL calibration must be run with the shaft and pinion INSTALLED (docs/TILT_DRIVE_CONTROL_SPEC.md SS7.5). Mass: 4mm steel, 250mm, 7.85 g/cm^3 = 24.7 g/side." +BUSH-TILT-4MM,Flanged bronze sleeve bushing 4mm ID x 8mm OD x 5mm — tilt drive shaft support,Bearing,4,1.5,6.0,Igus / McMaster-Carr / Misumi,flanged oil-impregnated bronze sleeve bearing 4x8x5mm,1.50,6.00,"Rev T1c (2026-08-30). Two per side: one in the fuselage root flange, one in the wing tip rib. FUSELAGE SEAT GETS NO BOSS OF ITS OWN, and that is a finding not an omission: the boss-margin rule (bore + 2 x 4.85) wants Ø17.75, but the root mortise forward face is only 4.50 mm from the shaft axis, so any compliant boss is cut in half by the mortise -- and shrinking it to fit leaves 0.48 mm of wall, under this repo 1.16 mm floor. The bonded root flange (PRINT-WING-ROOT-FLANGE) solves it for free: 5 mm of solid material on the inner wall face plus 2 mm of skin behind it gives 7 mm of bearing length, more than a boss would have. Seat Ø8.05 slip fit, bonded with West System 105/206. NOT a rolling bearing: the duty is a slow-oscillating 0.050 N.m shaft, where a sleeve bushing outlives a miniature ball race and costs a quarter as much." +GEAR-TILT-FUS-38T,"Spur gear 38T module 0.8, 20 deg PA, 6mm face, 4mm bore — fuselage tilt drive stage (1:1 pair)",Gear,4,4.0,16.0,KHK / SDP-SI / Misumi,spur gear module 0.8 38 teeth 20 deg PA steel or POM,5.00,20.00,"Rev T1c (2026-08-30). TWO PER SIDE (one on the actuator output spline, one on the drive shaft) -- a 1:1 pair, PD 30.4, tip Ø32.0, centre distance C = 30.40 mm on a pure hull-Z offset. WHY A GEAR PAIR AND NOT A COUPLING: a coaxial actuator does not fit -- the DS3225 body on the drive-shaft axis overlaps the Ø30.1 spar socket boss by 13.5 mm in Y at EITHER orientation of the output shaft, and relieving the boss would cut into the Ø20.4 socket bore. Both axes run along hull X so a plain parallel-axis spur mesh is legal with no right-angle stage. WHY 38T: C is set by the Rev R6 landing-gear bay seats (top out at Z +82.39; the 3.0 mm clearance budget puts the pad bottom at >= +85.39, so C >= 29.80). At module 0.8 the integer 1:1 pairs are 36T (C 28.80, only 2.00 mm of bay clearance), 37T (29.60, 2.80) and 38T (30.40, 3.60 -- clears). WHY 1:1 AND NOT A STEP-UP: a step-up would trade the 48x torque surplus for slew rate, but pulls the ACTUATOR back to 260 deg -- under one revolution -- re-opening the 180-vs-270 limited-rotation question Rev T1 exists to close. MESH PLANE is 11.0 mm inboard of X -100/-240, forced by the wing root tenon: the tenon forward face is 4.90 mm from the shaft axis, so clearing it in Y needs a tip radius under 3.90 mm and NO gear that can transmit at this centre distance qualifies -- it has to be cleared axially. That is what costs the actuator its 18 mm standoff. Load 0.050 N.m; specify steel or POM, not printed -- module 0.8 teeth are at the edge of FDM capability (plan 004 RISK-3)." +PRINT-WING-ROOT-FLANGE,"wing_root_flange_port/stbd.stl — bonded wing-root moment flange, 80 (Z) x 60 (Y) x 5 mm conforming plate",Printed Part,2,25.6,51.2,In-house 3D print,CF-PETG (20% CF) + hardened nozzle — see FIL-CF-PETG,0.00,0.00,"** MASS CORRECTED 2026-08-30 (weight audit): 12.7 -> 25.6 g each. ** The first figure applied 'solid density x 40% infill' (1.30e-3 x 0.40), which is the wrong model for this part and for most of this BOM: at 5 mm thick with 4 perimeters at 0.6 mm the plate is 4.8 mm of perimeter and 0.2 mm of infill -- it is essentially solid, and infill percentage barely touches it. Use merge_cargo_interior.RHO_PRINT = 1.05e-3 g/mm^3, the repo's own bulk as-printed figure (RHO_SOLID = 1.28e-3 brackets it above). 24,336 mm^3 x 1.05e-3 = 25.6 g. Rev T1c (2026-08-30). 0.15mm / 40% gyroid / 4 walls. GENERATOR airframe/stls/fuselage/generate_wing_root_flange.py -> wing_root_flange_port.stl (24,336 mm^3) and _stbd.stl (24,405 mm^3), both hull-frame, watertight single bodies. FUNCTION: WA-R1b, the MOMENT path of the wing root joint. The cargo bay caps socket depth at 18.67 mm and a socket capacity goes as 1/L^2, so at that depth a socket gives FOS 0.51 on the moment -- unrecoverable by any depth the bay allows. A flange reacts over wall AREA instead: F = 3M/(2h) = 274 N over 1,600 mm^2 = 0.17 MPa, FOS 29.2 against the 5 MPa bond-limited CF-PETG figure (docs/structural_analysis.md SS7.3). The bay requirement forced a BETTER joint, not a compromise. WHY IT IS A SEPARATE PART: measured 2026-08-30 by ray-casting the baked envelope on a 13 x 17 grid (221/221 hits), the sidewall skin moves through 34.3 mm (port) / 37.0 mm (stbd) of hull X across the 80 x 60 footprint -- the wall is not flat there, so a printed slab would be up to 34 mm thick and a plane-bounded one is tangent to the skin inside the footprint (measured: a 0.46 mm non-manifold edge, 4 incident faces, plus a sliver body). Same treatment as the CF thwarts and the splice collars: the shell reserves the volume, the part carries the shape. BOND with West System 105/206 to the inner sidewall; cure 24 h before foam pour. Clamped to the spar at the wall by the WA-R3 split collar (NOT YET A PART -- see fuselage-mid WBS WA-R17). Also carries the BUSH-TILT-4MM seat." diff --git a/current-specification/bom_revS.json b/current-specification/bom_revS.json index 1d61d5a..073ceb9 100644 --- a/current-specification/bom_revS.json +++ b/current-specification/bom_revS.json @@ -1019,16 +1019,16 @@ }, { "Ref": "PRINT-WINGS-S1223", - "Description": "s_wing_port/stbd_s1223_revo.stl — Wing pair with Selig S1223 airfoil (Rev O)", + "Description": "wing_port/stbd_s1223_revo.stl — Wing pair; section S1223/t17.7 (root) / S1223/t26.7 (tip) — Rev T1", "Category": "Printed Part", "Qty": "2", - "Unit_Mass_g": "118", - "Total_Mass_g": "236", + "Unit_Mass_g": "124", + "Total_Mass_g": "248", "Supplier": "In-house 3D print", - "Supplier_PN_or_Search": "CF-PETG — see FIL-CF-PETG", + "Supplier_PN_or_Search": "CF-PETG (20% CF) — see FIL-CF-PETG", "Est_Unit_Price_USD": "0.00", "Est_Total_Price_USD": "0.00", - "Notes": "0.15mm / 40% gyroid / 4 walls; S1223 t/c=12.14% max camber=8.65%; SCAD: serenity/stl/s_wings_s1223_revo.scad; CL=1.55 at 3deg AoA" + "Notes": "Rev T1 (2026-08-29): 0.15mm / 40% gyroid / 4 walls. THE SECTION IS NOT AN S1223 — it is the S1223 CAMBER LINE with the thickness envelope scaled x1.46 (root) and x2.20 (tip) to swallow the D20.4 fixed CF spar. Re-derived from the tabulated UIUC coordinates (REF-CAD-006): max t/c 17.72%/26.71% at 19.8% chord; max camber 8.67% at 49.0% chord (UNSCALED — identical to baseline); LE radius 0.0533c/0.1211c. The Rev O note this replaces (t/c=12.14% max camber=8.65%; CL=1.55 at 3deg AoA) describes the TRUE S1223 and DOES NOT apply to this part: CL_max, L/D and the cruise-lift figure are all UNVERIFIED for the built section pending XFOIL/RANS at Re 1.3-1.8e5. Mass 118->124 g/side from the thicker skins. SCAD: airframe/openscad/wings/wings_s1223_revo.scad; spec: docs/WING_ATTACH_INTERFACE.md S4A" }, { "Ref": "PRINT-NACELLE-PORT", @@ -2169,16 +2169,16 @@ }, { "Ref": "BRG-MF128ZZ", - "Description": "MF128ZZ flanged/shielded ball bearing 8x12x3.5mm — tilt-spar WINGTIP bearing", + "Description": "MF128ZZ flanged/shielded ball bearing 8x12x3.5mm — SUPERSEDED Rev T1", "Category": "Bearing", - "Qty": "2", + "Qty": "0", "Unit_Mass_g": "2", - "Total_Mass_g": "4", + "Total_Mass_g": "0", "Supplier": "Amazon / AliExpress / VXB", "Supplier_PN_or_Search": "MF128ZZ 8x12x3.5 flanged shielded", "Est_Unit_Price_USD": "2.00", - "Est_Total_Price_USD": "4.00", - "Notes": "Rev R2d (2026-07-19): 1 per side — WINGTIP bearing. DOWNSIZED from F688ZZ: the Ø16 seat radius (7.975mm) exceeded the S1223 tip half-thickness (7.80mm) and broke through BOTH airfoil skins by ~0.21mm (wings_s1223_revo.scad TIP_BRG_*", + "Est_Total_Price_USD": "0.00", + "Notes": "SUPERSEDED Rev T1 (2026-08-29) — QTY 0. There is no wingtip bearing any more: the spar is FIXED and bonded into the wing, and the tilt bearing moved to the nacelle trunnion ring. A bearing here would now be actively WRONG — it would let the spar spin under the drive pinion gear reaction. Retained for traceability of the Rev R2d F688ZZ->MF128ZZ downsize. REPLACEMENT: 2x SKIPPER-BRG-6804 per side at the nacelle trunnion (20x32x7); the pair totals 14.0 mm and MUST fit inside the 15 mm duct-bounded spar stub (docs/WING_ATTACH_INTERFACE.md S4.3a).", "null": [ " docs/TILT_SPAR_ANALYSIS.md §8). MF128ZZ (Ø12", " flange Ø13.5) seats with ~1.79mm skin margin. Wingtip radial reaction ~19 N (dyn) << MF128 dynamic capacity (~700 N) — load-safe." diff --git a/docs/MASS_AUDIT_CARGO_WING_ROOT.md b/docs/MASS_AUDIT_CARGO_WING_ROOT.md new file mode 100644 index 0000000..18f6ff9 --- /dev/null +++ b/docs/MASS_AUDIT_CARGO_WING_ROOT.md @@ -0,0 +1,350 @@ +# Mass Audit — Cargo Section and Wing Roots (Rev T1c) + +**Revision:** 1 (2026-08-30) +**Author:** Steve Griffing, PE(CSE), CISSP-ISSEP, CPP +**Analysis and drafting:** Claude (Claude Opus 5, Anthropic) under the author's +direction, per `AGENTS.md` §3 "Attribution and Licensing" +**License:** CC BY-SA 4.0 — + +> ⚠️ **ENGINEERING REVIEW REQUIRED — this output is not a substitute for a +> qualified engineer.** Every result, calculation, and recommendation produced +> with this skill **must be independently reviewed and accepted by a properly +> qualified individual** — a licensed Professional Engineer or an equivalently +> qualified authority for the jurisdiction and discipline — **before it is +> applied to any system carrying risk to life or safety.** This skill informs +> engineering judgment; it does not replace it, and it carries no professional +> liability. + +--- + +## 0. Headline + +**The largest mass problem in the cargo section is not geometry, it is +bookkeeping.** Twenty-three printed-part rows in `bom_revS.csv` were checked +against the STL they name. They understate the printed mass by **+521.6 g** in +total — **13.3 % of the 3,911 g (8.62 lbm) AUW** — and the single worst row is +the cargo section itself, at **+207 g**. + +No amount of ribbing recovers 522 g. The BOM has to be reconciled to the meshes +before any lightening decision can be trusted, because the design is currently +being weighed against a number that is not the aircraft. + +Separately, **`FOAM-PU-2LB` carried 900 g in the mass column — the *kit* mass, +not the installed foam.** Installed foam is **22 g**. + +Those two corrections move in opposite directions and do not cancel. + +--- + +## 1. Basis and conventions + +| Quantity | Value | Source | +|---|---|---| +| CF-PETG as-printed bulk density `RHO_PRINT` | 1.05 × 10⁻³ g/mm³ | `merge_cargo_interior.py` (4 perimeters + ≥ 40 % infill) | +| CF-PETG solid density `RHO_SOLID` | 1.28 × 10⁻³ g/mm³ | same | +| PU foam | 2 lb/ft³ = **32.04 × 10⁻⁶ g/mm³** | `FOAM-PU-2LB`, `bom_revS.csv` | +| CF-PETG flexural modulus | 6.67 GPa | REF-MAT-002 (20 % chopped CF, ASTM D790) | +| AUW, Phase 5–10 | 3,911 g (8.62 lbm) | `docs/structural_analysis.md` §3 — a **design-margin back-calculation, not a BOM sum** | + +> **`RHO_PRINT` is a bulk figure and must not be re-derived as +> `RHO_SOLID × infill`.** For anything thinner than about 6 mm the part is +> almost entirely perimeter: a 5 mm plate at 4 × 0.6 mm perimeters is 4.8 mm of +> wall and 0.2 mm of infill, so infill percentage barely moves it. Applying +> `1.30e-3 × 0.40` gives roughly **half** the true mass. That error was made in +> this repository on `PRINT-WING-ROOT-FLANGE` at Rev T1c and is corrected in §5. + +All masses below are `RHO_PRINT` unless stated. Thin-walled proposals in §6 are +quoted at `RHO_SOLID`, which is the honest density for a pure-perimeter section +and makes the saving estimate conservative. + +--- + +## 2. Cargo section — where the mass actually is + +Measured against the published +`airframe/stls/fuselage/cargo/cargo_sect_shell24_2mm_repaired.stl`. + +| Item | Volume (mm³) | Mass | +|---|---:|---:| +| Base shell before any feature (hollow, 2 mm wall) | 370,509 | 389.0 g | +| **Published shell, all features, as built** | **354,486** | **372.2 g** | + +The net is *negative* — the duct removal, the clamshell aperture and the two +mating-face cuts take out more than the bosses and pads put in. That is why the +feature-by-feature table below matters more than the total: it is where the +recoverable mass lives. + +### 2.1 Added features (envelope-clipped, as they land on the hull) + +| Feature | Volume (mm³) | Mass | Note | +|---|---:|---:|---| +| **Actuator pad, port** | 52,673 | **55.3 g** | 24.1 g legacy embed + 31.4 g Rev T1c standoff | +| **Actuator pad, starboard** | 51,599 | **54.2 g** | 23.3 g + 31.4 g | +| Spar socket boss, port | 12,227 | 12.8 g | Ø30.1 (was Ø27.7 / 10.9 g) | +| Spar socket boss, starboard | 10,656 | 11.2 g | Ø30.1 (was Ø27.7 / 9.5 g) | +| Landing-gear bay features (raw, pre-trim) | 119,985 | 126.0 g | Rev R6, pre-existing | +| Root flanges | — | **0 g** | reserved keep-out only; separate bonded parts | + +**The two actuator pads are 109.5 g — the heaviest discretionary feature on the +cargo section, and 29 % of the whole shell.** Before the Rev T1c standoff they +were 47.4 g. They are the obvious target and §6 quantifies them. + +### 2.2 Already recovered at Rev T1c + +Retiring the wing-root tie-rod couple removed four bosses from the shell: + +| | Volume (mm³) | Mass | +|---|---:|---:| +| rod fwd boss, port / starboard | 3,559 / 3,783 | 3.7 / 4.0 g | +| rod aft boss, port / starboard | 1,661 / 2,179 | 1.7 / 2.3 g | +| **shell subtotal** | **11,182** | **11.7 g** | +| plus `CF-ROD-8MM` + `CF-ROD-6MM` stock | — | **22.0 g** | +| **total already banked** | | **33.7 g** | + +--- + +## 3. Wing roots — there is almost nothing to take + +| | Volume (mm³) | Mass | +|---|---:|---:| +| `wing_port_s1223_revo.stl` | 90,992 | 95.5 g | +| `wing_stbd_s1223_revo.stl` | 90,992 | 95.5 g | + +Spanwise distribution, port, 8 slices across the 95.7 mm baked extent +(X −89 = root, X +6.7 = tip): + +| X band | Mass | +|---|---:| +| −89.00 … −77.04 | 6.97 g ← tenon only; the root face is at ≈ −77 | +| −77.04 … −65.07 | **14.54 g** ← root section, the heaviest slice | +| −65.07 … −53.11 | 13.98 g | +| −53.11 … −41.15 | 13.40 g | +| −41.15 … −29.19 | 12.81 g | +| −29.19 … −17.22 | 12.20 g | +| −17.22 … −5.26 | 11.57 g | +| −5.26 … +6.70 | 10.07 g | + +The distribution is a clean taper with no local pile-up, which is the signature +of a wing that is skin-limited rather than feature-limited. **The two dimensions +that set it are both already at their floors:** + +* `WALL_T = 2.5 mm` is **four perimeters at 0.6 mm** — the repo's minimum wall. + Taking it to 2.0 mm is 3.3 perimeters, which is not a wall, it is a + suggestion. **Do not.** +* `THICKNESS_SCALE = 1.46` at the root is **solved, not chosen** — it is the + exact figure that leaves 1.19 mm of skin over the Ø20.4 spar bore against the + 1.16 mm floor (`tools/wing_spar_station_fit.py`). Thinning the section breaks + the bore out of the skin. + +**Recommendation: take nothing from the wing.** The 33.7 g the tie rods gave +back is the whole of what this structure had to give, and it has been taken. + +--- + +## 4. Foam fill and voids + +### 4.1 Cargo section volume budget + +| | Volume (mm³) | Litres | +|---|---:|---:| +| Outer envelope (filled hull) | 3,537,697 | 3.538 | +| less printed shell | −354,486 | −0.354 | +| **enclosed cavity** | **3,183,211** | **3.183** | +| less cargo bay — **explicitly not foamed** (`FOAM-PU-2LB`: *"do NOT foam nacelle or open cargo bay"*) | −2,135,476 | −2.135 | +| **foamable, upper bound** | **1,047,735** | **1.048** | + +At 2 lb/ft³ that is **33.6 g of foam in the cargo section** — and it is an upper +bound, because the EPS void formers (avionics bays ×4, wiring trunk, power bus, +ventilation, Faraday cage pockets — `airframe/placeholders/foam/`) displace part +of it and are removed after cure. + +### 4.2 The 900 g error + +`FOAM-PU-2LB` carried **900 g** in `Unit_Mass_g`. That is the mass of the A + B +kit as purchased. The row's own note says **"~0.7 L mixed fill"**, and 0.7 L at +2 lb/ft³ is **22.4 g**. The measurement above independently supports that order: +the cargo section alone can accept at most 1.048 L. + +**Corrected to 22 g.** Procurement quantity is unchanged — one kit is still one +kit. Only the mass column moves. + +> **This is a pattern, not an isolated row.** The same kit-vs-installed +> confusion sits in `FIL-CF-PETG` (2,000 g of *spool stock*, of which the +> printed parts are the real mass and are already counted on their own rows), +> the West System epoxy row, and the EPS board row. Summing +> `bom_revS.csv` `Total_Mass_g` returns 12.7 kg, which is not the mass of +> anything — it mixes installed hardware, consumable stock, and ground-support +> equipment. **The BOM needs an `Installed` flag before its mass column can be +> used for a weight statement.** Logged as an open item. + +--- + +## 5. BOM-vs-mesh reconciliation + +Every `PRINT-*` row with `Qty > 0` whose description names an STL that exists +was measured. Twenty-three matched. + +| Row | Qty | BOM g/ea | Measured g/ea | Δ | +|---|---:|---:|---:|---:| +| **PRINT-CARGO-SECT** | 1 | 165.0 | **372.2** | **+207.2** | +| **PRINT-BATT-TRAY** | 1 | 22.0 | **140.2** | **+118.2** | +| **PRINT-HEAD-SHELL** | 1 | 83.0 | **177.6** | **+94.6** | +| **PRINT-CARGO-CRADLE** | 1 | 18.0 | **80.6** | **+62.6** | +| **PRINT-MIDDLE-CANONICAL** | 1 | 135.0 | **190.9** | **+55.9** | +| PRINT-REAR-NECK-INTAKE | 1 | 200.0 | 243.2 | +43.2 | +| PRINT-BELLY-PANEL | 2 | 6.0 | 24.5 | +18.5 | +| PRINT-WING-ROOT-FLANGE | 2 | 12.7 | 25.6 | +12.9 | +| PRINT-CARGO-DOOR-PORT | 1 | 45.0 | 14.7 | −30.3 | +| PRINT-CARGO-DOOR-STBD | 1 | 45.0 | 14.8 | −30.2 | +| PRINT-NACELLE-RING | 2 | 18.0 | 6.7 | −11.3 | +| *(12 further rows, each within ±7 g)* | | | | | +| **TOTAL, matched rows** | | **975.2** | **1,496.8** | **+521.6** | + +### 5.1 Corrected in this pass + +* **`PRINT-CARGO-SECT` 165 → 372 g.** Not a Rev T1c regression: the *pre*-Rev-T1c + shell already measured 316 g against the same 165 g row. The row predates the + Rev R6 landing-gear bay features, the actuator pads, the Ø30.1 sockets and the + CF thwart pockets. +* **`PRINT-WING-ROOT-FLANGE` 12.7 → 25.6 g/ea.** This one was our own error, + introduced at Rev T1c: `RHO_SOLID × 0.40` instead of `RHO_PRINT`. See the + warning in §1. +* **`FOAM-PU-2LB` 900 → 22 g.** §4.2. + +### 5.2 Not corrected here, and why + +`PRINT-BATT-TRAY` (+118 g), `PRINT-CARGO-CRADLE` (+63 g), `PRINT-HEAD-SHELL` +(+95 g), `PRINT-MIDDLE-CANONICAL` (+56 g) and `PRINT-REAR-NECK-INTAKE` (+43 g) +belong to other WBS branches. They are reported here with measurements so the +owning branch can act, rather than being edited across a governance boundary. + +**`PRINT-BATT-TRAY` deserves a second look on its own merits**, not just as a +bookkeeping fix: 140 g for a battery tray is 3.6 % of AUW, it sits in the cargo +section, and it is 30 % of its own bounding box — which is a lot of solid for a +part whose job is to locate a LiPo and react 4 g × 1.5 through straps. + +--- + +## 6. Lightening the actuator pads — the one real opportunity + +### 6.1 Loads + +| Term | Value | Basis | +|---|---:|---| +| Gear tangential force | 3.26 N (0.73 lbf) | `T / r` = 0.0496 N·m / 15.2 mm | +| Gear radial force | 1.19 N (0.27 lbf) | `F_t · tan 20°` | +| Gear separation resultant | 3.47 N (0.78 lbf) | `F_t / cos 20°` | +| Actuator inertial load | 3.53 N (0.79 lbf) | 60 g × 9.81 × 4 g × 1.5 | +| **Design load used below** | **5.0 N (1.12 lbf)** | worst-case combined, deliberately conservative | + +### 6.2 The governing failure mode is not strength — it is mesh opening + +A spur pair fails functionally long before it fails structurally: if the mount +deflects, the centre distance changes and the mesh opens. So the check is +**deflection**, not stress. Cantilever, `δ = F L³ / (3 E I)`, `L` = 18 mm, +`E` = 6.67 GPa: + +| Standoff form | `I` (mm⁴) | Deflection at 5 N | +|---|---:|---:| +| Solid slab, as built | 100,875 | 1.44 × 10⁻⁵ mm | +| Hollow box, 3.0 mm wall | 58,043 | 2.51 × 10⁻⁵ mm | +| 4 × Ø12 posts | 15,381 | 9.47 × 10⁻⁵ mm | + +A 0.05 mm centre-distance change is about 6 % of one module and would be +tolerable. **Every option is three or more orders of magnitude inside that.** +The solid pad is not carrying anything; it is there because a box was the easy +primitive. + +*Modes checked and found non-governing:* bearing at the M3 heat-set inserts +(4 inserts sharing 5 N), tear-out at the flange, buckling of the standoff +(L/r ≈ 2, nowhere near slender), and bond shear where the pad fuses to the wall. +Fatigue is not assessed — tilt is a low-cycle duty, but the cycle count is not +established. + +### 6.3 Options + +| Option | Volume/side | Mass/side | **Saving, pair** | Risk | +|---|---:|---:|---:|---| +| As built — solid | 29,889 mm³ | 31.4 g | — | — | +| **Hollow box, 2.5 mm wall + 2.5 mm face** | 11,666 mm³ | 14.9 g | **−32.9 g** | low | +| Hollow box, 3.0 mm wall + 2.5 mm face | 13,061 mm³ | 16.7 g | −29.3 g | lowest | +| 4 × Ø12 posts, no skirt | 8,143 mm³ | 10.4 g | **−41.9 g** | print-orientation risk | + +**Recommended: the 2.5 mm hollow box, −32.9 g.** It is four perimeters — the +same wall the rest of the airframe uses — it prints as part of the shell without +free-standing features, and it keeps a continuous face for the actuator flange. +The post variant saves 9 g more but puts four unsupported 18 mm columns inside +the hull, and their print orientation is set by the cargo shell's, not by what +suits them. + +Masses for the hollow options are quoted at `RHO_SOLID`, not `RHO_PRINT`, +because a thin wall is all perimeter. That makes these savings conservative. + +### 6.4 What NOT to do + +* **Do not shrink `NSVMT_STANDOFF` below 18 mm.** It is + `11 (gear plane) + 6 (gear face) + 1 (hub clearance)`, and the 11 is + `8 (tenon insertion) + 3 (the repo's own GAP_BUDGET to a moving part)`. + Recovering 3 mm buys 10.5 g and spends a stated clearance budget against a + rotating gear. +* **Do not drop the gear face width below 6 mm.** 6 mm is 7.5 × module, already + at the low end of the conventional 6–12 × module band. The load would allow + less; the convention should not be broken for 5 g. +* **Do not thin the legacy 40 mm pad embed without an FEA or a coupon.** It is + worth ~24 g/pair, but it also fuses the pad to a curved wall 3.6 mm from the + landing-gear bay seats, and that interaction has not been characterised. + +--- + +## 7. Revised Rev T1c mass delta + +Correcting the flange density error changes the figure previously recorded: + +| | Δ mass | +|---|---:| +| Cargo shell (Ø30.1 sockets, 18 mm standoffs, less the retired tie-rod bosses) | +56.0 g | +| `SPAR-CF-20X16` 58.2, `SHAFT-TILT-4MM` 49.4, `GEAR-TILT-FUS-38T` 16.0, `BUSH-TILT-4MM` 6.0, `PRINT-WING-ROOT-FLANGE` **51.2** | +180.8 g | +| Retired: `SPAR-TILT-4130` 96, `BRG-F688ZZ` 10, `PRINT-PUSHROD-CRANK` 6, `CF-ROD-8MM` 14, `CF-ROD-6MM` 8 | −134.0 g | +| **Rev T1c net** | **+102.8 g (+0.227 lbm), +2.63 % of AUW** | + +Previously recorded as +77.0 g / +1.97 %. **The earlier figure was low because +the flange was under-massed by 25.6 g.** Corrected here and in +`airframe/fuselage-mid/WBS.md` WA-R18. + +With the §6.3 recommendation applied, Rev T1c lands at **+69.9 g (+1.79 %)** — +below where it was first reported, on a corrected basis. + +--- + +## 8. Findings and actions + +| # | Finding | Action | Owner | +|---|---|---|---| +| **MA-1** | BOM printed-part masses understate by **+521.6 g** across 23 matched rows | Reconcile every `PRINT-*` row to its STL; add a CI check | root `TODO.md` §0.8 | +| **MA-2** | `PRINT-CARGO-SECT` 165 → **372 g** | **CORRECTED** | fuselage-mid | +| **MA-3** | `FOAM-PU-2LB` carried the 900 g **kit** mass, not the 22 g installed | **CORRECTED** | fuselage-mid | +| **MA-4** | `PRINT-WING-ROOT-FLANGE` used `RHO_SOLID × infill`; 12.7 → **25.6 g** | **CORRECTED** | fuselage-mid | +| **MA-5** | Actuator pads are 109.5 g of solid block carrying 5 N | Hollow to 2.5 mm wall: **−32.9 g** | fuselage-mid | +| **MA-6** | `PRINT-BATT-TRAY` measures **140.2 g** vs 22 g, in the cargo section | Re-measure, then lighten on its merits | fuselage-mid | +| **MA-7** | BOM mass column mixes installed mass, consumable stock and GCS | Add an `Installed` flag before any weight statement uses it | specification | +| **MA-8** | Wing roots have **no** recoverable mass — wall at 4-perimeter floor, thickness solved by the bore | None. Record so it is not re-litigated | wings-nacelles | +| **MA-9** | Rev T1c net is **+102.8 g**, not +77.0 g | **CORRECTED** in WA-R18 | fuselage-mid | + +--- + +## 9. Sources + +- `airframe/blender-scripts/merge_cargo_interior.py` — `RHO_PRINT`/`RHO_SOLID`, + feature geometry, `wing_keepout_positives()`, `NSVMT_*`, `TILT_STAGE_*`. +- `airframe/openscad/wings/wings_s1223_revo.scad` — `WALL_T`, + `THICKNESS_SCALE`, `SPAR_BORE_OD`. +- `tools/cargo_bay_envelope.py` — the 140 × 106 × 143.9 mm bay envelope used in §4.1. +- `tools/wing_spar_station_fit.py` — the 1.19 mm skin-over-bore figure that + fixes `THICKNESS_SCALE`. +- `docs/structural_analysis.md` §3 (load factors, AUW basis), §6.4/§7.3 + (CF-PETG allowables). +- `docs/WING_ATTACH_INTERFACE.md` §4.3c — the drive stage the pads carry. +- `REFERENCES.md` REF-MAT-002 (20 % CF-PETG, 77 MPa flexural / 6.67 GPa). +- `current-specification/bom_revS.csv` — rows audited 2026-08-30. +- All volumes measured 2026-08-30 with `trimesh`/`manifold3d` against the + published STLs under `/usr/bin/python3`. diff --git a/docs/TILT_DRIVE_CONTROL_SPEC.md b/docs/TILT_DRIVE_CONTROL_SPEC.md new file mode 100644 index 0000000..057155b --- /dev/null +++ b/docs/TILT_DRIVE_CONTROL_SPEC.md @@ -0,0 +1,416 @@ +# Nacelle Tilt Drive — Control References and Sensors (Rev T1) + +**Revision:** T1 (2026-08-30) +**Author:** Steve Griffing, PE(CSE), CISSP-ISSEP, CPP +**Analysis and drafting:** Claude (Claude Opus 5, Anthropic) under the author's +direction, per `AGENTS.md` §3 "Attribution and Licensing" +**License:** CC BY-SA 4.0 — + +> ⚠️ **ENGINEERING REVIEW REQUIRED — this output is not a substitute for a +> qualified engineer.** Every result, calculation, and recommendation produced +> with this skill **must be independently reviewed and accepted by a properly +> qualified individual** — a licensed Professional Engineer or an equivalently +> qualified authority for the jurisdiction and discipline — **before it is +> applied to any system carrying risk to life or safety.** This skill informs +> engineering judgment; it does not replace it, and it carries no professional +> liability. + +--- + +## 0. Why this document exists + +`docs/WING_ATTACH_INTERFACE.md` §4.3b closed the "180° or 270° servo?" question +by making it void: the tip stage is a **reduction**, so the drive shaft must turn +**more than one revolution** per 140° of nacelle. That has a consequence the +mechanical document states but does not develop: + +> the encoder becomes **load-bearing for control**, not telemetry — a multi-turn +> drive without absolute feedback does not know where the nacelle is. + +The tilt loop is therefore no longer "a servo commanded to an angle." It is a +**cascade** whose outer measurement is the only thing that knows the controlled +variable. This document specifies that loop: what is measured, what the +reference is, what the structure is, and what happens when each part of it +fails. It is the control-side companion to WA-R15. + +**Scope boundary.** This is a control-architecture and instrumentation +specification. It does **not** contain tuned gains, and it says why: the plant +has not been identified, and a tuning set delivered without a plant model and +stated stability margins is not a result, it is a guess. §7 defines the bench +test that produces the model. + +--- + +## 1. The loop + +| Term | Value | +|---|---| +| **Controlled variable (CV)** | Nacelle tilt angle `θ_n`, −5° (cruise) … +140° (hover), 145° of sweep | +| **Manipulated variable (MV)** | Actuator shaft position command, on the RS-485 fleet bus | +| **Measured variable (MV_meas)** | `θ_n`, absolute, from the AK7455 on the wingtip pad reading the trunnion ring magnet (REF-SENSOR-008) | +| **Inner measured variable** | Actuator shaft position, absolute, from the LibreServo board's own encoder | +| **Disturbances** | Aero moment about the tilt axis (**unquantified**, `docs/TILT_SPAR_ANALYSIS.md` §2.1.3); gear backlash across two external meshes; shaft wind-up; gravity residual (bounded, ≤ 0.019 kgf·cm — the pivot is at the nacelle CG) | +| **Actuator limits** | Rate: **not established** (§7.2). Span: unbounded — the rotation-limiting pin is removed, so the actuator has no travel limit of its own. Deadband: **not established** | + +### 1.1 Train, ratio, and sense + +```text +actuator --[fuselage spur pair 38T/38T, m 0.8, C 30.40, 1:1]--> drive shaft +drive shaft --[tip pinion 14T -> ring 50T, m 0.8, C 25.60, i 3.571]--> nacelle +``` + +* Total reduction actuator → nacelle: **3.571**. +* Actuator travel over the full sweep: `145° × 3.571 = 517.8°` = **1.438 rev**. + The actuator is multi-turn. This is what WA-R15 requires and what makes the + AK7455 non-optional. +* **Sense is preserved, not reversed.** Both stages are external meshes and each + reverses; two reversals restore the original sense. Actuator-positive is + nacelle-positive. Declare this in firmware rather than discovering it — a sign + error here drives the nacelle *away* from its setpoint at full authority, and + the fleet precedent for getting it wrong is a commissioning fault, not a + tuning fault. +* Torque referred to the actuator: `0.177 / 3.571 = 0.0496 N·m (0.44 lbf·in)` + against the DS3225's cited 2.402 N·m stall — **2.1 %**, a 48× margin. Torque + is not a design driver anywhere in this loop. + +### 1.2 Resolution, and why the outer sensor is the coarser one + +| | counts/rev | at the nacelle | +|---|---|---| +| AK7455, outer (REF-SENSOR-008) | 16,384 (14-bit) | **0.0220°** — the magnet rides the trunnion, 1:1 with the nacelle | +| LibreServo encoder, inner (REF-SENSOR-014, v2: AEAT-8800) | 65,536 (16-bit) | 0.00154° — divided by the 3.571 reduction | + +The inner sensor is **14× finer at the nacelle** than the outer one. It is still +the wrong sensor to close the outer loop on, because it is upstream of every +compliance in the train: two lash-bearing external meshes and 0.27° of shaft +wind-up (0.076° referred to the nacelle). It measures the actuator accurately +and the nacelle only optimistically. That asymmetry — a precise inner +measurement that cannot see the real disturbance, and a coarser outer +measurement that can — is the textbook case for cascade, not a reason to prefer +one sensor over the other. + +Over the full sweep the AK7455 gives 6,598 counts. Quantisation is not a +limiting error source at any plausible pointing requirement for this axis. + +> **LibreServo_v4 encoder part is UNVERIFIED.** REF-SENSOR-014 documents the +> AEAT-8800 on **v2**. The aircraft carries **v4** (`current-specification/bom_revS.csv` +> `SERVO-TILT`), whose position sensor this repository has not confirmed. The +> 16-bit figure above is therefore v2's, carried as a **class expectation, not a +> v4 datasheet value**. Confirm before it is used for anything but architecture. + +--- + +## 2. Structure — cascade, and why structure beats tuning here + +```mermaid +graph LR + REF["transition schedule
theta_cmd, rate-limited"] --> OUT["OUTER loop (Pilot)
nacelle angle PI"] + AK["AK7455 absolute
nacelle angle"] -->|"14-bit, 0.0220 deg"| OUT + OUT -->|"shaft position cmd
RS-485"| IN["INNER loop (LibreServo_v4)
actuator position/velocity"] + ENC["LibreServo encoder
actuator absolute"] --> IN + IN --> M["motor + 275:1 gearbox"] + M --> G1["fuselage spur 1:1"] --> SH["drive shaft"] --> G2["tip 14T/50T"] --> NAC["nacelle"] + NAC -.->|"the thing actually controlled"| AK +``` + +**Inner loop** — actuator position, closed on the board, fast, local. Rejects +motor and gearbox disturbance (cogging, friction, supply sag) before it reaches +the airframe. + +**Outer loop** — nacelle angle, closed by Pilot on the AK7455 at the flight- +control rate. Absorbs everything the inner loop is blind to: backlash, wind-up, +mesh eccentricity, and the aero moment. + +This is not a stylistic preference. A single loop closed on the actuator cannot +see backlash at all; a single loop closed on the AK7455 must chase motor-level +disturbances through a bus with unmeasured latency. The cascade puts each +disturbance in the loop that can actually reject it. + +**Fleet precedent, and it is exact.** The cargo winch already runs this +architecture — *"continuous rotation, gateway closes position on the AK7455 +spool encoder … multi-turn, unbounded by the servo itself"* +(`REFERENCES.md`, Servo Fleet Standardisation table; `docs/CARGO_WINCH_SPECIFICATION.md` +§3.7.3). Nacelle tilt moves onto the same pattern at Rev T1. Reuse the winch's +gateway command scheme rather than inventing a second one. + +> **The `REFERENCES.md` fleet table is now STALE for this row.** It records +> nacelle tilt as *"Position, firmware soft-limited, −5°…140°"* — i.e. a +> limited-travel application. Rev T1 makes it multi-turn. Corrected in that file +> under WA-R15. + +### 2.1 Rate-limit the reference, do not rate-limit with the integrator + +The transition schedule must present `θ_cmd` to the outer loop through an +explicit slew limiter set **below** the drive's demonstrated capability (§7.2). +A reference the drive cannot follow saturates the actuator, and a saturated +actuator with an integrating outer loop winds up. Anti-windup (clamping or +back-calculation, declared by name in the firmware) is required regardless, but +the rate limiter is what keeps it from being exercised on every normal +transition. + +### 2.2 Feedforward + +* **Gravity: none required.** The pivot is at the nacelle CG, which bounds the + gravity moment at 0.019 kgf·cm (`docs/TILT_SPAR_ANALYSIS.md` §2.1.1) — below + the loop's own resolution. This is a deliberate mechanical choice paying a + control dividend; do not add a gravity term and do not remove the pivot-at-CG + constraint without re-opening this line. +* **Aero: cannot be fed forward.** The aero moment about the tilt axis is + **unquantified** — the repository holds no nacelle `C_d` or frontal-area + figure (§2.1.3). It must therefore be rejected as a disturbance by integral + action in the outer loop, which is the reason the outer loop needs integral + action at all. + +--- + +## 3. Sensors + +| ID | Device | Measures | Interface | Role | +|---|---|---|---|---| +| `ENC-NACELLE-1/2` (`SKIPPER-TILT-ENC-PCB`) | AKM AK7455, 14-bit off-axis (REF-SENSOR-008) | Nacelle absolute angle | SPI, shared bus, separate CSN per side, plus `ERROR` | **Outer loop, control-critical.** Was telemetry through Rev S | +| — | LibreServo_v4 on-board absolute encoder | Actuator absolute position | Internal to the board | Inner loop | +| — | LibreServo_v4 motor current | Actuator torque proxy | RS-485 telemetry | Jam / obstruction detection, §5.3 | + +### 3.1 What changed about the AK7455 installation at Rev T1 + +Three things, all consequences of the spar becoming a fixed CF tube +(`docs/WING_ATTACH_INTERFACE.md` §4.5): + +1. **The distortion source is removed, not mitigated.** The ferromagnetic + 4130/17-4 steel shaft that used to run through the ring centre is gone. + `docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §6.1 still states the old premise and + is **factually wrong until corrected** (WA-R13). +2. **The keep-out is retained anyway.** The 10 mm non-ferrous radius still + governs the fasteners, the nacelle-side collar, and — new at Rev T1 — the + **steel drive shaft and its steel pinion**, which are nearby and moving. + In-situ zero-calibration remains required; it now absorbs the drive train + instead of the spar. +3. **The magnet grew to clear the Ø20 spar** (ID 26 / OD 41.2) and + `HALL_SENS_R` moved 11 → 16.8 mm so the IC still reads mid-annulus. At R = 11 + the IC would have sat 2.0 mm inboard of the magnet's inner edge — off the + magnet, reading nothing. This is a **control-availability** item, not a + packaging one, now that the loop depends on the reading. + +### 3.2 What is deliberately NOT added + +**No limit switches, and no end-stop sensors.** The AK7455 is absolute: it knows +where the nacelle is at power-on with no homing sweep, so soft limits in the +outer loop are sufficient and a switch would add a failure mode without adding +information. Mechanical hard stops remain, as the last resort they are — not as +a control element the loop is allowed to reach in normal operation. + +**No second angle sensor per nacelle.** The two nacelles' AK7455s cross-check +each other (§5.4), which covers the failure that actually matters — differential +tilt — without a redundant part. + +--- + +## 4. References (setpoints), spans, and sign conventions + +Declare all of the following explicitly in firmware and in the ICD. Most +commissioning faults on an axis like this are sign and span errors, not tuning +errors. + +| Quantity | Convention | +|---|---| +| `θ_n` zero | Cruise, nacelle thrust axis aligned with hull +Y (forward) | +| `θ_n` positive | Toward hover (thrust axis rotating toward hull +Z, dorsal) | +| `θ_n` span | **−5° … +140°**, soft-limited; mechanical stops beyond | +| Actuator positive | Same sense as `θ_n` (two external meshes, §1.1) | +| Actuator span | Unbounded in hardware; **soft-limited to `θ_n` × 3.571 = −17.9° … +500.0°** from the calibrated zero | +| AK7455 raw → `θ_n` | Signed, wrapped, with the in-situ calibration offset applied; the sensor's own zero is arbitrary | +| Units on the bus | Engineering units (degrees, ×100 fixed-point), **not** percent of span — the two loops have different spans and a percentage means different things in each | + +**Calibration is a build step, not a firmware default.** The AK7455's zero is set +in situ per aircraft, per side, against the mechanical cruise stop, and the +offset is stored. A unit swapped without re-calibration is a unit that does not +know where the nacelle is. + +--- + +## 5. Failure behaviour + +**A safety function is not derived from a control function.** The items in §5.4 +are architecturally separate from the loops in §2 and must not share their code +path, their sensor conditioning, or their enable logic. + +### 5.1 Loss of the AK7455 (SPI silent, `ERROR` asserted, or reading implausible) + +The outer loop **must not** integrate against a stale or absent measurement. +Required behaviour: freeze the outer loop, hold the last valid actuator position +command, and let the inner loop hold station on its own absolute encoder. + +**Hold, not return-to-zero.** In hover the nacelle *is* the lift vector; slewing +it to cruise on sensor loss is a control input, not a safe state. The aircraft +keeps flying on the attitude it has while the pilot is told. + +Plausibility test: the nacelle cannot move faster than the drive can move it, so +a reading that changes by more than the demonstrated slew rate (§7.2) in one +sample is a fault, not a measurement. + +### 5.2 Loss of the actuator or its bus + +**The train is not self-locking, and this is the most important line in this +document.** Both stages are spur meshes and the total reduction is 3.571 — far +from the ratio and geometry at which a gear train holds itself. With the motor +unpowered, the nacelle back-drives under whatever moment is on it. + +The gravity term is nulled by the pivot-at-CG, so on the ground and in still air +the nacelle will sit. **In flight the aero moment is unquantified**, and an +unquantified moment on a non-self-locking train is an unbounded rate. + +**OPEN — a holding provision is required and is not yet specified (TILT-CTL-01, +§8).** The candidates, in the order this analysis prefers them: + +1. Motor short-brake held by the LibreServo board on loss of command — costs + nothing mechanical, but is only as available as the board's own power. +2. A detent or over-centre latch at the hover and cruise ends — holds without + power, but only at the ends, and adds a mechanism to the tightest region on + the airframe. +3. A worm or lead-screw stage — self-locking by geometry, but it is a + right-angle stage and the whole drive architecture exists to avoid one + (plan 004 KTD1). + +Do not close this by assuming the motor holds. Whether it does is a bench +measurement (§7.3). + +### 5.3 Jam or obstruction + +Actuator current is available as RS-485 telemetry. A current at or near stall +with the AK7455 reading unchanged is a jam. Required behaviour: stop commanding +into it — a 48× torque margin against a 0.050 N·m requirement means the actuator +can comfortably destroy the drive train it is jammed against. + +### 5.4 Differential tilt — the safety layer + +Two nacelles at different tilt angles is a roll and yaw upset in hover, and it is +the failure mode that ends the flight. The two AK7455s make a natural +cross-check. + +Required, and **separate from the two position loops**: + +* Continuous comparison of port and starboard `θ_n`. +* A declared trip threshold and a declared response, both set by the flight- + dynamics case rather than by what the loops happen to achieve. +* Independent enable, so a fault in one position loop cannot suppress the trip. + +**The threshold is not set here.** Setting it requires the roll/yaw authority +available at a given tilt split, which needs the aero data the repository does +not have (§2.2). Tracked as TILT-CTL-02. + +### 5.5 Security + +The actuator is a bus device on the fleet RS-485 segment and the loop's MV +crosses that bus. Command authenticity is a design input, not an add-on +[REF-ISA-001]. LibreServo_v4 carries an OPTIGA Trust M +(`current-specification/bom_revS.csv` `SERVO-TILT`); the fleet position recorded +in `REFERENCES.md` is that the **gateway** signs the frame rather than the servo, +because the fork's own TPM/RS-485 work is schematic-only. Nothing in this +document assumes servo-native signing. + +--- + +## 6. What is NOT specified here, and why + +No gains. No gain or phase margins. No settling time. + +Every one of those is downstream of a plant model, and the plant model does not +exist yet. Specifically unknown: + +* **Dead time** `θ` — RS-485 command latency + inner-loop response + SPI read + + Pilot's own loop period. Dead time, not lag, is what bounds achievable + performance, and `θ/τ` decides whether a single PI is even the right structure + before any gain is chosen. +* **Time constant** `τ` and **process gain** `K_p` of the actuator-plus-train. +* **Backlash amplitude** across two external meshes. It appears as hysteresis + rather than steady-state error because the loop closes on true nacelle angle + (plan 004 RISK-2), but its size decides whether the outer loop can carry + integral action without limit-cycling. +* **Actuator slew rate** (§7.2) — and with it, whether any transition-time + requirement is achievable at all. + +Publishing gains before these are measured would be exactly the error +`docs/TILT_SPAR_ANALYSIS.md` §2.1 already caught once on this axis: a number +picked, then carried forward as though it had been derived. + +--- + +## 7. Bench programme (the work that closes §6) + +### 7.1 Plant identification + +Step and relay tests on one installed nacelle, actuator commanded, AK7455 +logged. Report `K_p`, `τ`, `θ`, and `θ/τ`. Assess controllability from `θ/τ` +**before** choosing a controller structure; a large ratio calls for a different +measurement point or dead-time compensation, not more gain. + +### 7.2 Slew rate — and the stale figure to avoid + +The repository contains **two** tilt-rate figures and neither is a design +requirement (`docs/TILT_SPAR_ANALYSIS.md` §2.1.2): + +* **10 °/s** — a bench *monitoring* rate from a CAN-message-rate test + (`docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §7.3). An instrumentation setting. +* **145° in 500 ms** (290 °/s) — a **stale** Phase-3 build-guide step written + for a different pivot. + +Do not adopt either as the transition requirement. Measure what the built drive +delivers, then ask the flight-dynamics case what it needs. + +> **DS3225 no-load speed is NOT in `REFERENCES.md`** — the `0.18 s/60°` figure +> catalogued there belongs to the superseded **SPT5425LV** (REF-SENSOR-013) and +> must not be reused for the DS3225. Nacelle rate is `actuator rate / 3.571` +> whatever it turns out to be; that relation is exact, the input to it is not +> known. + +### 7.3 Hold test (closes TILT-CTL-01) + +With the actuator unpowered and then with it commanded-to-hold, apply a known +moment at the nacelle and record the angle that results. This is what decides +between the three holding provisions in §5.2. + +### 7.4 Backlash characterisation + +Command a slow reversal and log commanded-vs-measured `θ_n`. The width of the +hysteresis loop is the number §6 needs. + +### 7.5 Encoder installation validation + +Off-axis flux at the IC must fall in the AK7455's 10–70 mT window with the +built ring and the 1.5 mm gap, **with the steel drive shaft and pinion +installed** — they are new since the sensor was selected. Then run the EEPROM +INL calibration over the real −5…+140° sweep. + +--- + +## 8. Open items + +| ID | Item | Blocks | +|---|---|---| +| **TILT-CTL-01** | Holding provision for a non-self-locking train (§5.2). Not closed by assuming the motor brakes. | Flight release | +| **TILT-CTL-02** | Differential-tilt trip threshold (§5.4). Needs roll/yaw authority vs tilt split. | Flight release | +| **TILT-CTL-03** | Plant model — `K_p`, `τ`, `θ` — and gains derived from it with stated margins (§7.1). | Bring-up | +| **TILT-CTL-04** | LibreServo_v4 position-sensor part and resolution unverified (§1.2). | Inner-loop design | +| **TILT-CTL-05** | Actuator slew rate unmeasured; no transition-time requirement exists (§7.2). | Transition schedule | +| **TILT-CTL-06** | Aero moment about the tilt axis unquantified (`TILT_SPAR_ANALYSIS.md` §2.1.3). | TILT-CTL-01, TILT-CTL-02 | +| **WA-R13** | `TILT_ENCODER_WIRING_EMI_SPEC.md` §6.1 states a premise that is now false (§3.1). | Documentation integrity | + +--- + +## 9. Sources + +- `docs/WING_ATTACH_INTERFACE.md` §4.3b (reduction, multi-turn actuator, WA-R15), + §4.5 (encoder changes), §5 (requirement register). +- `docs/TILT_SPAR_ANALYSIS.md` §1 (encoder closes the loop on true nacelle + angle), §2.1.1 (gravity bound), §2.1.2 (the two tilt-rate figures and their + status), §2.1.3 (aero open item), §2.1.4 (grounded torque). +- `docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §2.1, §2.3, §6.1 (§6.1 superseded), + §7.3 (10 °/s bench rate). +- `docs/CARGO_WINCH_SPECIFICATION.md` §3.7.3 — the fleet precedent for a + multi-turn actuator closed on an AK7455. +- `docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md` + KTD1 (parallel-axis argument), RISK-2 (backlash as hysteresis). +- `REFERENCES.md` REF-SENSOR-008 (AK7455), REF-SENSOR-013/014 (fleet servo and + LibreServo v2), REF-ISA-001 (ISA/IEC 62443-3-3). +- `airframe/blender-scripts/merge_cargo_interior.py` `TILT_STAGE_*` — the + fuselage stage geometry this loop drives through. diff --git a/docs/TILT_ENCODER_WIRING_EMI_SPEC.md b/docs/TILT_ENCODER_WIRING_EMI_SPEC.md index 0048947..5f4d072 100644 --- a/docs/TILT_ENCODER_WIRING_EMI_SPEC.md +++ b/docs/TILT_ENCODER_WIRING_EMI_SPEC.md @@ -61,6 +61,16 @@ Within the nacelle: - **Group 1 (SPI signals):** CS, CLK, MOSI, MISO as a single shielded quad (e.g., Lapp ETHERLINE® TORSION or equivalent: 4-pair shielded, foil + braid) - **Group 2 (Power):** +3V3 and GND in separate shielded twisted pair - **Minimum Spacing:** 15 mm separation between Groups 1 and 2 (to prevent capacitive coupling) + - **DOCUMENTED DEVIATION (Rev T1, wing conduit only):** inside the wing both + groups share one Ø6.5 mm bore at chord station 44.5. 15 mm of separation is + not achievable at any bore the S1223 section can hold there, and a second + 15-mm-separated conduit would have to sit in the shallow aft region where + the Ø7 EDF conduits already could not hold wall. Both groups are 100 % + braid-shielded per §2.1, which is the actual mitigation; this rule guards + UNSHIELDED proximity. Separation from the 40 A feeds — the clearance that + matters — is now far better than before: those conductors run inside the + spar's own grounded CF wall, 26 mm forward, rather than in an open conduit + 9 mm away. See `docs/WING_ATTACH_INTERFACE.md` §4.5. - **EDF Feed Clearance:** ≥20 mm horizontal clearance from 40 A power leads (motor + ESC returns); cross perpendicular if unavoidable (to minimize flux coupling) --- @@ -191,7 +201,22 @@ Each CAN-PERIPH-GW-1 gateway (port and starboard nacelles) publishes tilt-angle ### 6.1 Spar Proximity Effects -**Context:** The tilt spar is 4130 steel (ferromagnetic). The AK7455 magnet's field interacts with the ferrous spar, creating residual field distortion that shifts the encoded angle reading. +> **CORRECTED 2026-08-29 (Rev T1, WA-R13).** The premise below is no longer +> true. The tilt spar is now a **fixed roll-wrapped CARBON FIBRE tube** +> (20 × 16.3 mm, `docs/plans/2026-08-29-003-...` KTD4), not 4130 steel, so the +> ferromagnetic-shaft field distortion this section was written about **is +> removed, not mitigated**. The mitigations below are RETAINED anyway, with a +> changed target: the nearby ferrous parts are now the Ø4 mm steel tilt drive +> shaft and its pinion, plus any fasteners and the nacelle-side collar. The +> in-situ zero-calibration therefore stays mandatory — it now absorbs drive-train +> field effects rather than the spar's. §6.2's clearance rules stand unchanged. +> +> Two further Rev T1 changes affect this section's geometry: the ring magnet +> grew to **ID 26 / OD 41.2 mm** (ID 10 could not pass over a Ø20 spar) and +> `HALL_SENS_R` moved **11 → 16.8 mm** so the IC still reads mid-annulus. See +> `docs/WING_ATTACH_INTERFACE.md` §4.5. + +**Context (superseded — see the correction above):** The tilt spar is 4130 steel (ferromagnetic). The AK7455 magnet's field interacts with the ferrous spar, creating residual field distortion that shifts the encoded angle reading. **Mitigation Strategy (already addressed in §3 of AK7455_CALIBRATION_SPECIFICATION.md):** 1. **Zero-calibration** absorbs static spar field effects at each tilt angle diff --git a/docs/TILT_SPAR_ANALYSIS.md b/docs/TILT_SPAR_ANALYSIS.md index 7047ee6..0a38ecd 100644 --- a/docs/TILT_SPAR_ANALYSIS.md +++ b/docs/TILT_SPAR_ANALYSIS.md @@ -374,6 +374,105 @@ H1075** as the qualified plating-free alternative and **7075-T6** as the mass- critical (life-limited) fallback. All allowables pending MMPDS/AMS verification (§7, TODO §0.8). +### 3.6 Re-derivation for a FIXED spar (Rev T1, 2026-08-29) + +> **This section supersedes §3.1–§3.5 for the adopted architecture.** Plan +> `docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md` +> replaces the rotating Ø8 mm shaft with a **fixed, bonded 20 × 16.3 mm +> roll-wrapped carbon-fibre tube** that is a structural member of the wing. +> §3.1's bending case survives in form; §3.2 (torsion) and §3.5's keyability and +> bearing-journal discriminators **do not apply at all** and are marked +> superseded below. + +#### 3.6.1 What stops being a requirement + +Three of the four gates that selected 4130 were consequences of the spar +*rotating* and *transmitting torque*, not of the loads it carries: + +| §3.5 discriminator | Status under a fixed spar | +|---|---| +| **Keyed torque joint** ("CF delaminates at a keyway") | **VOID** — there is no keyway. The spar transmits no torque; the nacelle pivots on its own trunnion ring and tilt torque travels a separate Ø4 mm shaft. | +| **Bearing journal** ("CF abrades the F688ZZ race") | **VOID** — there is no journal. The spar is bonded in the wing and clamped in the fuselage; the bearings moved to the nacelle. | +| **Drive wind-up θ** (§3.2, §3.5) | **VOID** — no torque path, so no wind-up. The Hall-sensor de-rating argument in §3.5 is moot rather than merely reinforced. | +| **Fatigue endurance limit** | **Still relevant**, but the load is now gust-cycled bending on a non-rotating member, not fully-reversed rotating-bending. A fixed spar does not see stress reversal every revolution, which is the case an endurance limit exists to bound. | + +CF was rejected in §3.5 on the **functional** gate, explicitly not on strength. +With that gate gone, its mass advantage becomes available — and the *larger* +spar comes out *lighter* than the small steel one it replaces. + +#### 3.6.2 What sizes the section instead + +**Wire volume, not torque.** The bore must carry four 10 AWG ESC conductors on +the tilt axis. Four Ø5.5 mm circles circumscribe **13.28 mm** (exact 4-circle +packing ratio 1 + √2, [REF-MATH-001], computed by `tools/spar_bundle_fit.py`); +with 1.5 mm radial clearance so the bundle can twist through the −5…+140° sweep, +the minimum bore is **16.28 mm**. On a ~1.85 mm wall that is a **20 mm OD** tube. + +This is why §3.4's rejection of the "12 mm unified structural" candidate as +*"overbuilt; heavier, bigger duct crossing"* does not carry forward: both of its +objections were about a spar that crossed the duct and was sized past its loads. +The Rev T1 spar **never enters the duct** (the trunnion ring at the nacelle +inboard face removes the crossing entirely — see §4), and it is not overbuilt for +its bending duty so much as *sized by a different requirement altogether*. + +#### 3.6.3 Section properties and margins + +20 mm OD × 16.3 mm ID: + +```text +I = π(20⁴ − 16.3⁴)/64 = 4,389 mm⁴ (was 170 mm⁴) +Z = I/c = 438.9 mm³ (was 42.6 mm³ — up 10.3×) +A = 105.5 mm² +``` + +| Case | M | σ | FOS | +|---|---|---|---| +| 1 g | 0.73 N·m | 1.7 MPa | 179 | +| limit (4 g) | 2.94 N·m | 6.7 MPa | 45 | +| ultimate, two-support bound | 4.41 N·m | 10.0 MPa | 30 | +| **ultimate, cantilever bound** | **14.60 N·m** | **33.28 MPa** | **9.0** | + +**The cantilever bound is the quoted figure.** It takes the whole wing-root +moment on the spar alone and credits the printed skin with nothing, because the +skin's share is not characterised for a bonded FDM part. The two-support bound +(load reacted between the wingtip and the fuselage socket) is the physically +likelier case and is 3.3× kinder. + +> **The allowable is a stand-in, not a qualified figure.** 300 MPa is the same +> conservative cross-ply placeholder `tools/wing_spar_carrythrough.py` already +> uses for the CF thwart plate — this repo holds no ASTM D3039/D695 certificate +> for any CF stock (plan 003 DEP-1; `REFERENCES.md` "requires verification"). +> §7's caveat on the 4130 figure applied to one number; this applies to the +> whole row. **Obtain supplier certificates before fabrication.** + +#### 3.6.4 Mass + +| | Rev R2 (8 × 5 4130) | **Rev T1 (20 × 16.3 CF)** | +|---|---|---| +| Density | 7.85 g/cm³ | 1.60 g/cm³ | +| Installed length, per side | ~200 mm | 173 mm (55 socket + 85.7 wing + 32 stub) | +| **Mass per pair** | **96.2 g (0.212 lbm)** | **58.2 g (0.128 lbm)** | + +**−38.0 g (−0.084 lbm) per pair**, against a section modulus 10.3× larger. The +re-lofted wing skin adds ~8.1 g (0.018 lbm) per side and the retired tie-rod +couple removes ~5.1 g per side, so the airframe-structure change is a net +saving; the new Ø4 mm steel drive shaft (~18.7 g per side) spends most of it +back, leaving the whole migration close to mass-neutral before gears. + +#### 3.6.5 Procurement note + +Standard metric CF tube steps are **20 × 16** and **20 × 18**, not 20 × 16.3. +`20 × 16` (2.0 mm wall) gives 1.36 mm radial clearance around the bundle against +the 1.5 mm target — marginally under, and acceptable *provided the bundle can +still twist*, which is the actual requirement the clearance serves. `20 × 18` +(1.0 mm wall) clears easily but halves the wall. **Confirm against a real +supplier** (plan 003 OQ6) before committing, and re-run +`tools/spar_bundle_fit.py --tube ...` against the procured item — and against +the **measured** wire OD, since the 5.5 mm figure is itself an assumption +(plan 003 DEP-2 / OQ4). + +--- + --- ## 4. Airflow — Spar Crossing the Thrust Duct diff --git a/docs/WING_ATTACH_INTERFACE.md b/docs/WING_ATTACH_INTERFACE.md new file mode 100644 index 0000000..d6198bf --- /dev/null +++ b/docs/WING_ATTACH_INTERFACE.md @@ -0,0 +1,682 @@ +# Wing Attachment Interface Specification — Rev T1 + +**Revision:** T1c (2026-08-30) +**Author:** Steve Griffing, PE(CSE), CISSP-ISSEP, CPP +**Analysis and drafting:** Claude (Claude Opus 5, Anthropic) under the author's +direction, per `AGENTS.md` §3 "Attribution and Licensing" +**License:** CC BY-SA 4.0 — + +**Status:** SPECIFICATION — the wing side is built +(`airframe/openscad/wings/wings_s1223_revo.scad`, Rev T1). **The fuselage side +is now built too** (Rev T1c, 2026-08-30 — +`airframe/blender-scripts/merge_cargo_interior.py`, §3 and §4.3c below). The +**nacelle** side is still NOT built; for it, this document remains what it is to +be built to. + +--- + +## 1. Why this document exists + +Rev T1 changes what the spar *is*. Through Rev R2 the tilt spar was a rotating +drive shaft that happened to pass through the wing on two bearings; the wing was +a fairing threaded onto it. Under Rev T1 the spar is a **fixed 20 × 16.3 mm +carbon-fibre tube bonded into the wing over its full span**, and it is the +wing's primary bending member. + +That single change re-writes both ends of the wing at once: + +| | Rev R2 (rotating spar) | **Rev T1 (fixed bonded spar)** | +|---|---|---| +| Wing root moment path | tenon, then two bonded CF tie rods | **the spar itself, into a bonded root flange** | +| Root couple arm | 48 mm (chordwise rod spacing) | **86.7 mm (spanwise support span)** | +| Wingtip | MF128ZZ bearing seat (spar journals in the wing) | **register face; the spar is rigid in the wing** | +| Tilt bearing | wingtip, in the wing | **nacelle trunnion ring** | +| Tilt torque path | the spar | **separate Ø4 mm shaft + spur pair** | +| ESC power | 2 × Ø7 wing conduit, 17.65 mm off-axis | **inside the spar bore, on the tilt axis** | + +Because the load path now crosses two joints this repo's federation splits +across three owners, the numbers have to be stated once, in one place, rather +than three times in three WBS files. **This is that place.** Requirements below +carry an owner and a status. + +--- + +## 2. Basis + +### 2.1 Loads + +From `tools/wing_spar_carrythrough.py`, measured against the baked hull-frame +STLs, at `docs/structural_analysis.md` §3's convention (4 g limit = 3 g gust + +1 g manoeuvre, × 1.5 ultimate — the 1.5 is **14 CFR §23.2230** [REF-FAA-004], +adopted as an engineering baseline, *not* a compliance claim: Serenity is an +sUAS under Part 107, which imposes no structural certification basis), AUW +3.911 kg (8.62 lbm): + +> **§23.2265 [REF-FAA-004] applies to this airframe.** It requires a *special* +> factor beyond the basic 1.5 for parts "subject to appreciable variability +> because of uncertainties in manufacturing processes or inspection methods" — +> which is exactly FDM-printed CF-PETG. The repo's FOS 4.0 joint target is the +> response to that; the requirement is cited, the numeric value is the +> project's own choice. + +| Quantity | Limit | **Ultimate** | +|---|---|---| +| Nacelle load, per side | 76.7 N (17.24 lbf) | **115.1 N (25.87 lbf)** | +| Wingtip reaction `R_tip` | 110.6 N (24.87 lbf) | **165.9 N (37.30 lbf)** | +| Wing root moment | 9.74 N·m (86.2 lbf·in) | **14.60 N·m (129.2 lbf·in)** | +| Wing torsion about the spar axis | 0.068 N·m | **0.41 N·m (3.6 lbf·in)** | + +### 2.2 Spar section + +20 mm OD × 16.3 mm ID roll-wrapped carbon fibre: +`I = 4,389 mm⁴`, `Z = 438.9 mm³`, `A = 105.5 mm²`, 14.5 g (0.032 lbm) over the +85.7 mm wing span, 29.1 g (0.064 lbm) over the full 173 mm installed run. + +Bending at the ultimate root moment, **cantilever bound** (the whole moment +taken by the spar alone, not shared with the skin): +`σ = 33.28 MPa`, **FOS 9.0** against the 300 MPa cross-ply stand-in. + +> **The allowable is not verified.** 300 MPa is the same conservative stand-in +> `tools/wing_spar_carrythrough.py` uses for the CF thwart plate, carried over +> because the repo holds no ASTM D3039/D695 certificate for any CF stock +> (plan 003 DEP-1). Obtain supplier certificates before fabrication. + +### 2.3 Bore sizing + +The bore is sized by **wire volume, not torque**. Four 10 AWG conductors at +Ø5.5 mm circumscribe a **13.28 mm** circle — the exact 4-circle packing ratio +`1 + √2` [REF-MATH-001], computed in `tools/spar_bundle_fit.py`. With 1.5 mm +radial clearance so the bundle can twist through the tilt sweep, the minimum +bore is **16.28 mm**, hence 20 × 16.3. + +> **The 5.5 mm wire OD is an assumption**, not a measurement: +> `current-specification/bom_revS.csv` records no OD for `WIRE-10AWG` +> (plan 003 DEP-2 / OQ4). A larger real OD scales the entire bore chain and +> would re-open the airfoil trade. **Measure the procured wire before cutting +> CF.** + +--- + +## 3. Wing ROOT → fuselage + +### 3.1 Mechanism + +The joint **splits by load type**: a short bonded socket takes the shear, and a +bonded flange on the inner sidewall takes the moment. The tenon locates and +reacts neither. There are no tie rods. §3.3 derives why the split is forced — +the cargo bay bounds the socket depth, and a socket's capacity goes as 1/L². + +```mermaid +graph LR + N["nacelle
115.1 N ultimate"] --> T["wingtip trunnion
on the nacelle"] + T --> S["fixed 20 x 16.3 CF spar
bonded through the wing"] + S --> SK["fuselage socket 18.5 mm
SHEAR only, FOS 16"] + S --> FL["bonded root flange 80 x 60
MOMENT, FOS 29.2"] + FL --> TH["CF thwarts Y -40 / +118
close the couple in the hull"] + SK --> TH + TN["root tenon 12 x 20 x 8"] -.->|"locates only —
no moment"| SK +``` + +### 3.2 Geometry the fuselage must provide + +| Item | Value | Was (Rev S1b) | +|---|---|---| +| Spar station, hull Y | **+21.00 mm** | +38.15 mm | +| Spar height, hull Z | **+66.85 mm** | +68.42 mm | +| Socket bore | **Ø20.4 mm** (20.0 + 0.2/side epoxy gap) | Ø8.3 mm | +| Socket spanwise reach inboard of the wall | **18.5 mm — SHEAR ONLY** | ~19 mm (bearing boss) | +| Bonded root flange on the inner sidewall | **80 (Z) × 60 (Y) mm — MOMENT** | none | +| Root bearing | **NONE — bonded/clamped socket** | F688ZZ | +| Mortise (for the locating tenon) | 12.8 × 20.8 mm | 30.8 × 20.8 mm | + +Hull Y is derived, not chosen: the wing's LE root sits at hull Y −7.0 +(`tools/bake_hull_frame.py`), and the spar is at chord station 28.0 → +`−7.0 + 28.0 = +21.0`. Hull Z likewise: the wing's chord line bakes to Z ++58.0, and the spar rides the **unscaled** camber midline, +8.84 mm at this +station → `58.01 + 8.84 = 66.85`. + +> The camber midline is unscaled deliberately. `s1223_section()` opens the +> thickness envelope *about* the camber line (Rev S1b), so `THICKNESS_SCALE` +> does not move the bore centre. Applying the thickness scale to the midline +> would lift the socket 4.07 mm above the spar it is supposed to receive. + +### 3.3 Why the joint splits in two + +**The cargo bay bounds the socket, and the bound is binding.** Owner +requirement (2026-08-29): the centre of the cargo bay stays clear. The bay's +clear span begins at hull **X −100** and the wall skin sits at **X −81.33**, so +the socket has **18.67 mm** of depth and no more. + +A rigid pin in an elastic socket develops a roughly triangular pressure +distribution either side of the reversal point, resultants at `L/3` from each +end — an effective couple arm of `2L/3`: + +```text +F = 3M / (2L) + V/2 +area = D · L/3 (projected bearing, the CARGO-03c convention) +sigma = F / area ∝ 1/L² +``` + +| Socket L | σ | FOS | | +|---|---|---|---| +| **18.67 mm** | **9.89 MPa** | **0.51** | all the bay allows | +| 30 mm | 3.94 MPa | 1.27 | would enter the bay | +| 55 mm | 1.24 MPa | 4.02 | would enter the bay | + +Stress goes as **1/L²**, so depth is the only lever a socket has — and the bay +has taken it away. **The socket therefore stops being the moment path.** + +**Shear, however, is fine and never needed depth:** +`σ = 115.1 / (20 × 18.5) = 0.31 MPa` → **FOS 16**. + +**The moment moves to a bonded root flange** on the *inner face* of the +sidewall, reacting over wall **area** instead of socket **depth** — so it needs +no inboard reach at all. Triangular pressure over height `h`, arm `2h/3`: + +| h × w | F | area | σ | FOS | +|---|---|---|---|---| +| 40 × 40 | 548 N | 533 mm² | 1.03 MPa | 4.9 | +| 60 × 50 | 365 N | 1,000 mm² | 0.37 MPa | 13.7 | +| **80 × 60** | **274 N** | **1,600 mm²** | **0.17 MPa** | **29.2** | +| 100 × 60 | 219 N | 2,000 mm² | 0.11 MPa | 45.6 | + +**80 × 60 mm is specified.** The cargo section is ~150 mm tall inside at this +station, so 80 mm of height is available without crowding. The flange lies flat +against the wall and protrudes only its own thickness (~5 mm, to X ≈ −86) +against a bay edge at −100. + +**This is better than the 55 mm socket it replaces, not a compromise** — FOS +29.2 against 4.02 — because a flange trades an unfavourable 1/L² depth term for +a linear area term. The bay-clear requirement forced a better joint. + +It also **demotes the LG-11 coupon** here from a gate to a packaging +convenience: at 5 / 15 / 47 MPa the flange gives FOS 29.2 / 87.6 / 274.6. The +coupon would only decide how small the flange could shrink. + +The tube-to-flange transfer is the wing's own 85.7 mm bond, not a separate +fitting: the spar is bonded through the wing root, and the flange is clamped to +the spar at the wall by the same split collar that makes the joint releasable. + +### 3.4 Clamp + +The spar is bonded into the **wing** and clamped into the **fuselage**. That +asymmetry is deliberate: it makes *wing + spar* one serviceable assembly that +comes off the aircraft by releasing the root clamp and withdrawing outboard — +the light-aircraft spar-stub-into-socket pattern — and it puts the releasable +joint where there is room for a collar. + +- Split-collar pinch clamp, ≥ 5 mm wall over Ø20 (i.e. ~Ø30 outside), + M3 heat-set inserts, 2 screws. +- **Positive split gap when clamped.** If the halves close on each other before + they close on the tube, the collar grips itself and the spar is free. +- **No set screws.** CF tube splinters under a point load; the grip must be + distributed (plan 003 RISK-3). + +### 3.6 The flange is a CONFORMING plate, not a plane-bounded slab + +**Built 2026-08-30, and the first attempt was wrong in an instructive way.** + +Every other wing-root positive in `merge_cargo_interior.py` is modelled as deep +material intersected with the hull envelope, which turns it into internal +thickening that fills whatever the curved skin leaves. That idiom is wrong for +this flange, twice: + +- Filling to the bay edge would make it **5–19 mm** thick across the 80 × 60 + footprint instead of 5 — of order 90 g of solid CF-PETG per side, for a plate + whose *specified* 5 mm already buys FOS 29.2. +- Bounding it with a plane at the nominal inner-face station (X −86.33 / −253.37) + instead leaves that plane nearly **tangent** to the skin near the footprint's + forward-lower corner. Measured on the first rebuild: one 0.46 mm non-manifold + edge with 4 incident faces at (−86.33, +8.6, +52.2), plus a 2-face zero-area + sliver body — enough to fail `tools/validate_stls.py` outright. Too long for + the 0.05 mm degenerate-edge collapse to touch, and not a mesh artifact to be + repaired away: it is the model saying the plane does not belong there. + +The flange is therefore built the way it is actually made — a layer of constant +depth measured *from the skin* — by subtracting the hull envelope translated +inboard by `ROOT_FLANGE_T`. The sidewall's normal at this station is within a few +degrees of hull X, so a pure X translation is a wall-parallel offset to better +than the 0.2 mm bond gap, and the plate conforms to the real curvature with no +tangency anywhere. + +### 3.5 Torsion — why no anti-rotation pin + +Wing pitching moment about the spar axis is 0.41 N·m at ultimate +(`Cm ≈ 0.25`, 40 kt, one panel). Reacted as bond shear over a 40 mm socket that +is **0.016 MPa — FOS 306**. Nacelle thrust adds none: the duct axis passes +through the pivot, which is on the spar axis. A round tube in a round bonded +socket is sufficient; the retired aft tie rod had no remaining job. + +--- + +## 4. Wing TIP → nacelle + +### 4.1 Mechanism + +The spar protrudes past the wing tip face and the **nacelle** carries the +bearings. The wing tip provides a register face, a supported bushing boss for +the tilt drive shaft, and the fixed encoder. + +```mermaid +graph LR + W["wing tip face
register pad, X 14..58.6"] --> ST["spar stub
15 mm proud - DUCT BOUNDED"] + ST --> BR["trunnion bearing pair
IN THE NACELLE, ring plane X 28"] + BR --> NR["nacelle rotates
-5 .. +140 deg"] + SH["tilt drive shaft
station 53.6, bushed in the pad"] --> PN["pinion 14T PD 11.2"] + PN -->|"spur REDUCTION i=3.571
C = 25.6"| RG["tilt ring gear 50T PD 40.0
on the trunnion"] + RG --> NR + AK["AK7455 on the wing pad
R 16.8, fixed"] -.->|"reads across
1.5 mm air gap"| RM["ring magnet ID 26 / OD 41.2
on the trunnion"] + NV["nav 3-core, wing bore st. 8"] --> XJ["crosses at the trunnion"] + XJ --> LT["outboard nav light
rotates with the nacelle"] +``` + +### 4.2 Geometry the nacelle must provide + +| Item | Value | Note | +|---|---|---| +| Spar stub proud of the wing tip face | **15.0 mm — DUCT-BOUNDED** | The spar must TERMINATE at ≥ 26 mm from the duct axis (see §4.3a). Max 15.7; 15.0 built. **The bearing pair must fit inside it** — 2 × 6804 (20 × 32 × 7) = 14.0 mm does. | +| Trunnion bearing bore | **Ø20.0 H7** | on the nacelle, ring plane X ≈ 28 mm from the duct axis | +| Bearing duty | axial **and** radial, 21.9 N each at 4 g × 1.5 | thrust is axial to the spar in cruise and transverse in hover — a stack chosen for one attitude is wrong for the other (plan 004 RISK-4) | +| Ring gear | **50T, module 0.8, PD 40.0 mm** | concentric with the spar; root Ø 38.0 clears it with 9 mm of hub each side; OD 41.6 inside the 53.4 envelope | +| Pinion | **14T, module 0.8, PD 11.2 mm** | on the wing's drive shaft; 14T is the no-undercut floor at 20° PA | +| Reduction ratio | **3.571** | shaft turns **1.389 revolutions** per 140° of nacelle | +| Gear centre distance | **25.6 mm** | → wing shaft at chord station 53.6 | +| Ring magnet | **ID 26 / OD 41.2 mm**, diametric | mean radius 16.8 = `HALL_SENS_R` | +| Magnet axial gap to the AK7455 face | **1.5 mm** | set by the nacelle standoff | +| Non-ferrous zone | ≥ 10 mm radius around the IC | see §4.5 | +| 4 × 10 AWG disconnect | **in the nacelle annulus** | see §4.4 | + +### 4.3a The spar stub is bounded by the thrust duct + +**Owner requirement (2026-08-29): the spar must not penetrate the nacelle +thrust tube.** That is a hard geometric bound and it is tighter than the bearing +stack would like. + +The duct is a cylinder of `r = 25 mm` about the nacelle's local Z axis. The spar +runs along local X at `Y = 0`, so every point of it at station X sits +`√(X² + Y²) ≥ |X|` from the duct axis. The spar therefore clears the duct **iff +it terminates at `|X| ≥ 26`** (25 plus the 1 mm margin plan 003 R4 states): + +```text +wing tip face |X| = 37.7 (NACELLE_OD_X/2) + 4.0 (joint gap) = 41.7 mm +spar must stop |X| = 26.0 mm +=> MAXIMUM STUB 15.7 mm (15.0 built, 0.7 in hand) +``` + +The 32.0 mm stub this document previously specified would have reached +`|X| = 9.7` — **fifteen millimetres inside the duct wall**, straight through the +thrust column between the two EDFs. It was budgeted outward from the bearing +stack and never checked against the duct, which is the same class of error as +the Rev R2 through-duct spar this whole revision exists to remove. + +**The bearing stack must fit inside 15 mm.** Two thin-section 6804 (20 × 32 × 7) +total 14.0 mm and fit; 6804 is already a BOM item (`SKIPPER-BRG-6804`). A deeper +stack does not fit and must not be assumed. + +### 4.3b The drive is a reduction — the shaft turns more than one revolution + +**Owner direction (2026-08-29):** the actuator drives the shaft through **more +than a single revolution** to sweep the nacelle 140°. That inverts the tip +stage, and it dissolves a problem this document previously reported as +unsolvable. + +An earlier pass assumed a limited-rotation hobby servo (180°/270°), which forces +a **step-up** — ring smaller than pinion. Since the ring gear is concentric with +the spar and must clear Ø20, that produced an impossibility: at plan 004 KTD4's +`C = 15 mm` the algebra returns `PD_ring = 10.5 mm`, a ring gear smaller than +the spar it encircles. + +**As a reduction the ring is the larger member and the geometry closes easily.** +With `i = N_ring / N_pinion`, the shaft turns `140° × i`: + +| N_ring | PD | i | shaft rev | C | station | ring OD | root Ø | | +|---|---|---|---|---|---|---|---|---| +| 36 | 28.8 | 2.571 | 1.000 | 20.00 | 48.00 | 30.4 | 26.8 | exactly one rev, not "more than" | +| 45 | 36.0 | 3.214 | 1.250 | 23.60 | 51.60 | 37.6 | 34.0 | | +| **50** | **40.0** | **3.571** | **1.389** | **25.60** | **53.60** | **41.6** | **38.0** | **selected** | +| 54 | 43.2 | 3.857 | 1.500 | 27.20 | 55.20 | 44.8 | 41.2 | leaves 1.1 mm to the tenon — under the floor | + +*(module 0.8, 14T pinion, PD 11.2)* + +**50T is selected.** It also leaves the AK7455 pocket a 10.2 mm chordwise window +between the spar bore's aft edge (38.2) and the shaft's forward edge (51.4) — +the encoder radius, the board width and the gear centre distance are one coupled +set, and none of the three is independently free. + +**Consequence for the actuator, and it is a real change.** A multi-turn output +means the drive is **no longer a limited-rotation servo**. It is a +continuous-rotation gearmotor or a stepper, closed on the AK7455's absolute +nacelle angle rather than on the actuator's own travel. That: + +- **retires the 180°-vs-270° question**, which was blocking; +- removes the 145°-of-travel constraint plan 004 KTD5 identified as the + *binding* one (torque never was — the reduction delivers 3.571× whatever the + actuator gives, against a 0.177 N·m grounded requirement); +- makes the AK7455 **load-bearing for control**, not just telemetry: without + absolute feedback a multi-turn drive has no idea where the nacelle is. + +Shaft torque is 0.050 N·m; wind-up over the installed length is 0.27° (Ø4 steel, +`G` = 79 GPa). **Re-open the actuator selection** — the DS3225 was already ~17× +oversized on torque and is now also the wrong *kind* of device. + +### 4.3c The fuselage end of the shaft — and why it needs a gear pair too + +**Built 2026-08-30.** §4.3b settled what the actuator must BE. This settles +where it goes, and the answer was not the obvious one. + +A coaxial coupling from the actuator straight onto the drive shaft is the +simplest thing that could work, and it does not fit. Put the DS3225 body on the +drive-shaft axis (hull Y +46.60, Z +69.09) and it overlaps the Ø30.1 spar socket +boss by **13.5 mm in Y and the full 20 mm in Z**, at *either* orientation of the +output shaft along the 40 mm body — the shaft sits 24 mm from one end, so the +two available placements are Y +22.6…+62.6 and Y +30.6…+70.6, and both eat the +boss. Relieving the boss to clear it would cut into the Ø20.4 socket bore. + +So the fuselage end carries a spur pair as well. Both axes run along hull X, so +it is a plain parallel-axis mesh with no right-angle stage — the same kinematic +argument that chose the tip stage (plan 004 KTD1), applied at the other end. +Inside the fuselage there is no airfoil to pay for it. + +| Item | Value | Why this value | +|---|---|---| +| Stage ratio | **1:1** | see below | +| Module / teeth | **0.8, 38T / 38T** (PD 30.4, tip Ø32.0) | first integer 1:1 pair clearing the bay floor | +| Centre distance | **30.40 mm, pure hull +Z** | forced by the landing-gear bays | +| Actuator axis | hull **Y +46.60, Z +99.49** | shaft axis + C | +| Gear plane | **11.0 mm inboard** of X −100 / −240 | forced by the wing root tenon | +| Actuator mounting face | X **−118.0** / **−222.0** | gear plane + face + hub clearance | + +**The centre distance is set by the landing gear, not chosen.** The Rev R6 bay +seats top out at Z +82.39 and the actuator pad overlaps both bays in Y, so Z +separation is the entire margin. With the repo's 3.0 mm clearance budget the pad +bottom must sit at Z ≥ +85.39, i.e. the actuator axis at Z ≥ +98.89 — a centre +distance of at least 29.80 mm. At module 0.8 the integer 1:1 pairs are 36T +(C 28.80, 2.00 mm of bay clearance — under budget), 37T (29.60, 2.80 — still +under) and **38T (30.40, 3.60 — clears)**. + +**The stage is 1:1 on purpose.** A step-up would trade surplus torque for slew +rate, and the surplus is real: 2.402 N·m of actuator stall against 0.050 N·m at +the shaft, a 48× margin. It is rejected because it pulls the *actuator* back +below one revolution (500° of shaft ÷ 1.923 = 260°), which re-opens the +180-vs-270 limited-rotation question §4.3b exists to close. 1:1 keeps the +actuator at 1.438 rev over the full 145° sweep. + +**The gear plane is forced by the tenon, and this is the costly one.** Two +solids share the shaft's X band at the wall: + +- the Ø30.1 spar socket boss — axes 25.70 mm apart, so a shaft gear may have a + tip radius of at most `25.70 − 15.05 − 1.0 = 9.65 mm`; +- the wing root **tenon** (X −100…−108, Y +51.50…+63.50) — its forward face is + 4.90 mm from the shaft axis, so clearing it *in Y* needs a tip radius under + **3.90 mm**, i.e. under 10 teeth at module 0.8. + +The second bound does not yield to gear sizing: **no gear that can transmit at +this centre distance clears the tenon in Y.** It has to be cleared *axially* +instead, which puts the mesh inboard of the tenon's inboard face and drags the +actuator 18 mm further into the bay with it — reaching X −158.5 / −221.5 instead +of −140.5 / −239.5. + +**That cost is real and it is recorded, not absorbed.** It is 18 mm per side, in +the hull Z band +85.99…+112.99. That band is above the bay's working floor — the +pad is datumed to the landing-gear bay tops and the CARGO-01 payload envelope is +measured upward from the closed-door crown at Z +8.72 — so what is lost is roof +volume rather than floor footprint. It is tracked at +`airframe/fuselage-mid/WBS.md` WA-R15. + +**Sense.** Two external meshes, two reversals: actuator-positive is +nacelle-positive. Total reduction 3.571; actuator travel 1.438 rev over 145°. +The control-side consequences — cascade structure, sensor roles, fail states — +are specified in [`docs/TILT_DRIVE_CONTROL_SPEC.md`](TILT_DRIVE_CONTROL_SPEC.md). + +--- + +### 4.4 The power disconnect belongs in the nacelle, not the wingtip + +Plan 003 U3 specified a wingtip "maintenance garage" holding the four 10 AWG +bullet disconnects. **It does not fit and is reassigned to the nacelle.** + +Any 10 AWG disconnect — ring-terminal studs, bullets, or blade tabs — needs +~6 mm of clear height. Aft of the spar the tip section falls away fast +(measured at `THICKNESS_SCALE_TIP` 2.20): + +| Chord station | 40 | 44 | 54 | 66 | 78 | +|---|---|---|---|---|---| +| Tip section depth (mm) | 17.50 | 15.60 | 11.20 | 6.78 | 3.43 | + +Subtracting 2 × `WALL_T` of skin leaves 12.5 mm at station 40 and 1.8 mm by +station 66 — over a chordwise run too short to lay four disconnects out in, and +that volume is already claimed by the AK7455 conduit and the drive shaft. + +The nacelle has the volume: plan 003's own routing already lands the bundle in +*"the annular space between the duct wall (r = 25) and the outer skin"* before +ESC1/ESC2. Putting the break there also keeps the 40 A joint out of the same +pocket as the AK7455 plug, which is what +`docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §2.3 requires and what a shared wingtip +garage would have violated. + +**Service model is unchanged and needs no hatch:** sliding the nacelle off the +spar exposes the entire wing tip face. **The nacelle is the cover.** This is the +nacelle-off-spar model already adopted in +`docs/plans/2026-08-26-001-nacelle-esc-intake-integration-plan.md`. + +### 4.5 Encoder — what changed and what did not + +**Unchanged:** AK7455 (SPI, off-axis) stays selected. AS5600 and MT6701 stay +rejected — both are on-axis-only parts and there is still no free shaft end, +because the spar bore is now full of the power bundle. + +**Changed, and the nacelle must follow:** + +1. **The spar is no longer ferromagnetic.** Rev R2's magnetic-siting problem was + a 4130/17-4 PH steel shaft through the ring centre distorting the bias field + (`docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §6.1). The Rev T1 spar is carbon + fibre. **That distortion source is removed, not mitigated** — and §6.1's + stated premise is now factually wrong and needs correcting there. + The non-ferrous keep-out is **retained anyway**, because it also governs the + fasteners and the nacelle-side collar, and the drive shaft and pinion *are* + steel and *are* nearby. In-situ zero-calibration stays required; it now + absorbs the drive train rather than the spar. +2. **The ring magnet had to grow.** It rides a collar on the spar, and the spar + went Ø8 → Ø20; ID 10 cannot pass over a Ø20 tube. ID 26 / OD 41.2 clears the + spar plus a 3.0 mm non-ferrous collar and fits inside the trunnion ring's + measured 53.4 mm envelope (plan 003 OQ2). +3. **`HALL_SENS_R` 11 → 16.8 mm** so the IC still reads **mid-annulus**. This is + not cosmetic: a diametric ring's field is only clean over the annulus, and an + IC left at R = 11 would sit 2.0 mm inboard of the magnet's inner edge (r 13.0) — off + the magnet entirely. +4. **The ring magnet and the ring gear are nearly coradial** (magnet annulus + r 13.0–20.6, gear PD r 20.0, gear OD r 20.8) and must therefore be **axially + separated** on the trunnion — and both must fit within the 15 mm the duct + allows the stub (§4.3a). Nacelle to resolve; this is the tightest packaging + constraint the joint has. + +--- + +## 4A. The built section is not an S1223 — designation and data + +Re-derived 2026-08-29 by sampling `S1223_UPPER` / `S1223_LOWER` at 1/20000 +chord. These are computed from the tabulated UIUC coordinates [REF-CAD-006], +not quoted from a datasheet. + +**Designations.** The built sections share S1223's *camber line* and nothing +else, so they carry their own names: + +| | designation | meaning | +|---|---|---| +| root | **S1223/t17.7** | S1223 camber line, thickness envelope × 1.46 | +| tip | **S1223/t26.7** | S1223 camber line, thickness envelope × 2.20 | + +The *filename* keeps `s1223` for continuity of the git/STL/BOM trail. The +*section* does not. + +| | baseline | root built | tip built | +|---|---|---|---| +| thickness scale | 1.000 | 1.460 | 2.200 | +| max t/c | 12.14 % | **17.72 %** | **26.71 %** | +| at x/c | 0.198 | 0.198 | 0.198 | +| max camber | 8.67 % | 8.67 % | 8.67 % | +| at x/c | 0.490 | 0.490 | 0.490 | +| LE radius r/c | 0.02502 | 0.05333 | 0.12110 | +| LE radius, absolute | 3.23 mm @ c 129 | 6.88 mm @ c 129 | 11.26 mm @ c 93 | + +**Correction to the previous header.** It claimed max thickness *"at 22.6 % +chord"* and max camber *"at 39.4 % chord"*. Both magnitudes were about right; +both **chordwise locations were wrong**, and neither was traceable to the +coordinate table the file actually builds from. The re-derivation returns +19.8 % and 49.0 % — exactly the published S1223 characterisation. + +Camber is identical across all three columns because `s1223_section()` scales +thickness *about an unscaled camber line*. LE radius goes as `t_scale²`, a +consequence of scaling a fixed shape's thickness envelope, verified numerically +(0.02502 × 1.46² = 0.05333; × 2.20² = 0.12110). The tip's leading edge is 12 % +of its own chord — geometrically closer to a strut-fairing nose than to a +low-Reynolds high-lift section. + +### What published S1223 data still applies + +**Flow regime first**, because it governs which methods are even legal. +`Re = V·c/ν` at 40 kt (20.58 m/s), `ν = 1.46 × 10⁻⁵ m²/s` (ISA SL, 15 °C): + +| station | chord | Re | +|---|---|---| +| root | 129 mm | ≈ 182,000 | +| MAC | 111 mm | ≈ 156,000 | +| tip | 93 mm | ≈ 131,000 | + +All below `Re = 5 × 10⁵` — the **low-Reynolds regime**, where laminar +separation bubbles dominate and published polars do not transfer between +Reynolds numbers, let alone between sections. Chord is unchanged at Rev T1, so +Re is unchanged. + +| Claim | Status | +|---|---| +| max camber 8.67 % at 49.0 % chord | **Exact** for the built sections — camber is unscaled by construction, not approximated | +| zero-lift angle, lift-curve slope | **Partially survives at the root only.** Thin-airfoil theory makes `dc_l/dα = 2π` and `α_(L=0)` functions of the camber line alone, independent of thickness — which is *why* preserving the camber line matters. But that theory is valid for **thin** sections; 17.7 % is already outside its comfortable range and 26.7 % is emphatically outside. Optimism at the root; nothing at the tip | +| `CL ≈ 1.55` at 3° AoA | **Does not survive** | +| `CL_max ≈ 2.0` | **Does not survive.** `c_l,max` comes only from measurement or computation at the actual Re — never from theory, never carried across sections | +| `L/D ≈ 30–35` | **Does not survive** | +| 7.6 N cruise lift, "~22 % AUW" | **Does not survive** — derived from the above | +| printed surface finish | **Never characterised.** FDM layer lines act as a de-facto trip strip whose effect at these Re is real and unquantified for this part | + +**Establishing real numbers** needs XFOIL or a transition-sensitive RANS run at +Re 1.3–1.8 × 10⁵, or a bench/tunnel result. A fully-turbulent RANS model will +misrepresent the separation bubble. Tracked in `TODO.md` §0.8 and +`docs/flight_envelope.md`. + +> ⚠ **Engineering review required.** These are computed geometric properties +> and a statement of what is *not* known. They are not a performance +> substantiation and must be reviewed and accepted by a qualified engineer +> before any flight-relevant decision rests on them. + +--- + +## 5. Requirement register + +| ID | Requirement | Owner | Status | +|---|---|---|---| +| **WA-R1** | Spar socket at hull Y +21.00, Z +66.85, Ø20.4, 18.5 mm deep — SHEAR path (FOS 16) | fuselage-mid | **BUILT** (T1c) | +| **WA-R1b** | Bonded root flange 80 (Z) × 60 (Y) mm on the inner sidewall — MOMENT path (FOS 29.2). Nothing inboard of hull X ≈ −86; the bay stays clear | fuselage-mid | **BUILT** (T1c) — as a *conforming* 5 mm plate, not a plane-bounded slab; see §3.6 | +| **WA-R2** | Root bearing (F688ZZ) deleted; bonded/clamped socket replaces it | fuselage-mid | **BUILT** (T1c) | +| **WA-R3** | Split-collar pinch clamp, ≥ 5 mm wall, M3 inserts, positive split gap | fuselage-mid | **OPEN** — the Ø30.1 socket boss now matches the clamp's ~Ø30 envelope, but the clamp itself is not yet a part | +| **WA-R4** | Mortise re-sized 30.8 → 12.8 mm wide for the locating tenon | fuselage-mid | **BUILT** (T1c) — tenon fit +0.40 mm/side in both axes | +| **WA-R5** | Cargo-bay envelope unchanged — **CLOSED by design**: the joint no longer enters the bay | fuselage-mid | **CLOSED** | +| **WA-R6** | Fuselage-side conduits for the nav 3-core (Y +1.0) and the AK7455 pair (Y +37.5), and a drive-shaft bore at Y +46.6 | fuselage-mid | **BUILT** (T1c) — Ø4.2 / Ø7.5 / Ø4.4, plus a Ø8.05 shaft-bushing seat in the root flange | +| **WA-R7** | Trunnion bearing bore Ø20.0, ring plane X ≈ 28, axial + radial duty | wings-nacelles | **OPEN** | +| **WA-R8** | Tilt ring gear 50T module 0.8 (PD 40.0) concentric with the spar, C = 25.6 to the wing shaft; reduction 3.571 | wings-nacelles | **OPEN** | +| **WA-R9** | Ring magnet ID 26 / OD 41.2, axially separated from the ring gear, both inside the 15 mm stub | wings-nacelles | **OPEN** | +| **WA-R10** | 4 × 10 AWG disconnect in the nacelle annulus, partitioned from the AK7455 plug | wings-nacelles | **OPEN** | +| **WA-R11** | Nav 3-core crosses at the trunnion, radially separated from the power bundle | wings-nacelles | **OPEN** | +| **WA-R12** | Trunnion bearing pair within the **15 mm** duct-bounded stub (2 × 6804 = 14.0 mm fits); no member closer than 26 mm to the duct axis | wings-nacelles | **OPEN** | +| **WA-R15** | Actuator re-select: multi-turn, not a limited-rotation servo (§4.3b) | avionics / fuselage-mid | **RESOLVED** (T1c) — DS3225 body + LibreServo_v4 with the rotation-limit pin removed, run continuous-rotation and closed on the AK7455, per the cargo-winch precedent. Same body, so the pad footprint and bolt pattern are unchanged; mount position and standoff are new (§4.3c) | +| **WA-R13** | `TILT_ENCODER_WIRING_EMI_SPEC.md` §6.1 corrected — the spar is no longer ferromagnetic | avionics | **OPEN** | +| **WA-R14** | Wing side: bores, pad, root path, thickness scales | wings-nacelles | **BUILT** (Rev T1) | + +--- + +## 6. Open items + +- **OI-1 — Wire OD.** `WIRE-10AWG` has no recorded OD. Everything in §2.3 scales + with it. **Blocks CF tube procurement.** +- **OI-2 — CF allowable.** No ASTM D3039/D695 certificate exists for any CF + stock in this repo. The 300 MPa figure is a stand-in (plan 003 DEP-1). +- **OI-3 — CLOSED as a gate.** The LG-11 coupon no longer decides whether the + root joint is buildable: the flange clears FOS 4.0 by 7× at the standing 5 MPa + figure (FOS 29.2 / 87.6 / 274.6 at 5 / 15 / 47 MPa). It now only sets how + small the flange could shrink — a packaging convenience. +- **OI-4 — CLOSED.** The 180°-vs-270° question is void: the drive is multi-turn, + so the actuator's own travel no longer sets the ratio (§4.3b). **Replaced by + OI-7.** +- **OI-7 — CLOSED 2026-08-30.** The actuator is a **DS3225 body carrying the + LibreServo_v4 board with its rotation-limit pin removed**, run as a + continuous-rotation multi-turn unit and closed on the AK7455 — the same + architecture the cargo winch already uses (`REFERENCES.md`, Servo Fleet + Standardisation; `docs/CARGO_WINCH_SPECIFICATION.md` §3.7.3). It is not a new + part number: the *body* is unchanged, so the pad footprint, bolt pattern and + 60 g mass all survive from Rev S1d. What changed is the operating mode, the + mount position, and the 18 mm standoff (§4.3c). The "~17× oversized" note was + right in direction and understated in size — the real figure at the built + reduction is **48×** — but a smaller body is not available on this path: + LibreServo's smallest documented target is a standard-size (40 mm) servo. + The encoder is now **load-bearing for control**, not telemetry; that is + specified in [`docs/TILT_DRIVE_CONTROL_SPEC.md`](TILT_DRIVE_CONTROL_SPEC.md), + which also opens TILT-CTL-01…06 — the items a multi-turn drive brings with it, + chief among them that **the train is not self-locking and has no specified + holding provision.** +- **OI-8 — Trunnion packaging (new, open).** The bearing pair, the ring gear and + the ring magnet must all fit within the 15 mm the duct allows, and the magnet + and gear are nearly coradial. This is now the joint's tightest constraint. +- **OI-5 — Aero revalidation.** The section is no longer S1223: root t/c + 12.14 → 17.72 %, tip 18.93 → 26.70 %. **Every aero figure in the repo that + cites this wing is unverified**, including the 7.6 N cruise-lift figure in + `wings_s1223_revo.scad`'s header and everything derived from it. Do not + present the re-lofted wing as an S1223 performance match (plan 003 RISK-1). +- **OI-6 — CLOSED.** The bay is not intruded; the joint was re-designed around + the requirement rather than trading against it (§3.3). + +--- + +## 7. Verification + +```text +/usr/bin/python3 tools/spar_bundle_fit.py +/usr/bin/python3 tools/wing_spar_station_fit.py --bore 20.4 --station 28 +/usr/bin/python3 tools/wing_airfoil_integrity.py +/usr/bin/python3 tools/wing_internal_clearance.py --verbose +/usr/bin/python3 tools/wing_spar_carrythrough.py +/usr/bin/python3 tools/validate_stls.py +/usr/bin/python3 tools/cargo_bay_envelope.py +/usr/bin/python3 tools/landing_gear_wing_clearance.py --proud +/usr/bin/python3 tools/wing_root_deconflict.py # RED until WA-R1/R6 land +``` + +`wing_root_deconflict.py` **used to fail by design** at Rev T1b, on three +findings that were all one fact — the fuselage still carried +`WING_SPAR_Y = +38.15` and `WING_SPAR_BORE_D = 8.3`. Rev T1c moves the fuselage +side and the gate is expected to pass. Three changes were needed in the tool +itself, and each was a real defect rather than a re-tune: + +| Change | Why | +|---|---| +| `tenon_params()` selected `_LOCATING` only for the literal path `"two_rod"` | so the Rev T1 default `"spar_carrythrough"` fell through to `_ENLARGED` and the tool measured a 30 mm structural tenon the wing does not build, reporting a 8.60 mm/side mortise foul against a correctly-sized mortise. The SCAD writes the rule as a negative (`!= "enlarged_tenon"`) precisely so new paths inherit the safe size; mirroring it as a positive whitelist inverted that | +| the spar corridor was swept at one diameter | it steps at the socket's inboard end — Ø20.4 outboard, Ø16.3 inboard — so a single-diameter sweep reads the intended 2.05 mm annulus as "wall not cut" | +| every route was probed 12 mm past the wall bracket | correct for a rigid shaft, wrong for a wire. The Ø32.0 shaft gear sits 9.11 mm from the AK7455 conduit axis and **no** gear train at a 30.40 mm centre distance clears it; the lead turns forward into the wire trunk at the wall, and probing past the bracket reports that intended turn as a blockage on every run | + +--- + +## 8. Sources + +- `docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md` + — architecture, KTD1–KTD8, frozen station/thickness figures. +- `docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md` + — drive trade study. Its KTD4 shaft station is corrected in §4.3. +- `docs/TILT_SPAR_ANALYSIS.md` §2.1 (torque), §3 (superseded 8 mm section), + §4 (duct blockage). +- `docs/TILT_ENCODER_WIRING_EMI_SPEC.md` §2.1, §2.3, §6.1 (§6.1 superseded by + §4.5 above). +- `docs/structural_analysis.md` §3 (load factors), §7.3 (5 MPa bond-limited + figure). +- `REFERENCES.md` REF-MATH-001 (packing), REF-MAT-001/002 (CF-PETG), + REF-SENSOR-008 (AK7455), REF-FAA-003 §91.209(a) (nav lights). +- Geometry measured 2026-08-29 by `tools/wing_spar_station_fit.py`, + `tools/wing_internal_clearance.py`, `tools/wing_spar_carrythrough.py`, and + `tools/spar_bundle_fit.py` against the Rev T1 sources. diff --git a/docs/flight_envelope.md b/docs/flight_envelope.md index 91e84d5..06ffdcc 100644 --- a/docs/flight_envelope.md +++ b/docs/flight_envelope.md @@ -27,13 +27,41 @@ result materially. | T/W, Phase 11 (hover, nacelles only) | 1.43 | `README.md` | | Nacelle tilt range | −5° to 140° hard stops; 0° = cruise (horizontal), 90° = hover (vertical), 120° = backing thrust | `README.md` "Nacelles" | | Wing area (both wings) | 19,025 mm² (0.019025 m², 0.2048 ft²) | `airframe/openscad/wings/wings_s1223_revo.scad` | -| Airfoil | Selig S1223, t/c = 12.14%, camber = 8.65% | `wings_s1223_revo.scad`; UIUC Airfoil Database | -| CL_max | ≈ 2.0 at Re = 100,000 | Selig & Guglielmo wind-tunnel data, cited in `wings_s1223_revo.scad` | +| Airfoil | **S1223/t17.7 (root) / S1223/t26.7 (tip)** — S1223 camber line, thickness envelope scaled ×1.46 / ×2.20. **NOT an S1223.** t/c 17.72 % / 26.71 % at 19.8 % chord; camber 8.67 % at 49.0 % chord (unscaled) | `wings_s1223_revo.scad` "SECTION DESIGNATION AND DATA"; `docs/WING_ATTACH_INTERFACE.md` §4A | +| CL_max | **UNKNOWN for the built section** — see the banner below. The 2.0 figure used throughout this document is true-S1223 wind-tunnel data and is retained only so the numbers remain reproducible | Selig & Guglielmo, for the *unmodified* S1223 | | Root/tip chord, semi-span | 129 mm / 93 mm, 85.7 mm | `wings_s1223_revo.scad` | | Air density (ISA sea level) | ρ = 1.225 kg/m³ | Standard atmosphere | --- +> ## ⚠ EVERY AERODYNAMIC RESULT IN THIS DOCUMENT IS UNVERIFIED AS OF REV T1 +> +> **The wing section changed on 2026-08-29 and this document has not been +> re-derived against it.** The Rev T1 spar migration scales the thickness +> envelope ×1.46 at the root and ×2.20 at the tip to swallow a Ø20.4 spar, so +> the built sections are **S1223/t17.7** and **S1223/t26.7** — not S1223. +> +> The **camber line is preserved exactly** (8.67 % at 49.0 % chord), which is +> the reason to expect the change to be less severe than the thickness numbers +> alone suggest: thin-airfoil theory makes zero-lift angle and lift-curve slope +> depend on the camber line and not on thickness. But that theory is valid for +> *thin* sections, and at 26.71 % t/c the tip is far outside its range. +> +> **`CL_max ≈ 2.0` does not carry over.** `c_l,max` depends on the thickness +> distribution, the leading-edge radius (now 2.1× the baseline at the root and +> 4.8× at the tip), and the laminar separation bubble — all three changed. It +> can only come from measurement or computation at the actual Re, never from +> theory and never from a different section. +> +> Every V_min, stall-speed, and lift figure below therefore rests on an input +> that no longer describes the aircraft. They are retained unchanged so the +> derivations stay reproducible and so re-running them is a substitution rather +> than a rewrite. **Do not fly to these numbers.** +> +> Closing this needs XFOIL or a transition-sensitive RANS run at +> Re 1.3–1.8 × 10⁵ (the operating range — a fully-turbulent model will +> misrepresent the bubble), or a bench/tunnel result. Tracked in `TODO.md` §0.8. + ## 1. V_min — Minimum Control Airspeed vs. Nacelle Tilt Angle This is a tilt-rotor, not a fixed-wing aircraft: weight is supported by a @@ -71,12 +99,15 @@ with zero forward airspeed (θ_hover, solving sin(θ) = W/T): | 120° (backing thrust) | 0 kt | 0 kt | Vertical component still exceeds weight (sin 120° = 0.866) | | 140° (hard stop) | 0 kt | ≈ 0 kt, thin margin | Vertical component only 1.0 N above weight at Phase 5–10 AUW — **do not rely on 140° for sustained hover margin**; treat as a transient/backing-thrust extreme, not a hover trim point | -**Important caveat on CL_max:** the 2.0 figure is wind-tunnel data at -Re ≈ 100,000; this airframe's cruise Reynolds number at the wing root is -≈ 177,000 at 40 kt (`wings_s1223_revo.scad`), where CL_max is expected to be -at least as good, typically better, for the S1223 — using the lower-Re -figure here is conservative (over-predicts V_min slightly rather than -under-predicting it). No wind-tunnel or CFD validation at the actual +**Important caveat on CL_max — strengthened at Rev T1.** The 2.0 figure is +wind-tunnel data at Re ≈ 100,000 for the **unmodified** S1223. This airframe's +cruise Reynolds number at the wing root is ≈ 182,000 at 40 kt (ISA SL; +≈ 177,000 at 20 °C), and for a *true* S1223 the higher Re would make the +lower-Re figure conservative. +**That argument no longer applies**, because the built section is not an S1223 +(see the banner at the top of this document). The Re comparison is still valid; +the section comparison is not, and the section change is the larger effect. +Treat CL_max as UNKNOWN rather than conservative. No wind-tunnel or CFD validation at the actual operating Re has been performed for this specific planform; treat the table above as a design estimate pending flight test, not a flight-tested limit. diff --git a/docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md b/docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md index 8ccbc66..7b1d076 100644 --- a/docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md +++ b/docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md @@ -43,6 +43,80 @@ it replaces** (67.5 g vs 96.2 g per pair, −0.063 lbm). --- +## Implementation record — wing side BUILT 2026-08-29 (Rev T1) + +**U1, U2, U3, U5 (wing half), and plan 004 U3 (wing half) are implemented.** +The joint requirements this plan hands to the fuselage (U7) and the nacelle +(U4, U6, U8) are consolidated, with their numbers, in +[`docs/WING_ATTACH_INTERFACE.md`](../WING_ATTACH_INTERFACE.md) — build to that, +not to this plan's prose. + +**Frozen figures confirmed by tool, not accepted on trust.** `THICKNESS_SCALE` +1.456 and `THICKNESS_SCALE_TIP` 2.190 at station 28.0 reproduce exactly +(`tools/wing_spar_station_fit.py --bore 20.4 --station 28`), as does +`SPAR_Z` 66.85 and the 13.28 mm circumscribed bundle. Both scales were rounded +UP in the source (1.46 / 2.20) per this file's own established convention — +Rev S1b took 1.447 → 1.45 and U6 took 1.5505 → 1.56 so the wall is not sitting +on its own limit. Built walls are 1.19 mm (root) and 1.21 mm (tip). + +**Four things in this plan did not survive implementation. Each is recorded +where it was found, not silently worked around:** + +1. **U3's wingtip split-collar pinch clamp — MOVED TO THE ROOT.** A collar that + grips Ø20 CF without crushing it needs ~Ø30 outside; the re-lofted tip + section is 22.83 mm deep at its deepest. It cannot exist there. It would + also have nothing to do: the spar is bonded over 85.7 mm (5,492 mm² of bond, + ~27 kN of axial retention at 5 MPa), and a bonded spar cannot be withdrawn, + so a clamp at that end cannot make the joint serviceable either. The clamp + belongs at the joint that is designed to open — the fuselage socket. Wing + + bonded spar are now one serviceable assembly. +2. **U3's wingtip maintenance garage — MOVED TO THE NACELLE.** Any 10 AWG + disconnect needs ~6 mm of clear height; aft of the spar the tip section falls + 17.50 → 3.43 mm over stations 40 → 78, and what depth exists is already + claimed by the AK7455 conduit and the drive shaft. This plan's own routing + already lands the bundle in the nacelle annulus, which has the volume — and + putting the break there also keeps the 40 A joint out of the AK7455's pocket, + which `TILT_ENCODER_WIRING_EMI_SPEC.md` §2.3 requires and a shared garage + would have violated. No hatch is needed: the nacelle is the cover. +3. **U9's tie-rod expectation — the couple is RETIRED, not re-sized.** The + forward rod (station 14.0, Ø8.2 → 9.9..18.1) now intersects the Ø20.4 spar + bore (17.80..38.20) outright, and the aft rod's only remaining job would be + wing torsion — 0.41 N·m at ultimate, bond shear 0.016 MPa, FOS 306. A fixed + bonded spar reacts the root moment directly, which is what neither the tenon + nor the rods could do, so the whole work-around retires with it. +4. **RISK-2 was not actually covered by its own gate.** `wing_airfoil_integrity.py` + validated the tabulated table — i.e. the section at `t_scale` 1.0 — and never + read either thickness scale, so it would have passed identically at 1.0 and + 2.20. It now validates the sections `wing_solid()` actually lofts. Both are + valid closed polygons at 1.46 / 2.20; RISK-2 is closed on evidence rather + than on a gate that could not see it. + +**Open questions closed:** OQ3 (servo range) is **CLOSED and void** — the drive +is multi-turn, so the actuator's own travel no longer sets the ratio. OQ4 +(wire OD) remains open and blocking. See the interface spec §6. + +**Three further owner directives landed 2026-08-29 after the first build pass, +and two of them invalidated built geometry:** + +1. **The spar must not penetrate the thrust duct.** The 32 mm stub reached + |X| = 9.7 against a duct wall at 25 — fifteen millimetres inside it. Cut to + **15.0 mm** (max 15.7). The nacelle's bearing pair must now fit inside that. +2. **The centre of the cargo bay must stay clear.** That rules out the 55 mm + socket U7 was scoped around; only 18.67 mm exists before the bay. The joint + splits instead — socket for shear (FOS 16), bonded 80 × 60 flange for the + moment (FOS 29.2) — which is *better* than the socket it replaces, because a + flange trades a 1/L² depth term for a linear area term. +3. **The drive turns the shaft more than one revolution**, inverting the tip + stage to a reduction (see plan 004's correction note). **OQ5 is resolved by construction:** +the nacelle keeps its canonical station because the pivot moved inside it +(KTD7/KTD8), and only the wing-internal station moved. + +**RISK-1 stands and is now measurable.** Root t/c 12.14 → 17.72 %, tip +18.93 → 26.70 %, both verified against the built source. Every aero figure in +this repo that cites this wing is unverified until CFD or bench data exists. + +--- + ## Problem Frame ### What is actually broken diff --git a/docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md b/docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md index 119fe57..8dd2d7a 100644 --- a/docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md +++ b/docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md @@ -38,6 +38,76 @@ recover, not margin to celebrate. --- +## CORRECTION 2026-08-29 — KTD4's shaft station is kinematically impossible + +**U3 (the wing-side shaft bore) is BUILT, at station 53.6, not 43.0.** + +**SUPERSEDED IN PART 2026-08-29 (owner direction): the stage is a REDUCTION.** +The actuator drives the shaft through **more than one revolution** per 140° of +nacelle, so the ring is the LARGER member. The impossibility recorded below was +real *under the step-up reading* this plan assumed (a limited-rotation servo), +and it is what a reduction dissolves. Built: module 0.8, **14T pinion / 50T +ring**, i = 3.571, shaft 1.389 rev, **C = 25.6 → station 53.6**. The +step-up analysis below is retained as the record of why the original figure +failed — it is still correct about KTD4's C = 15 mm being impossible. + +**OQ1 (servo 180° vs 270°) is CLOSED — the question is void.** A multi-turn +output means the actuator is a continuous-rotation gearmotor or stepper closed +on the AK7455, not a limited-rotation servo. That also retires KTD5's "travel is +the binding constraint" finding, and makes the encoder load-bearing for CONTROL +rather than telemetry. **New open item: actuator re-select.** + +KTD4 places the drive shaft 15.0 mm from the spar axis. Close this plan's own +inputs on each other and that centre distance returns an impossible gear: + +- The tilt ring gear is **concentric with the spar** (KTD3 puts it on the + trunnion), so its root diameter must clear Ø20 plus a hub wall → PD ≥ ~30 mm. +- The stage is a step-**up** — the nacelle sweeps 145°, more than a servo's + range — so the pinion is the SMALLER member: + `PD_ring × 145 = PD_pinion × servo_deg`. +- `C = (PD_ring + PD_pinion) / 2`. + +At `C = 15` with a 270° servo this yields **`PD_ring = 10.5 mm` — a ring gear +smaller than the spar it must encircle.** No tooth count fixes it. + +| PD_ring | Servo | PD_pinion | C | Wing shaft station | +|---|---|---|---|---| +| 30.0 | 270° | 16.1 | 23.1 | 51.1 | +| **33.8** | **270°** | **18.2** | **26.0** | **54.0 — BUILT** | +| 30.0 | 180° | 24.2 | 27.1 | 55.1 | +| 33.8 | 180° | 27.2 | 30.5 | 58.5 | + +C = 26.0 is built because it is also what leaves the AK7455 pocket its 1.80 mm +of chordwise clearance between the spar bore and the shaft bore — the three +dimensions are coupled and none of them is free. + +**This escalates OQ1 from advisory to blocking.** The 180° column is not +buildable at the current wing: station 58.5 collides with the root tenon at +58.5 mm. Confirm the servo's real angular range before cutting gears; a 180° +servo forces either a smaller ring gear or a tenon relocation. + +**KTD1 (Option A, shaft + spur) is unaffected and stands.** The right-angle +argument that selected it over a belt is about axis orientation, not centre +distance, and remains correct. What changes is only where the shaft sits. + +**U2's trunnion, U4's ring gear, U5's nozzle re-datum, and U6 remain OPEN** — +they are nacelle work and were deliberately not touched this pass. Their +interface numbers are consolidated in +[`docs/WING_ATTACH_INTERFACE.md`](../WING_ATTACH_INTERFACE.md) §4. + +**RISK-1 (tip congestion) was realised, exactly as predicted.** The wingtip now +hosts the spar bore, the register pad, the AK7455 pocket, the drive-shaft +bushing boss, and the nav-conduit exit. Settling it took three coupled moves — +the shaft's centre distance, `HALL_SENS_R` 11 → 16.8, and the AK7455 conduit +54.0 → 44.5 — and the surviving margins are 2.10 mm on each side of the encoder +pocket. There is no slack left at this station. + +**OQ5 (inherited) is resolved:** the trunnion IS concentric with the spar; the +nacelle keeps its canonical station because the pivot moved inside it, so no +tip-rib offset bracket is needed. + +--- + ## Problem Frame ### The kinematic constraint that decides the trade @@ -311,7 +381,7 @@ overlap; bearing load check recorded against the datasheet rating. **Approach:** Ø4.4 bore at station **43** (KTD4), spanwise, parallel to the spar. Support bushings at the root rib and tip rib. Servo coupling at the bulkhead. -Check against the spar bore, the AK7455 conduit at station 54, and the belt-free +Check against the spar bore, the AK7455 conduit at station 44.5, and the belt-free section — the wing now carries three spanwise bores and they must not intersect. **Acceptance criteria:** diff --git a/docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md b/docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md index 7e598d5..8f8786c 100644 --- a/docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md +++ b/docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md @@ -193,9 +193,20 @@ architecture that creates the overhang. at **Z 60** with `INTAKE_BLEND_L = 90` — well past the monotonic region, which is *why* the curves cross. **The blend must terminate by Z ≈ 30, tangent to the dome**, not run to Z 90. See R5. -- **OQ5** — Should the plan-002 station move (which lowers the spar 3.05 mm and - costs that much clearance) be re-sequenced ahead of or behind this work? They - interact on the same margin. +- **OQ5 — RESOLVED 2026-08-29 by implementation.** The spar station move is + **done** (wing Rev T1, station 28.0, `SPAR_Z` 66.85), so it is no longer a + sequencing question — this plan's clearance budget must be computed against + the built spar height, not against a pending one. The +9.8 mm figure in R3 + already assumes station 28.0 and the ESC1 relocation, so it stands; but + **re-verify it against the built geometry** rather than carrying it forward, + because R3 is an owner-accepted margin and this plan is the last thing that + can silently spend it. +- **OQ6 (new)** — The nacelle inherits three joint requirements from the wing + side that touch this plan's geometry: the 4 × 10 AWG disconnect relocates into + the nacelle annulus (WA-R10), the ring magnet grows to ID 26 / OD 41.2 and must + be axially separated from the ring gear (WA-R9), and the spar stub protrusion + (32 mm) needs confirming against the final trunnion bearing stations (WA-R12). + See `docs/WING_ATTACH_INTERFACE.md` §4. --- diff --git a/docs/plans/2026-08-30-001-weight-reduction-targets-plan.md b/docs/plans/2026-08-30-001-weight-reduction-targets-plan.md new file mode 100644 index 0000000..5726c77 --- /dev/null +++ b/docs/plans/2026-08-30-001-weight-reduction-targets-plan.md @@ -0,0 +1,502 @@ +--- +title: "chore: Ranked weight-reduction targets — cargo section, wing root, and carried mass" +date: 2026-08-30 +plan_type: mechanical+process +artifact_contract: ce-unified-plan/v1 +artifact_readiness: implementation-ready +execution: geometry+hardware +product_contract_source: ce-plan-bootstrap +parent: docs/MASS_AUDIT_CARGO_WING_ROOT.md +--- + +# chore: Ranked weight-reduction targets + +**Target repo:** Serenity-UAV (this repo) + +*"She'll fly true. Just gotta stop putting rocks in her pockets."* + +--- + +## Summary + +Hover T/W was ~1.19 against a 1.2 minimum, and Rev T1c added +102.8 g. This plan +lists what can come back out, ranked by mass, with the check that has to clear +before each one is cut. + +**The ranking has an uncomfortable shape and it is worth saying up front:** the +single largest lever is not structural at all. Swapping `BATT-6S-4000` for +`BATT-6S-2800` is **225 g** — more than every printed-part optimisation in this +plan put together. Everything below it is 20–35 g at a time. + +**And nothing here should be cut until W2 lands.** The BOM understates printed +mass by **+521.6 g (13.3 % of AUW)**, so the aircraft is currently being weighed +against a number that is not the aircraft. Shaving 30 g off a part whose BOM row +is 118 g wrong is not weight reduction, it is decoration. + +| # | Target | Measured now | Recoverable | Confidence | Gate | +|---|---|---:|---:|---|---| +| **W1** | Battery down-select 4000 → 2800 mAh | 750 g | **−225 g** | high | endurance/mission, not structure | +| **W2** | **BOM ⇄ mesh reconciliation** | — | **0 g** | — | **gates every other item** | +| **W3** | Battery-tray floor pocket | 140.2 g | **−23.1 g** | high | rail-slot depth, not bending | +| **W4** | Actuator standoffs → hollow | 109.5 g | **−32.9 g** | high | mesh deflection (already cleared) | +| **W5** | Cargo cradle → windowed frame | 80.6 g | **−25 g (est.)** | medium | latch-tab flex, 250 g payload | +| **W6** | Nacelle pod STL identity | 132 g BOM vs 278 or 658 g measured | unknown | — | must be resolved before it is a target | +| **W7** | Landing-gear bay bolt bosses | 126.0 g raw | unquantified | low | bolt bearing; needs its own study | +| | **Printed-part subtotal (W3–W5)** | | **≈ −81 g** | | | +| | **With W1** | | **≈ −306 g** | | | + +**Explicitly NOT a target: the wing roots.** §W8 records why, so it is not +re-litigated. + +--- + +## Problem Frame + +### Why this is worth doing at all + +`docs/structural_analysis.md` §1.1.5 puts hover T/W at ~1.19 without a battery +swap and ~1.25 with one, against a stated 1.2 minimum. Rev T1c's +102.8 g +(`airframe/fuselage-mid/WBS.md` WA-R18) pushes the un-swapped case further under. +So the airframe is not comfortably above its own hover criterion, and the margin +has to come from somewhere. + +### Why the BOM gates everything + +`docs/MASS_AUDIT_CARGO_WING_ROOT.md` §5 measured all 23 `PRINT-*` rows whose +description names an STL that exists. The rows understate by **+521.6 g**: + +| Row | BOM | Measured | Δ | +|---|---:|---:|---:| +| `PRINT-CARGO-SECT` | 165.0 | 372.2 | +207.2 | +| `PRINT-BATT-TRAY` | 22.0 | 140.2 | +118.2 | +| `PRINT-HEAD-SHELL` | 83.0 | 177.6 | +94.6 | +| `PRINT-CARGO-CRADLE` | 18.0 | 80.6 | +62.6 | +| `PRINT-MIDDLE-CANONICAL` | 135.0 | 190.9 | +55.9 | + +Three were corrected in the audit. The rest were measured and left for their +owning branches. Until they are reconciled, no weight statement in this +repository means anything — and a plan that optimises against a wrong baseline +optimises the wrong parts. + +### The efficiency signal worth reading + +Bounding-box fill fraction is a crude but honest tell for an over-built part: + +| Part | Mass | Envelope (mm) | Fill | +|---|---:|---|---:| +| `battery_tray.stl` | 140.2 g | 154 × 51.5 × 62 | **27.2 %** | +| `wing_port_s1223_revo.stl` | 95.5 g | 95.7 × 129 × 31.4 | 23.5 % | +| `cargo_cradle_autolatch.stl` | 80.6 g | 110 × 80 × 72 | 12.1 % | +| `cargo_sect_shell24_2mm_repaired.stl` | 372.2 g | 194 × 199 × 163 | 5.6 % | + +The wing is at 23.5 % because it is a thick, highly-cambered section skinned at +the 4-perimeter floor — that is the *shape*, not slack. The tray is at 27.2 % +because it has an 8.5 mm floor. Those two look similar in the column and are +completely different findings, which is why the column is a *signal* and the +per-part analysis below is the *answer*. + +--- + +## Requirements + +- **R1** — No target is cut before its BOM row is reconciled to its mesh (W2). +- **R2** — Every cut carries a named failure mode, a computed margin against a + cited allowable, and the deflection or fit consequence where that governs + instead of stress. +- **R3** — No cut takes a wall below **2.5 mm** (4 perimeters at 0.6 mm), the + repo's minimum-wall convention. +- **R4** — No cut erodes a clearance budget that is already stated and spent + (`GAP_BUDGET` 3.0 mm to a moving part; 1.16 mm minimum skin over a bore). +- **R5** — Mass, CG and hover T/W are re-derived once, after all accepted cuts, + not incrementally per part. +- **R6** — Each modified part re-passes `tools/validate_stls.py` as a watertight + single body. + +--- + +## Key Technical Decisions + +**KTD1 — W2 is a prerequisite, not a parallel task.** *(session-settled: the +audit found the baseline wrong by 13.3 % of AUW.)* Governs R1. + +**KTD2 — Structural cuts are justified against a *named* mode, and the mode is +usually not stress.** On the tray floor it is a rail-slot depth constraint; on +the actuator standoff it is gear-mesh deflection; on the cradle it is flex-tab +compliance. Each was mis-diagnosable as a strength problem and none of them is +one. Governs R2. + +**KTD3 — The wing root is closed to further reduction.** Both of its dimensions +are already at a floor: `WALL_T` 2.5 mm is four perimeters, and +`THICKNESS_SCALE` 1.46 is the *solved* value leaving 1.19 mm of skin over the +Ø20.4 bore against a 1.16 mm minimum. Governs R3, R4. + +**KTD4 — W1 is an operations decision, not an engineering one.** The 2800 mAh +pack is 225 g lighter and shorter-endurance. This plan sizes it and states the +trade; it does not make the call. + +--- + +## Implementation Units + +### W2. Reconcile the BOM to the meshes *(do this first)* + +**Goal:** A mass column that describes the aircraft. + +**Dependencies:** none — this gates everything else +**Files:** `current-specification/bom_revS.csv`, `tools/bom_mass_check.py` (new), +`docs/MASS_AUDIT_CARGO_WING_ROOT.md` + +**Approach:** For every `PRINT-*` row with `Qty > 0`, resolve the STL its +description names, measure it at `RHO_PRINT` (1.05 × 10⁻³ g/mm³), and write the +measured figure into `Unit_Mass_g`. Then add the check to CI so a regenerated +mesh cannot silently diverge from its row again. + +Two traps the audit already hit, both worth encoding in the tool: + +- **`RHO_PRINT` is a bulk figure.** Do not re-derive it as + `RHO_SOLID × infill`. Below ~6 mm thickness a part is nearly all perimeter and + infill barely moves it; that error halved the `PRINT-WING-ROOT-FLANGE` row. +- **Consumable stock is not installed mass.** `FOAM-PU-2LB` carried the 900 g + *kit*; installed foam is 22 g. `FIL-CF-PETG` carries 2,000 g of spool. Add an + `Installed` flag rather than trying to fix these row by row — summing the + current mass column returns 12.7 kg, which is the mass of nothing. + +**Acceptance criteria:** +- [ ] Every `PRINT-*` row with a resolvable STL matches it within ±5 %. +- [ ] Rows whose named STL cannot be resolved are listed explicitly (W6 is one). +- [ ] `Installed` flag present; the installed-only sum is stated. +- [ ] CI fails on a row that drifts from its mesh. + +**Verification:** `/usr/bin/python3 tools/bom_mass_check.py` exits 0. + +**Scope:** M (3–5 files) + +--- + +### W1. Battery down-select + +**Goal:** Decide 4000 mAh vs 2800 mAh on stated numbers rather than inertia. + +**Dependencies:** W2 (so T/W is computed against a real airframe mass) +**Files:** `current-specification/bom_revS.csv`, `docs/flight_envelope.md`, +`docs/POWER_DISTRIBUTION.md` + +**Approach:** `BATT-6S-4000` is **750 g — 19 % of the 3,911 g AUW and the single +heaviest item on the aircraft.** `BATT-6S-2800` is 525 g. Present endurance at +each capacity against the hover and cruise power draws already in +`POWER_DISTRIBUTION.md`, with T/W at each mass, and let the owner choose per +mission profile rather than fixing one pack. + +**This is a mission trade, not a structural one.** State it and stop; the +airframe does not care which pack is fitted, and the tray already accepts both +(cavity 148 × 56 × 41 mm). + +**Acceptance criteria:** +- [ ] Endurance and T/W tabulated for both packs at the reconciled AUW. +- [ ] Owner decision recorded, either way, with its reason. + +**Verification:** Not automatable — a recorded decision. + +**Scope:** S (1–2 files) + +--- + +### W3. Battery-tray floor pocket + +**Goal:** −23.1 g from a floor that is thick for a geometric reason, in the +region where that reason does not apply. + +**Dependencies:** W2 +**Files:** `airframe/openscad/fuselage/battery_tray.scad`, +`airframe/stls/fuselage/battery_tray.stl` + +**Approach:** `FLOOR = 8.5 mm`, and the SCAD is explicit that this is **not a +strength figure**: the rail slot (`RAIL_D = 6.5`) must not breach the cavity +floor, so 8.5 leaves 2.0 mm above it. That constraint only exists *at the rail +slots*. Everywhere else the floor is carrying a battery on a 56 mm span. + +Retain full thickness in a band around each rail slot; pocket the central field +from below to 3.0 mm. + +**Bending check, simply-supported strip, 20 % CF-PETG at 77 MPa flexural +(REF-MAT-002, ASTM D790), 750 g pack:** + +| Floor `t` | σ, limit 4 g×1.5 | FOS | σ, crash 9 g×1.5 | FOS | δ, limit | +|---|---:|---:|---:|---:|---:| +| 8.5 mm (as built) | 0.173 MPa | 444 | 0.390 MPa | 197 | 0.002 mm | +| 4.0 mm | 0.783 MPa | 98 | 1.762 MPa | 44 | 0.019 mm | +| **3.0 mm** | **1.392 MPa** | **55** | **3.132 MPa** | **25** | **0.045 mm** | +| 2.5 mm | 2.004 MPa | 38 | 4.510 MPa | 17 | 0.079 mm | + +3.0 mm clears the §3 FOS 4.0 target by **6× on the crash case**. 2.5 mm would +too, but 3.0 matches the tray's own `WALL` and keeps one thickness in the part. + +Pocket 138 × 29 × 5.5 mm = 22,011 mm³ → **−23.1 g**, tray 140.2 → 117.1 g. + +*Modes checked and non-governing:* rail-slot breach (retained band), bearing at +the M3 detent bores (in the rail band, untouched), strap-slot tear-out +(`STRAP_SLOT` is in the wall, not the floor). + +**Acceptance criteria:** +- [ ] Rail slots retain ≥ 2.0 mm of material above them. +- [ ] Central floor ≥ 3.0 mm; FOS ≥ 4.0 at 9 g × 1.5 recorded. +- [ ] STL watertight, single body; BOM row updated to the measured mass. + +**Verification:** `tools/validate_stls.py`; re-measure and compare to 117.1 g. + +**Scope:** S (1–2 files) + +--- + +### W4. Hollow the actuator standoffs + +**Goal:** −32.9 g. Already analysed; this unit is the execution. + +**Dependencies:** W2 +**Files:** `airframe/blender-scripts/merge_cargo_interior.py` + +**Approach:** The two nacelle-tilt actuator pads are **109.5 g — 29 % of the +cargo shell**, of which 62.8 g is the Rev T1c standoff, modelled as a solid +61.5 × 27 × 18 mm block. Replace with a 2.5 mm wall + 2.5 mm mounting face. + +**The governing mode is mesh opening, not strength.** Worst-case combined load +5.0 N (3.47 N gear separation + 3.53 N actuator inertia at 4 g × 1.5); +cantilever `δ = FL³/3EI`: + +| Form | `I` (mm⁴) | δ | vs a 0.05 mm centre-distance budget | +|---|---:|---:|---| +| Solid, as built | 100,875 | 1.44 × 10⁻⁵ mm | 3,500× inside | +| **Hollow, 2.5 mm** | ~58,000 | ~2.5 × 10⁻⁵ mm | 2,000× inside | +| 4 × Ø12 posts | 15,381 | 9.47 × 10⁻⁵ mm | 500× inside | + +The post variant saves 9 g more and is **not** recommended: it puts four +unsupported 18 mm columns inside the hull at a print orientation set by the +cargo shell, not by them. + +**Do not instead** shrink `NSVMT_STANDOFF` (18 = 11 gear plane + 6 face + 1 hub, +and 3 of the 11 is `GAP_BUDGET` to a rotating gear) or thin the 6 mm gear face +(already 7.5 × module, low end of the conventional 6–12× band). Together they +are 15 g for two stated budgets — R4 forbids it. + +**Acceptance criteria:** +- [ ] Standoff wall ≥ 2.5 mm; mounting face continuous under the flange. +- [ ] M3 pilot bores still land in ≥ 6 mm of material. +- [ ] `wing_root_deconflict.py` still CLEAR; shell watertight single body. +- [ ] Measured saving ≥ 30 g on the pair. + +**Verification:** `tools/wing_root_deconflict.py`, `tools/validate_stls.py`, +re-measure the shell. + +**Scope:** S (1–2 files) + +--- + +### W5. Cargo cradle — windowed frame + +**Goal:** ≈ −25 g from a part that weighs 80.6 g to carry a 250 g payload. + +**Dependencies:** W2, and **CARGO-01** (the cradle's bay placement is still +blocked; redesigning it before that settles risks doing the work twice) +**Files:** `airframe/stls/fuselage/cargo/generate_cargo_mounts.py` + +**Approach:** The cradle is *not* over-thick — it is already a 2.5 mm-walled +shell. Its problem is that it is a **fully-closed 110 × 80 × 60 mm box** for a +2.45 N line tension and a 250 g payload. Walls are ~53,600 mm³ of the 76,793; +the base plate is ~22,000. + +Replace the closed side walls with a windowed frame: retain full-height corner +posts, the top rim, the base perimeter, and the four flex-tab corners; window +the panels between. A 50 % window fraction on the side walls is ≈ −28 g. + +**Two things must not be windowed**, and they are why this is medium confidence +rather than high: + +1. **The four corner flex tabs and the rim they hinge from.** The auto-latch + works by tab compliance; changing the rim's local stiffness changes the snap + force, and that force is not characterised in this repo. +2. **The Dyneema tie-off boss and its load path to the rim.** The whole payload + hangs from it. + +Material is PETG here, not CF-PETG — use the PETG allowable, not REF-MAT-002. + +**Acceptance criteria:** +- [ ] Base plate retains a continuous load path from the tie-off boss to all + four corners. +- [ ] Flex-tab geometry and the rim within 10 mm of each tab **unchanged**. +- [ ] Payload bearing check at 250 g × 9 g × 1.5 against the PETG allowable. +- [ ] STL watertight single body; measured saving ≥ 20 g. + +**Verification:** `tools/validate_stls.py`; bench snap-force comparison against +an unmodified cradle before flight use. + +**Scope:** M (3–5 files) + +--- + +### W6. Resolve the nacelle pod STL identity *(investigation, not a cut)* + +**Goal:** Find out what the nacelle pods actually weigh. + +**Dependencies:** W2 +**Files:** `current-specification/bom_revS.csv`, `PROJECT_INDEX.md` + +**Approach:** `PRINT-NACELLE-PORT`/`-STBD` name +`s_eng_left_stator_shell24_revo.stl` at **132 g each**. That filename does not +exist — the `s_` prefix was dropped project-wide. Two candidates do: +`nacelle_port_revs.stl` (**277.9 g**) and +`eng_left_shell24_50mm_repaired.stl` (**658.5 g**). + +**If the canonical pod is either of those, the pods alone are 292 g or 1,053 g +heavier than the BOM says** — which would dwarf everything else in this plan. +This unit is to determine which STL is canonical and correct the row. It is +listed as a target only because the answer might make it the largest one. + +**Acceptance criteria:** +- [ ] Canonical nacelle pod STL identified and named in the BOM row. +- [ ] Measured mass recorded; if it exceeds the row by > 50 g, raise a + dedicated target in the wings-nacelles WBS. + +**Verification:** `tools/bom_mass_check.py` resolves the row. + +**Scope:** S (1–2 files) + +--- + +### W7. Landing-gear bay bolt bosses *(study)* + +**Goal:** Establish whether any of the 126.0 g raw bay feature volume is +recoverable. + +**Dependencies:** W2 +**Files:** `airframe/blender-scripts/merge_cargo_interior.py`, +`airframe/landing-gear/WBS.md` + +**Approach:** The Rev R6 bay features are 126.0 g of raw positive volume before +keep-out trimming. They are bolt bosses carrying real landing loads, and +`tools/landing_gear_wing_clearance.py` already reports the fore-stbd boss +standing 12.0 mm proud with a warning that a cut-back would cost upper-bolt +bearing. **Assume nothing is free here** until the bolt bearing check is redone. + +**Acceptance criteria:** +- [ ] Per-boss mass and bolt bearing margin tabulated. +- [ ] A recoverable figure stated, or "none" stated with its reason. + +**Scope:** M (3–5 files) + +--- + +### W8. Wing root — CLOSED, recorded so it is not reopened + +**No action. This unit exists to stop the question being asked again.** + +- Spanwise mass distribution is a clean taper (14.54 g in the root slice down to + 10.07 g at the tip) with **no local pile-up** — the signature of a skin-limited + structure, not a feature-limited one. +- `WALL_T = 2.5 mm` is **four perimeters at 0.6 mm**. 2.0 mm is 3.3 perimeters, + which is not a wall. +- `THICKNESS_SCALE = 1.46` is **solved, not chosen**: it is the exact value + leaving 1.19 mm of skin over the Ø20.4 spar bore against the repo's 1.16 mm + floor (`tools/wing_spar_station_fit.py`). Thinning the section breaks the bore + out of the skin. +- The **33.7 g** the retired tie rods returned (11.7 g of shell bosses + 22.0 g + of CF rod stock) was the whole of what this structure had to give, and it has + been taken. + +--- + +### Checkpoint: after W2, W3, W4 + +- [ ] BOM reconciled; installed-only AUW stated. +- [ ] ≈ 56 g recovered from the tray and standoffs. +- [ ] Gate suite green. +- [ ] Owner review before W5 (the cradle touches a mechanism whose snap force is + uncharacterised). + +--- + +## Risks & Dependencies + +- **RISK-1 (high) — optimising against a wrong baseline.** Mitigated by KTD1: + W2 first, and nothing else starts until it lands. +- **RISK-2 (medium) — the cradle's latch is a compliance mechanism.** Snap force + is uncharacterised, so any stiffness change near the tabs is a change to a + function nobody has measured. Mitigated by the W5 exclusion zones and a bench + comparison. +- **RISK-3 (medium) — W6 could invert the whole ranking.** If the canonical + nacelle pod is 278 g or 658 g rather than 132 g, the pods become the dominant + target and this plan's ordering is wrong. +- **RISK-4 (low) — cumulative FOS erosion.** Individually each cut keeps a large + margin; R5 requires one combined re-derivation rather than trusting the sum of + per-part checks. +- **DEP-1** — W5 is behind **CARGO-01** (cradle bay placement unresolved). +- **DEP-2** — All T/W statements inherit the unverified CF allowable + (`WING_ATTACH_INTERFACE.md` OI-2) and the unquantified aero terms. + +--- + +## Open Questions + +- **OQ1** — Which battery is the mission baseline (W1)? Owner call. +- **OQ2** — Is `RHO_PRINT = 1.05 × 10⁻³` right for *thick* parts? It is the + bulk figure and is conservative for thin walls, but a 45 mm-tall tray at 40 % + gyroid may genuinely be lighter than it predicts. A weighed printed sample + would settle it and would sharpen every number in this plan. +- **OQ3** — Does the cargo cradle fly on every sortie, or only on delivery + missions? If the latter, its 80.6 g is removable payload rather than airframe + mass, and W5 drops down the ranking. +- **OQ4** — W7: is there any bay-boss mass that does not carry bolt bearing? + +--- + +## Verification Contract + +```text +/usr/bin/python3 tools/bom_mass_check.py # new, W2 +/usr/bin/python3 tools/validate_stls.py +/usr/bin/python3 tools/wing_root_deconflict.py +/usr/bin/python3 tools/landing_gear_wing_clearance.py --proud +/usr/bin/python3 tools/cargo_bay_envelope.py +/usr/bin/python3 tools/precommit_index.py --check +``` + +Not automatable: the W1 owner decision, the W5 bench snap-force comparison, and +the single combined mass/CG/T-W re-derivation required by R5. + +--- + +## Definition of Done + +1. Every `PRINT-*` BOM row matches its mesh within ±5 %, enforced in CI, with an + `Installed` flag separating installed mass from consumable stock. +2. W3 and W4 cut, each with a recorded FOS or deflection margin against a cited + allowable, and each part re-passing `validate_stls.py`. +3. W5 either cut with the bench comparison recorded, or explicitly deferred + behind CARGO-01. +4. W6 resolved — the canonical nacelle pod named and measured. +5. The battery decision recorded either way, with its reason. +6. Mass, CG and hover T/W re-derived **once** on the final geometry, with the + T/W margin against the 1.2 minimum stated explicitly. + +--- + +## Sources & Research + +- `docs/MASS_AUDIT_CARGO_WING_ROOT.md` — parent; all measured masses, the + +521.6 g BOM finding, and the foam budget. +- `airframe/fuselage-mid/WBS.md` WA-R18, MA-1, MA-5, MA-6, MA-7. +- `airframe/openscad/fuselage/battery_tray.scad` — `FLOOR = 8.5` and its own + note that the figure is set by `RAIL_D`, not by load. +- `airframe/stls/fuselage/cargo/generate_cargo_mounts.py` `make_autolatch_cradle()` + — 2.5 mm walls, 250 g payload rating, flex-tab latch. +- `airframe/blender-scripts/merge_cargo_interior.py` — `NSVMT_*`, + `TILT_STAGE_*`, `RHO_PRINT`/`RHO_SOLID`. +- `docs/structural_analysis.md` §3 (load factors, FOS 4.0 target), §6.4/§7.3 + (CF-PETG allowables), §1.1.5 (T/W and the mass notice). +- `REFERENCES.md` REF-MAT-002 (20 % CF-PETG, 77 MPa flexural / 6.67 GPa). +- Volumes and bounding-box fill fractions measured 2026-08-30 with + `trimesh`/`manifold3d` against the published STLs under `/usr/bin/python3`. + +--- + +*Analysis and plan drafted by Claude (Claude Opus 5, Anthropic) under the +author's direction, 2026-08-30, per `AGENTS.md` §3 AI attribution.* diff --git a/docs/structural_analysis.md b/docs/structural_analysis.md index 25c81a5..52b429e 100644 --- a/docs/structural_analysis.md +++ b/docs/structural_analysis.md @@ -9,7 +9,7 @@ | Reference | Role in This Analysis | | --- | --- | | ASTM F2910-14 [ASTM F38] | Primary design specification for sUAS construction. Used as design intent reference; formal compliance matrix is a pre-certification task (see §9). | -| ASTM F3264-18 [ASTM F44] | Normal category aeroplane airworthiness. The 1.5× ultimate/limit load factor methodology (cf. 14 CFR Part 23.303) is adapted here as a conservative engineering baseline. This is NOT a compliance claim — F3264 applies to manned aircraft. | +| ASTM F3264-18 [ASTM F44] | Normal category aeroplane airworthiness. The 1.5× ultimate/limit load factor methodology (**14 CFR §23.2230** [REF-FAA-004] — *corrected 2026-08-29 from "Part 23.303", which belonged to the pre-2017 Part 23 and does not exist in the current rule*) is adapted here as a conservative engineering baseline. This is NOT a compliance claim — F3264 applies to manned aircraft, and Part 23 is an adopted baseline, not Serenity's certification basis. | | 14 CFR Part 107 | FAA Small UAS operating rules. Defines the legal operational envelope (altitude, VLOS, pilot certification, registration, lighting). Structural requirements are not in Part 107; this is cited for operational context only. | | ISO 21384-1:2022 | UAS general requirements standard. Cited as design intent; ISO 21384-1 compliance review is a pre-certification task. | | IEEE 1936.1-2021 | Drone applications framework (operational category classification). Referenced for operational envelope classification; does not provide structural load data. | @@ -66,10 +66,21 @@ weight-reduction pass that yields T/W ≈ 1.19 per TODO.md §1.1.5. ## 3. Design Load Cases All sizing uses a 2.0× design factor (loads × 2.0g) with an additional 1.5× safety factor -where specified. Load factor methodology is adapted from 14 CFR Part 23.303 (ultimate -load = 1.5 × limit load) as a conservative engineering baseline. Joint FOS target of 4.0 -is a design-team judgment value; no published FDM-specific knockdown factor standard -exists for CF-PETG at the time of writing. CF structural member (keel bar, boss pins, +where specified. Load factor methodology is adapted from **14 CFR §23.2230** +[REF-FAA-004] (ultimate load = 1.5 × limit load) as a conservative engineering baseline. +*(Corrected 2026-08-29: this previously cited "14 CFR Part 23.303", a pre-2017 section +number removed by the Amdt. 23-64 restructure — same class of stale citation as the +§23.1401 case already recorded in REFERENCES.md.)* + +Joint FOS target of 4.0 remains a design-team judgment value, and no published +FDM-specific knockdown-factor standard exists for CF-PETG at the time of writing — +but it now has a citable regulatory basis rather than resting on judgment alone. +**14 CFR §23.2265** [REF-FAA-004] requires a *special* factor of safety beyond the +basic 1.5 for any part "subject to appreciable variability because of uncertainties in +manufacturing processes or inspection methods", and FDM-printed polymer structure — +layer adhesion, raster orientation, moisture uptake, machine-to-machine variation — sits +squarely in that clause. The *requirement* for an extra factor is cited; its numeric +value (4.0) stays this project's own choice. CF structural member (keel bar, boss pins, ring frames, skid rods) material allowable σ_u = 1 500 N/mm² is an estimate for commercial unidirectional pultruded CF stock; supplier test certificates are required before fabrication (see §2 References). diff --git a/tools/cargo_bay_envelope.py b/tools/cargo_bay_envelope.py index 6e4f7c0..ef75ced 100644 --- a/tools/cargo_bay_envelope.py +++ b/tools/cargo_bay_envelope.py @@ -318,10 +318,15 @@ def main(): if "--stations" in sys.argv: print_stations(mesh) - spar_od = wing_spar_bore_od() - 0.30 # bore is OD + 0.15 mm/side clearance + # REV T1 (2026-08-29): the bond gap is 0.2 mm/side, not the rotating fit's + # 0.15, and the spar does not rotate -- it is a FIXED bonded CF tube and the + # wing's primary bending member (docs/WING_ATTACH_INTERFACE.md SS1). Both + # the arithmetic and the label were carried over from the retired 8 mm + # rotating steel shaft and reported a 20.10 mm tube that does not exist. + spar_od = wing_spar_bore_od() - 0.40 # bore is OD + 0.20 mm/side bond gap print("\nSpar-diameter reconciliation (wing SCAD vs cargo shell)") print(f" wing wings_s1223_revo.scad SPAR_BORE_OD = {wing_spar_bore_od():.2f}" - f" -> rotating tilt-spar OD {spar_od:.2f}") + f" -> fixed bonded CF spar OD {spar_od:.2f}") print(f" shell merge_cargo_interior.WING_SPAR_BORE_D = {SHELL_BORE_D:.2f}") if abs(SHELL_BORE_D - wing_spar_bore_od()) > 0.05: print(f" MISMATCH {SHELL_BORE_D - wing_spar_bore_od():+.2f} mm — the wing " diff --git a/tools/index_tags.json b/tools/index_tags.json index 3cf438f..a23fed0 100644 --- a/tools/index_tags.json +++ b/tools/index_tags.json @@ -1,5 +1,5 @@ { - "generated": "2026-08-29", + "generated": "2026-08-30", "files": { ".clang-format": { "description": "clang-format configuration for the Serenity UAV firmware sources.", @@ -207,6 +207,7 @@ "description": "Serenity UAV \u2014 TODO (Open Work Only)", "tags": [ "documentation", + "emi-hardening", "project-tracking", "security" ], @@ -2088,6 +2089,13 @@ ], "archived": false }, + "airframe/stls/fuselage/generate_wing_root_flange.py": { + "description": "Generate the two bonded wing-root flanges (WA-R1b, Rev T1c).", + "tags": [ + "structural" + ], + "archived": false + }, "airframe/stls/fuselage/head_cargo_splice_collar.stl": { "description": "STL mesh (binary)", "tags": [ @@ -2313,6 +2321,22 @@ ], "archived": false }, + "airframe/stls/fuselage/wing_root_flange_port.stl": { + "description": "STL mesh (binary)", + "tags": [ + "cad-mesh", + "structural" + ], + "archived": false + }, + "airframe/stls/fuselage/wing_root_flange_stbd.stl": { + "description": "STL mesh (binary)", + "tags": [ + "cad-mesh", + "structural" + ], + "archived": false + }, "airframe/stls/nacelles/edf_aft_spider_sleeve.stl": { "description": "STL mesh (binary)", "tags": [ @@ -9432,7 +9456,7 @@ "archived": false }, "current-specification/bom_revS.csv": { - "description": "Bill of materials, CSV (171 items)", + "description": "Bill of materials, CSV (176 items)", "tags": [ "bom", "specification" @@ -9805,6 +9829,13 @@ ], "archived": false }, + "docs/MASS_AUDIT_CARGO_WING_ROOT.md": { + "description": "Mass Audit \u2014 Cargo Section and Wing Roots (Rev T1c)", + "tags": [ + "documentation" + ], + "archived": false + }, "docs/NOZZLE_DRIVE_TRADE.md": { "description": "Nozzle-Drive Mechanism Trade Study (Rev T, 2026-07-19)", "tags": [ @@ -9892,6 +9923,15 @@ ], "archived": false }, + "docs/TILT_DRIVE_CONTROL_SPEC.md": { + "description": "Nacelle Tilt Drive \u2014 Control References and Sensors (Rev T1)", + "tags": [ + "documentation", + "emi-hardening", + "security" + ], + "archived": false + }, "docs/TILT_ENCODER_WIRING_EMI_SPEC.md": { "description": "Nacelle Tilt-Angle Feedback (AK7455) \u2014 EMI Wiring Specification", "tags": [ @@ -9959,6 +9999,13 @@ ], "archived": false }, + "docs/WING_ATTACH_INTERFACE.md": { + "description": "Wing Attachment Interface Specification \u2014 Rev T1", + "tags": [ + "documentation" + ], + "archived": false + }, "docs/attribution_and_licensing.md": { "description": "Serenity UAV \u2014 Attribution and Licensing Policy", "tags": [ @@ -10158,6 +10205,13 @@ ], "archived": false }, + "docs/plans/2026-08-30-001-weight-reduction-targets-plan.md": { + "description": "chore: Ranked weight-reduction targets", + "tags": [ + "documentation" + ], + "archived": false + }, "docs/references/108090023_STS3215-C001_Datasheet.pdf": { "description": "PDF document", "tags": [ @@ -12804,6 +12858,13 @@ ], "archived": false }, + "tools/spar_bundle_fit.py": { + "description": "Size a hollow spar bore against the conductor bundle it has to swallow.", + "tags": [ + "build-tooling" + ], + "archived": false + }, "tools/tests/test_nacelle_esc_service.py": { "description": "Python script", "tags": [ @@ -12866,10 +12927,9 @@ "archived": false }, "tools/wing_root_deconflict.py": { - "description": "Deconflict everything crowded into the wing-root zone of the cargo bulkhead.", + "description": "Python script", "tags": [ - "build-tooling", - "security" + "build-tooling" ], "archived": false }, @@ -12881,10 +12941,9 @@ "archived": false }, "tools/wing_spar_carrythrough.py": { - "description": "Does the rotating wing spar have to span the fuselage? First-principles check.", + "description": "Python script", "tags": [ - "build-tooling", - "security" + "build-tooling" ], "archived": false }, @@ -13008,6 +13067,7 @@ "docs/LANDING_GEAR_ANALYSIS.md", "docs/LICENSE", "docs/MANIFEST.json", + "docs/MASS_AUDIT_CARGO_WING_ROOT.md", "docs/NOZZLE_DRIVE_TRADE.md", "docs/OBSERVER_LASER_ANALYSIS.md", "docs/OBSERVER_MANUFACTURING_READINESS.md", @@ -13018,6 +13078,7 @@ "docs/PYLON_INTEGRATION_2026-07-18.md", "docs/README.md", "docs/REVN_BUILD_GUIDE_24IN.md", + "docs/TILT_DRIVE_CONTROL_SPEC.md", "docs/TILT_ENCODER_WIRING_EMI_SPEC.md", "docs/TILT_SPAR_ANALYSIS.md", "docs/TODO.md", @@ -13026,6 +13087,7 @@ "docs/VERIFY_PLACEMENT_WORKFLOW.md", "docs/WBS.md", "docs/WBS_FEDERATION.md", + "docs/WING_ATTACH_INTERFACE.md", "docs/attribution_and_licensing.md", "docs/bom_revR.json", "docs/electrical_fault_margins.md", @@ -13051,6 +13113,7 @@ "docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md", "docs/plans/2026-08-29-004-feat-nacelle-trunnion-pivot-tilt-drive-plan.md", "docs/plans/2026-08-29-005-nacelle-mould-line-conformance-plan.md", + "docs/plans/2026-08-30-001-weight-reduction-targets-plan.md", "docs/references/108090023_STS3215-C001_Datasheet.pdf", "docs/references/Bus_servo_control_circuit.pdf", "docs/references/CERN_OHL_rationale.pdf", @@ -13198,6 +13261,7 @@ "emi-hardening": [ "AGENTS.md", "README.md", + "TODO.md", "WBS.md", "airframe/FreeCAD-scripts/serenity_placeholders_assembly.py", "airframe/README.md", @@ -13260,6 +13324,7 @@ "docs/PHASED_BUILD_GUIDE.md", "docs/README.md", "docs/REVN_BUILD_GUIDE_24IN.md", + "docs/TILT_DRIVE_CONTROL_SPEC.md", "docs/TILT_ENCODER_WIRING_EMI_SPEC.md", "docs/TILT_SPAR_ANALYSIS.md", "docs/Tilt-Rotor 10AWG Wire Clearance Analysis.md", @@ -13407,6 +13472,7 @@ "docs/POWER_DISTRIBUTION.md", "docs/PROTO_PRINT_DAVINCI_JR.md", "docs/README.md", + "docs/TILT_DRIVE_CONTROL_SPEC.md", "docs/TODO_1_1_0_COMPLETION_SUMMARY.md", "docs/VERIFY_PLACEMENT_WORKFLOW.md", "docs/WBS.md", @@ -13438,9 +13504,7 @@ "tools/landing_gear_bay_pad_fit.py", "tools/open_mating_faces.py", "tools/precommit_index.py", - "tools/wing_internal_clearance.py", - "tools/wing_root_deconflict.py", - "tools/wing_spar_carrythrough.py" + "tools/wing_internal_clearance.py" ], "licensing": [ "LICENSE", @@ -13863,6 +13927,7 @@ "airframe/stls/fuselage/cargo_middle_splice_collar.stl", "airframe/stls/fuselage/dorsal_antenna_fin.stl", "airframe/stls/fuselage/generate_conforming_collars.py", + "airframe/stls/fuselage/generate_wing_root_flange.py", "airframe/stls/fuselage/head_cargo_splice_collar.stl", "airframe/stls/fuselage/head_shell24.stl", "airframe/stls/fuselage/head_shell24_2mm_repaired.stl", @@ -13889,6 +13954,8 @@ "airframe/stls/fuselage/middle_shell24_2mm_repaired.stl", "airframe/stls/fuselage/rear_shell24_2mm_repaired.stl", "airframe/stls/fuselage/river_access_cover.stl", + "airframe/stls/fuselage/wing_root_flange_port.stl", + "airframe/stls/fuselage/wing_root_flange_stbd.stl", "airframe/stls/nacelles/edf_aft_spider_sleeve.stl", "airframe/stls/nacelles/edf_stator_sleeve.stl", "airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl", @@ -14126,6 +14193,8 @@ "airframe/stls/fuselage/middle_shell24_2mm_repaired.stl", "airframe/stls/fuselage/rear_shell24_2mm_repaired.stl", "airframe/stls/fuselage/river_access_cover.stl", + "airframe/stls/fuselage/wing_root_flange_port.stl", + "airframe/stls/fuselage/wing_root_flange_stbd.stl", "airframe/stls/nacelles/edf_aft_spider_sleeve.stl", "airframe/stls/nacelles/edf_stator_sleeve.stl", "airframe/stls/nacelles/eng_left_shell24_50mm_repaired.stl", @@ -16356,6 +16425,7 @@ "tools/precommit_kicad_load.py", "tools/precommit_sanitize.py", "tools/purge_stale_fcstd_objects.py", + "tools/spar_bundle_fit.py", "tools/tests/test_nacelle_esc_service.py", "tools/validate_kicad.py", "tools/validate_stls.py", diff --git a/tools/spar_bundle_fit.py b/tools/spar_bundle_fit.py new file mode 100644 index 0000000..0a1e7f7 --- /dev/null +++ b/tools/spar_bundle_fit.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +"""Size a hollow spar bore against the conductor bundle it has to swallow. + +WHY THIS EXISTS +--------------- +The Rev T tilt-spar was sized for TORQUE (8 mm OD x 1.5 mm wall AISI 4130, +`docs/TILT_SPAR_ANALYSIS.md` SS3) and its 5 mm bore carried nothing heavier +than the WS2812C nav-light 3-core. The unified-spar architecture +(`docs/plans/2026-08-29-003-feat-unified-20mm-spar-trunnion-belt-drive-plan.md`) +moves the four 10 AWG ESC feeds ONTO the tilt axis, which makes the bore a +WIRE-VOLUME problem instead of a torque problem. + +Two independent sources got the number wrong before this tool existed: + + * the owner's draft (`docs/plans/2026-08-27-nacelle-wiring-plan.md`) proposed + `SPAR_BORE_D = 11.0` for 4 conductors, and + * the external source conversation quoted 13.3 mm in prose and then wrote + 11.0 into its own plan. + +Neither leaves ANY clearance, and 11.0 does not fit at all. The failure mode +is not subtle -- it is a 6-hour print and a procured CF tube that cannot be +threaded -- so the packing arithmetic gets its own fail-closed tool rather than +living in a comment. + +THE ARITHMETIC +-------------- +For `n` equal circles of diameter `d` packed inside the smallest enclosing +circle, the enclosing diameter is `d * K(n)` where `K` is the exact, +published optimal-packing ratio. Only the small-n cases this airframe can +actually present are tabulated; anything outside the table is refused rather +than interpolated (an interpolated packing ratio is a fabricated constant). + + n = 1 K = 1 exact + n = 2 K = 2 exact + n = 3 K = 1 + 2/sqrt(3) exact (= 2.15470...) + n = 4 K = 1 + sqrt(2) exact (= 2.41421...) + n = 5 K = 1 + sqrt(2(1 + 1/sqrt(5))) exact (= 2.70130...) + n = 6 K = 3 exact + n = 7 K = 3 exact (hexagonal + centre) + +Reference for the K(n) values: Melissen, J.B.M. (1997), "Packing and Covering +with Circles", PhD thesis, Utrecht University -- the n <= 7 cases are proven +optimal there and are reproduced in the standard circle-packing literature. +Catalogued as REF-MATH-001 in REFERENCES.md. + +`K(n) * d` is a ZERO-CLEARANCE floor: the wires touch each other and the bore +wall simultaneously. A buildable bore adds a radial clearance so the bundle +can be pulled through and, on this aircraft, so it can TWIST -- the nacelle +sweeps -5..+140 deg and the bundle absorbs that as distributed torsion along +the captive length instead of a swept arc at a fixed port. + + bore_min = K(n) * d + 2 * radial_clearance + tube_OD = bore_min + 2 * wall + +CAVEAT CARRIED FORWARD (plan 003 DEP-2 / OQ4) +--------------------------------------------- +`current-specification/bom_revS.csv` records NO outside diameter for +`WIRE-10AWG` (generically sourced, "10AWG silicone wire red/black"). The +default 5.5 mm here is the upper end of the typical 10 AWG silicone range and +is an ASSUMPTION, not a measured or cited figure. The tool prints that +caveat on every run and `--wire-od` exists so the real figure can be +substituted the moment it is measured. Do not quote this tool's output as +verified until that happens. + +Author : Steve Griffing, PE(CSE), CISSP-ISSEP, CPP +AI note : Authored by Claude (model: Claude Opus 5, Anthropic) under the + author's direction, 2026-08-29, per AGENTS.md AI attribution. +License : CC BY-SA 4.0 + +Run: /usr/bin/python3 tools/spar_bundle_fit.py + /usr/bin/python3 tools/spar_bundle_fit.py --tube 20x16.3 --tube 16x14 +""" + +import argparse +import math +import sys + +# --------------------------------------------------------------------------- +# Exact optimal enclosing-circle ratios, n circles of unit diameter. +# See the module docstring for the citation. DO NOT interpolate this table. +# --------------------------------------------------------------------------- +PACKING_K = { + 1: 1.0, + 2: 2.0, + 3: 1.0 + 2.0 / math.sqrt(3.0), + 4: 1.0 + math.sqrt(2.0), + 5: 1.0 + math.sqrt(2.0 * (1.0 + 1.0 / math.sqrt(5.0))), + 6: 3.0, + 7: 3.0, +} + +# Design defaults for the Serenity unified spar. +DEFAULT_N = 4 # 2 ESCs x (B+ / B-) +DEFAULT_WIRE_OD = 5.5 # [mm] ASSUMED -- see the docstring caveat +DEFAULT_CLEARANCE = 1.5 # [mm] radial, plan 003 R1 +DEFAULT_TUBES = ["16x12", "16x13", "16x14", "20x16", "20x16.3", "20x18"] + + +def packing_k(n): + """Exact enclosing-circle ratio for n equal circles, or refuse.""" + if n not in PACKING_K: + sys.exit(f"no exact packing ratio tabulated for n = {n}; " + f"tabulated: {sorted(PACKING_K)}. Refusing to interpolate " + f"-- an interpolated packing constant is a fabricated one.") + return PACKING_K[n] + + +def parse_tube(spec): + """Parse an 'ODxID' tube spec (e.g. '20x16.3') into (od, id, wall).""" + try: + od_s, id_s = spec.lower().split("x", 1) + od, bore = float(od_s), float(id_s) + except ValueError: + sys.exit(f"could not parse tube spec {spec!r}; expected e.g. 20x16.3") + if bore >= od: + sys.exit(f"tube {spec}: bore {bore} is not smaller than OD {od}") + return od, bore, (od - bore) / 2.0 + + +def main(): + ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + ap.add_argument("--wires", type=int, default=DEFAULT_N, + help=f"conductor count (default {DEFAULT_N})") + ap.add_argument("--wire-od", type=float, default=DEFAULT_WIRE_OD, + help=f"single conductor OD, mm (default " + f"{DEFAULT_WIRE_OD} -- ASSUMED, see docstring)") + ap.add_argument("--clearance", type=float, default=DEFAULT_CLEARANCE, + help=f"required RADIAL clearance around the bundle, mm " + f"(default {DEFAULT_CLEARANCE}, plan 003 R1)") + ap.add_argument("--tube", action="append", default=None, + metavar="ODxID", + help="candidate tube to PASS/FAIL (repeatable); " + f"default set: {' '.join(DEFAULT_TUBES)}") + args = ap.parse_args() + + n, d, clr = args.wires, args.wire_od, args.clearance + k = packing_k(n) + circumscribed = k * d + bore_min = circumscribed + 2.0 * clr + + print("spar bundle fit -- circle packing inside a round bore") + print(f" conductors {n} x D{d:.2f} mm") + print(f" packing ratio K({n}) {k:.5f} (exact, tabulated -- " + f"not interpolated)") + print(f" circumscribed bundle D{circumscribed:.2f} mm " + f"(ZERO clearance: wires touch bore wall)") + print(f" required radial clr {clr:.2f} mm") + print(f" => minimum bore D{bore_min:.2f} mm") + print() + print(" ASSUMPTION: WIRE-10AWG has no recorded OD in " + "current-specification/bom_revS.csv.") + print(" The default 5.5 mm is the upper end of the typical 10 AWG " + "silicone range and is") + print(" NOT a measured or cited figure (plan 003 DEP-2 / OQ4). " + "Re-run with --wire-od") + print(" once the procured wire is measured before quoting any of " + "this as verified.") + + # Zero-clearance reference table across the plausible OD range, so the + # sensitivity to the unverified wire OD is visible rather than implied. + print(f"\nzero-clearance floor vs. wire OD (n = {n}):") + print(" wire OD bundle D bore @ " + f"{clr:.1f} mm radial tube OD @ 2.0 mm wall") + for wod in (4.5, 5.0, 5.5, 6.0): + b = k * wod + bm = b + 2.0 * clr + print(f" {wod:5.2f} {b:6.2f} {bm:6.2f}" + f" {bm + 4.0:6.2f}") + + print(f"\ncandidate tubes (bundle D{circumscribed:.2f}, " + f"target radial clearance {clr:.2f} mm):") + print(" tube wall bore radial clr verdict") + worst_fail = False + for spec in (args.tube or DEFAULT_TUBES): + od, bore, wall = parse_tube(spec) + radial = (bore - circumscribed) / 2.0 + if radial < 0.0: + verdict = "FAIL bundle does not fit at all" + worst_fail = True + elif radial < clr: + verdict = f"MARGINAL {clr - radial:.2f} mm under target" + else: + verdict = "PASS" + print(f" {spec:<10} {wall:4.2f} {bore:5.2f} " + f"{radial:+5.2f} {verdict}") + + print("\n MARGINAL is not automatically a rejection: a bundle that fits " + "but cannot freely") + print(" twist is a fatigue problem, not a fit problem, and this " + "aircraft's bundle must") + print(" absorb the -5..+140 deg sweep as distributed torsion " + "(plan 003 R1/R2).") + if worst_fail: + print("\n At least one candidate FAILS outright.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/wing_airfoil_integrity.py b/tools/wing_airfoil_integrity.py index 5852685..be1b4c1 100644 --- a/tools/wing_airfoil_integrity.py +++ b/tools/wing_airfoil_integrity.py @@ -107,6 +107,58 @@ def check_polygon(upper, lower): return poly +def scaled_pts(upper, lower, t_scale): + """Mirror of the SCAD s1223_scaled_pts(): thickness scaled about camber. + + y = midline(x) + (y_surface(x) - midline(x)) * t_scale, applied to BOTH + surface lists in their SCAD order (upper LE->TE, then lower TE->LE). + """ + def mid(x): + return (wsf.surf_y(upper, x) + wsf.surf_y(lower, x)) / 2.0 + out = [] + for pts in (upper, lower): + for x, y in pts: + m = mid(x) + out.append((x, m + (y - m) * t_scale)) + return out + + +def check_scaled_sections(upper, lower, src): + """Validate the sections ACTUALLY built, at their real thickness scales. + + Checks 1 and 2 above validate the tabulated table -- i.e. the section at + t_scale = 1.0. That is not what gets lofted. wing_solid() builds its + root from s1223_scaled_pts(THICKNESS_SCALE) and its tip from + s1223_scaled_pts(THICKNESS_SCALE_TIP), and s1223_section()'s own header + warns the decomposition "was intended for 0.85-1.0 and had left that + range long ago" at 1.25. + + The unified-spar re-loft (docs/plans/2026-08-29-003-...) takes the root to + ~1.46 and the tip past 2.0, and flags self-intersection at those scales as + RISK-2 -- a risk this gate could not see, because it never read either + scale. A gate that passes identically at t_scale 1.0 and 2.19 is not + covering the failure it is being cited for. + + Returns a list of (label, t_scale, polygon, min_thickness) tuples. + """ + results = [] + for label, name in (("root", "THICKNESS_SCALE"), + ("tip", "THICKNESS_SCALE_TIP")): + ts = wsf.scad_scalar(src, name) + pts = scaled_pts(upper, lower, ts) + poly = Polygon(pts) + # Sample over the SAME interior band check 1 uses -- excluding both + # the LE and TE closure regions, where the surfaces legitimately meet + # and a zero is expected rather than a defect. + band = [ + (wsf.surf_y(upper, x / 400.0) - wsf.surf_y(lower, x / 400.0)) * ts + for x in range(int(LE_EXCLUDE * 400) + 1, + int((1.0 - TE_EXCLUDE) * 400)) + ] + results.append((label, ts, poly, min(band), max(band))) + return results + + def wing_solid_uses_hull(src): """True if wing_solid() still lofts with hull() rather than a true loft. @@ -181,6 +233,25 @@ def main(): print(" built cross-section IS the tabulated outline, not a convex") print(" approximation of it. No area-ratio check applies.") + # ---- 4. the sections that actually get lofted, at their real scales ---- + print("\n4. built sections are valid at their ACTUAL thickness scales") + print(" (checks 1-2 validate the table, i.e. t_scale = 1.0; wing_solid()") + print(" lofts THESE two. s1223_section() was written for 0.85-1.0.)") + for label, ts, poly, thk, tc_max in check_scaled_sections(upper, lower, src): + note = "" if ts <= 1.0 + 1e-9 else " (outside the 0.85-1.0 design range)" + print(f" {label:<5} t_scale {ts:5.3f} max t/c {tc_max:6.2%} " + f"min t/c {thk:6.3%} area {poly.area:.6f}{note}") + if not poly.is_valid: + print(f" FAIL -- {explain_validity(poly)}") + failures.append(f"{label} section self-intersects at " + f"t_scale {ts:.3f}") + elif thk <= 0.0: + print(f" FAIL -- non-positive thickness {thk:+.5f}") + failures.append(f"{label} section pinches to zero at " + f"t_scale {ts:.3f}") + else: + print(" ok") + print() if failures: print(" RESULT: FAIL") diff --git a/tools/wing_internal_clearance.py b/tools/wing_internal_clearance.py index 5abe9fd..ad6f17f 100644 --- a/tools/wing_internal_clearance.py +++ b/tools/wing_internal_clearance.py @@ -150,6 +150,28 @@ def r(self): return self.d / 2.0 +def station_law(src, chord_root, chord_tip, mm_name, frac_name, label): + """Resolve a bore's chordwise law: constant mm, or a legacy chord fraction. + + Constant-mm (…_STATION) is the correct law for this wing -- with a straight + LE it keeps every bore parallel, so chord taper cannot erode the webs + between them. A constant chord FRACTION (…_XFR) tapers with the chord and + is what produced the Rev S1b/S1c convergence this whole tool exists to + catch. Both are accepted so the tool still reports honestly on an older + source; only the fraction form is flagged. + """ + mm = re.search(rf"^{mm_name}\s*=\s*(-?[\d.]+)\s*;", src, re.M) + if mm: + value = float(mm.group(1)) + return value, value, f"{mm_name} = {value} mm (constant station)" + frac = re.search(rf"^{frac_name}\s*=\s*(-?[\d.]+)\s*;", src, re.M) + if frac: + value = float(frac.group(1)) + return (chord_root * value, chord_tip * value, + f"{frac_name} = {value} c (CHORD FRACTION -- tapers)") + sys.exit(f"could not find {mm_name} or {frac_name} for {label}") + + def load_wing(): """Parse the wing SCAD into sections and the list of internal bores.""" with open(WING_SCAD, encoding="utf-8") as handle: @@ -169,50 +191,81 @@ def load_wing(): spar_station = scad_scalar(src, "SPAR_BORE_STATION") spar_d = scad_scalar(src, "SPAR_BORE_OD") - cable_d = scad_scalar(src, "CABLE_BORE_D") - cable_sep = scad_scalar(src, "CABLE_BORE_SEP") hall_d = scad_scalar(src, "HALL_CABLE_D") bores = [Bore("spar", spar_d, spar_station, spar_station, "through")] - # The EDF double-D and the AK7455 conduit each take EITHER a constant-mm - # station (…_STATION) or a legacy constant chord FRACTION (…_XFR). Accept - # both so this tool reports on the pre-fix source as well as the fixed one - # -- that is the whole point of a regression check. - def station_law(mm_name, frac_name, label): - mm = re.search(rf"^{mm_name}\s*=\s*(-?[\d.]+)\s*;", src, re.M) - if mm: - value = float(mm.group(1)) - return value, value, f"{mm_name} = {value} mm (constant station)" - frac = re.search(rf"^{frac_name}\s*=\s*(-?[\d.]+)\s*;", src, re.M) - if frac: - value = float(frac.group(1)) - return (chord_root * value, chord_tip * value, - f"{frac_name} = {value} c (CHORD FRACTION -- tapers)") - sys.exit(f"could not find {mm_name} or {frac_name} for {label}") - - cable_root, cable_tip, cable_law = station_law( - "CABLE_BORE_STATION", "CABLE_BORE_XFR", "EDF cableway") - for sign, side in ((-1.0, "fwd"), (+1.0, "aft")): - bores.append(Bore(f"EDF {side}", cable_d, - cable_root + sign * cable_sep / 2.0, - cable_tip + sign * cable_sep / 2.0, "exit")) + # REV T1 (2026-08-29): the 2 x D7 EDF "double-D" is RETIRED -- the ESC + # feeds moved inside the spar bore, on the tilt axis. Two bores replace it + # and one is new: + # nav_bore() D3.2 @ 8.0 nav-light 3-core, must EXIT the tip face + # tilt_shaft_bore() D4.4 @ 54.0 tilt drive shaft, exits THROUGH the pad + # The AK7455 conduit survives, moved 54.0 -> 44.0 because the drive shaft's + # gear-mesh centre distance claims station 54. + # + # The legacy CABLE_BORE_* names are still accepted when present, so this + # tool remains a regression check against the pre-Rev-T1 source -- which is + # the whole point of a gate that was written to catch a bore-convergence + # fault. On a Rev T1 source they are simply absent. + if re.search(r"^CABLE_BORE_D\s*=", src, re.M): + cable_d = scad_scalar(src, "CABLE_BORE_D") + cable_sep = scad_scalar(src, "CABLE_BORE_SEP") + cable_root, cable_tip, cable_law = station_law( + src, chord_root, chord_tip, + "CABLE_BORE_STATION", "CABLE_BORE_XFR", "EDF cableway") + for sign, side in ((-1.0, "fwd"), (+1.0, "aft")): + bores.append(Bore(f"EDF {side}", cable_d, + cable_root + sign * cable_sep / 2.0, + cable_tip + sign * cable_sep / 2.0, "exit")) + else: + cable_law = "RETIRED at Rev T1 (power moved into the spar bore)" + + if re.search(r"^NAV_BORE_D\s*=", src, re.M): + nav_d = scad_scalar(src, "NAV_BORE_D") + nav_st = scad_scalar(src, "NAV_BORE_STATION") + # "exit": the nav light rotates WITH the nacelle, so this 3-core must + # break out of the tip face -- the pad must not cover it. + bores.append(Bore("nav 3-core", nav_d, nav_st, nav_st, "exit")) + + if re.search(r"^SHAFT_BORE_D\s*=", src, re.M): + shaft_d = scad_scalar(src, "SHAFT_BORE_D") + shaft_st = scad_scalar(src, "SHAFT_BORE_STATION") + # "through": the shaft runs THROUGH the pad by design, which is the + # point -- the pad lobe is its outboard bushing boss. + bores.append(Bore("tilt shaft", shaft_d, shaft_st, shaft_st, "through")) hall_root, hall_tip, hall_law = station_law( + src, chord_root, chord_tip, "HALL_CABLE_STATION", "HALL_CABLE_XFR", "AK7455 conduit") bores.append(Bore("AK7455", hall_d, hall_root, hall_tip, "pocket")) - pad_fwd_r = scad_scalar(src, "TIP_PAD_FWD_R") if re.search( - r"^TIP_PAD_FWD_R\s*=", src, re.M) else scad_scalar(src, "TIP_PAD_OD") / 2.0 - pad_aft_r = scad_scalar(src, "TIP_PAD_AFT_R") if re.search( - r"^TIP_PAD_AFT_R\s*=", src, re.M) else scad_scalar(src, "TIP_PAD_OD") / 2.0 + # Tip-pad chordwise extent. Rev T1 hulls THREE lobes (spar / AK7455 / + # drive-shaft bushing); Rev S1c hulled two; earlier revisions were a plain + # disc. Read whichever set the source actually defines -- the pad's + # forward edge is the figure that matters, because a pad that reaches a + # conduit's tip-face exit is a blocked harness, not an overlap. hall_sens_r = scad_scalar(src, "HALL_SENS_R") + if re.search(r"^TIP_PAD_R\s*=", src, re.M): # Rev T1 + pad_fwd_edge = spar_station - scad_scalar(src, "TIP_PAD_R") + aft_lobes = [spar_station + hall_sens_r + + scad_scalar(src, "TIP_PAD_SENS_R")] + if re.search(r"^TIP_PAD_SHAFT_R\s*=", src, re.M): + aft_lobes.append(scad_scalar(src, "SHAFT_BORE_STATION") + + scad_scalar(src, "TIP_PAD_SHAFT_R")) + pad_aft_edge = max(aft_lobes) + else: # Rev S1c / earlier + pad_fwd_r = scad_scalar(src, "TIP_PAD_FWD_R") if re.search( + r"^TIP_PAD_FWD_R\s*=", src, re.M) else scad_scalar(src, "TIP_PAD_OD") / 2.0 + pad_aft_r = scad_scalar(src, "TIP_PAD_AFT_R") if re.search( + r"^TIP_PAD_AFT_R\s*=", src, re.M) else scad_scalar(src, "TIP_PAD_OD") / 2.0 + pad_fwd_edge = spar_station - pad_fwd_r + pad_aft_edge = spar_station + hall_sens_r + pad_aft_r return { "root": root, "tip": tip, "bores": bores, "spar_station": spar_station, - "pad_fwd_edge": spar_station - pad_fwd_r, - "pad_aft_edge": spar_station + hall_sens_r + pad_aft_r, + "pad_fwd_edge": pad_fwd_edge, + "pad_aft_edge": pad_aft_edge, "laws": [f"spar : SPAR_BORE_STATION = {spar_station} mm (constant station)", f"EDF double-D: {cable_law}", f"AK7455 : {hall_law}"], diff --git a/tools/wing_root_deconflict.py b/tools/wing_root_deconflict.py index cf7c490..bee432e 100644 --- a/tools/wing_root_deconflict.py +++ b/tools/wing_root_deconflict.py @@ -7,8 +7,15 @@ against the port and starboard bulkheads**, each rotating one wing spar and the nacelle keyed to it (`airframe/wings-nacelles/WBS.md` SS1.1.2 SPAR-01). Their mounts must clear the landing-gear bays, the port/starboard avionics bays, the -wing root mortise and the spar bearing seat -- **and the nacelle ESC and -nav-light cableways must not be blocked**. +wing root mortise and the spar socket -- **and the nacelle ESC and nav-light +cableways must not be blocked**. + +REV T1 UPDATE (2026-08-29). The premise above is superseded in one respect and +the tool follows it: the spar no longer rotates and the servos no longer turn +it. The spar is a FIXED bonded CF tube, the actuator is a MULTI-TURN unit +driving a separate O4 shaft through a 1:1 spur pair, and the nav 3-core has been +evicted from the spar bore by the four 10 AWG ESC feeds that now live there. +See docs/WING_ATTACH_INTERFACE.md SS4.3b. The nav light is the easy one: it routes through the hollow spar's ~5 mm ID and tilts with it (95 deg twist, `docs/TILT_SPAR_ANALYSIS.md` SS5). The 40 A EDF ESC @@ -23,7 +30,7 @@ Two sides of the check ---------------------- -OBSTRUCTIONS -- solid things: servo body, servo horn swing, spar bearing seat, +OBSTRUCTIONS -- solid things: actuator body, tilt gear pair, spar socket boss, wing root mortise, the WING ROOT TENON that enters it, landing-gear bay seats, avionics bays. ROUTES -- volumes that must stay open: the EDF ESC double-D conduit, the @@ -88,7 +95,13 @@ SERVO_DEPTH = mci.NSVMT_BODY_H # 40.5 body height -> hull X, inboard SERVO_EAR_SPAN = mci.NSVMT_EAR_SPAN # 54.5 flange overall, hull Y SERVO_MASS_G = 60.0 # datasheet SS2-2 -HORN_SWING_R = 22.0 + +# REV T1 (2026-08-29): the horn and pushrod are GONE. The actuator no longer +# swings a horn against a crank on a rotating spar -- it drives a O4 shaft +# through a 1:1 spur pair (mci.TILT_STAGE_*), so what has to be checked for +# clearance is the GEAR PAIR envelope, not a horn arc. HORN_SWING_R is retired +# with the linkage it described. +GEAR_ENVELOPE_R = mci.TILT_STAGE_OD / 2.0 # 16.0 mm, gear tip radius WING_CHORD_LINE_Z = 58.01 # hull Z of the wing chord line at the root GAP_BUDGET = 3.0 # mm, minimum acceptable edge-to-edge clearance @@ -104,6 +117,10 @@ # those slivers as BLOCKED. One constant for both keeps them from disagreeing # on what counts as noise. BOOL_NOISE_TOL = 1.0 # mm^3, see note above +# Routes carried by a FLEXIBLE conductor rather than a rigid member. Their +# protected volume stops at the wall bracket; see routes() for why. +WIRE_ROUTES = ("nav-light 3-core conduit", "Hall/encoder conduit") + X_CL = -169.9 # cargo centreline PAYLOAD_W = 101.6 # mission payload width, CARGO-01 @@ -165,24 +182,47 @@ def route_stations(): with open(wsf.WING_SCAD, encoding="utf-8") as fh: src = fh.read() chord = mci.WING_ROOT_CHORD - cable_d = wsf.scad_scalar(src, "CABLE_BORE_D") - cable_sep = wsf.scad_scalar(src, "CABLE_BORE_SEP") - cable_stn = wsf.scad_scalar(src, "CABLE_BORE_STATION") hall_d = wsf.scad_scalar(src, "HALL_CABLE_D") hall_stn = wsf.scad_scalar(src, "HALL_CABLE_STATION") spar_bore = wsf.scad_scalar(src, "SPAR_BORE_OD") spar_stn = wsf.scad_scalar(src, "SPAR_BORE_STATION") - # Each bore is camber-centred at ITS OWN chordwise station, so the two EDF - # bores do not share a midline: at 22.75 and 32.25 mm the S1223 camber line - # differs by ~0.8 mm. Evaluating one midline for the pair puts both bores - # off the shell's matching harness ports and reports phantom blockage. - out = [ - ("EDF ESC conduit (40 A feeds)", - [((cable_stn - cable_sep / 2) / chord, cable_d), - ((cable_stn + cable_sep / 2) / chord, cable_d)]), - ("Hall/encoder conduit", [(hall_stn / chord, hall_d)]), - ("spar bore / nav-light 3-core", [(spar_stn / chord, spar_bore)]), - ] + # Each bore is camber-centred at ITS OWN chordwise station, so bores at + # different stations do not share a midline -- evaluating one midline for + # several bores puts them off the shell's matching ports and reports + # phantom blockage. + # + # REV T1 (2026-08-29): the route set changed with the spar. + # * The EDF double-D is RETIRED -- the four 10 AWG feeds moved INSIDE the + # spar bore, on the tilt axis. The spar bore is therefore no longer + # "nav-light only": it is now the 40 A route as well, and is relabelled + # to say so. A stale "nav-light 3-core" label on the one bore that now + # carries the aircraft's main propulsion current would be the most + # misleading string in this tool. + # * The nav 3-core gets its own small bore FORWARD of the spar. + # * A tilt drive-shaft bore appears AFT of the AK7455 conduit. + # The legacy names are still read when present so this tool keeps working + # as a regression check against a pre-Rev-T1 source. + out = [] + if re.search(r"^CABLE_BORE_D\s*=", src, re.M): + cable_d = wsf.scad_scalar(src, "CABLE_BORE_D") + cable_sep = wsf.scad_scalar(src, "CABLE_BORE_SEP") + cable_stn = wsf.scad_scalar(src, "CABLE_BORE_STATION") + out.append(("EDF ESC conduit (40 A feeds)", + [((cable_stn - cable_sep / 2) / chord, cable_d), + ((cable_stn + cable_sep / 2) / chord, cable_d)])) + if re.search(r"^NAV_BORE_D\s*=", src, re.M): + out.append(("nav-light 3-core conduit", + [(wsf.scad_scalar(src, "NAV_BORE_STATION") / chord, + wsf.scad_scalar(src, "NAV_BORE_D"))])) + out.append(("Hall/encoder conduit", [(hall_stn / chord, hall_d)])) + if re.search(r"^SHAFT_BORE_D\s*=", src, re.M): + out.append(("tilt drive-shaft bore", + [(wsf.scad_scalar(src, "SHAFT_BORE_STATION") / chord, + wsf.scad_scalar(src, "SHAFT_BORE_D"))])) + spar_label = ("spar bore / 4x 10 AWG ESC feeds" + if re.search(r"^NAV_BORE_D\s*=", src, re.M) + else "spar bore / nav-light 3-core") + out.append((spar_label, [(spar_stn / chord, spar_bore)])) # U5/KTD1 two-rod couple: both rod clearance bores are camber-centred at # their own station exactly like the bores above (see # wing_root_tie_rod_fwd_bore()/_aft_bore() in the wing SCAD). Root-only @@ -205,29 +245,44 @@ def routes(side): mid = midline_mm() inb = mci.PORT_INB if side == "port" else mci.STBD_INB outb = mci.PORT_OUTB if side == "port" else mci.STBD_OUTB - lo, hi = min(inb, outb), max(inb, outb) - # U5/KTD1: the tie-rods are ROOT-ONLY embeds, shorter than the main - # spar's PORT_INB..PORT_OUTB span -- probing the wider span reads solid - # wall beyond each rod's own embed as a false BLOCKED. Use each rod's - # actual fuselage-side embed span instead (mci.ROD_*_*_INB/OUTB). - rod_span = { - "fwd tie-rod bore": (mci.ROD_FWD_PORT_INB, mci.ROD_FWD_PORT_OUTB) if side == "port" - else (mci.ROD_FWD_STBD_INB, mci.ROD_FWD_STBD_OUTB), - "aft tie-rod bore": (mci.ROD_AFT_PORT_INB, mci.ROD_AFT_PORT_OUTB) if side == "port" - else (mci.ROD_AFT_STBD_INB, mci.ROD_AFT_STBD_OUTB), - } + # The U5/KTD1 tie-rod special case (root-only embeds needing their own + # shorter probe span) went with the rods -- the wing gates them off at + # TENON_LOAD_PATH = "spar_carrythrough" and the fuselage no longer drills + # them. What replaces it is a THREE-WAY split by what the route carries: + # a rigid shaft, a stepped bore, or a flexible wire (see below). + sgn = 1.0 if side == "port" else -1.0 # +1 where outboard is +X + deep_in = inb - 12.0 * sgn # well inboard of the bracket + deep_out = outb + 12.0 * sgn # well outboard of the skin + out = [] for label, bores in route_stations(): for i, (xfr, d) in enumerate(bores): z = WING_CHORD_LINE_Z + mid(xfr) y = mci.WING_LE_ROOT_Y + xfr * mci.WING_ROOT_CHORD tag = label if len(bores) == 1 else f"{label} #{i + 1}" - if label in rod_span: - x0, x1 = rod_span[label] - span_lo, span_hi = min(x0, x1), max(x0, x1) + if label.startswith("spar bore"): + # REV T1: the spar corridor STEPS at the socket's inboard end. + # Outboard of it the O20.4 tube itself occupies the bore; + # inboard of it only the 10 AWG bundle continues, at the tube's + # O16.3 ID. Probing one diameter the whole way reads the + # intended 2.05 mm annulus between them as a blockage. + out.append((tag, xcyl(y, z, *sorted((inb, deep_out)), d / 2.0))) + out.append((f"{tag} (bundle exit)", + xcyl(y, z, *sorted((deep_in, inb)), + mci.SPAR_WIRE_BORE_D / 2.0))) + elif label in WIRE_ROUTES: + # A WIRE is not a bore. Its protected volume ends at the wall + # bracket's inboard face: past that it is in open bay and can + # turn, and it must -- the O32.0 shaft gear sits 9.11 mm from + # the AK7455 conduit axis and no gear train at a 30.40 mm centre + # distance clears it. The lead turns forward into the wire trunk + # at the wall (docs/WING_ATTACH_INTERFACE.md SS4A-bis / WA-R6). + # Probing 12 mm past the bracket would report that intended turn + # as a blockage every run. + out.append((tag, xcyl(y, z, *sorted((inb, deep_out)), d / 2.0))) else: - span_lo, span_hi = lo - 12.0, hi + 12.0 - out.append((tag, xcyl(y, z, span_lo, span_hi, d / 2.0))) + out.append((tag, xcyl(y, z, *sorted((deep_in, deep_out)), + d / 2.0))) return out @@ -240,16 +295,25 @@ def tenon_params(): That distinction is the whole point of the fit check below. U5/KTD1 (2026-08-24): `WING_ROOT_TAB_W/H/L` are now a - `TENON_LOAD_PATH`-conditional expression in the SCAD (locating-only under - the default "two_rod" path, the original structural size under - "enlarged_tenon"), not a plain literal `wsf.scad_scalar()` can parse. - Read whichever branch's `_LOCATING`/`_ENLARGED` constants are actually - active instead. + `TENON_LOAD_PATH`-conditional expression in the SCAD, not a plain literal + `wsf.scad_scalar()` can parse. Read whichever branch's + `_LOCATING`/`_ENLARGED` constants are actually active instead. + + FIXED 2026-08-29: this used to select `_LOCATING` only for the literal + string "two_rod", so the Rev T1 default path ("spar_carrythrough") fell + through to `_ENLARGED` and the tool measured a 30 x 20 x 12 mm structural + tenon the wing does not build -- reporting "TENON FOULS THE MORTISE" by + 8.60 mm/side against a mortise correctly sized for the 12 mm locating tab. + The SCAD's own rule is written as a NEGATIVE (`TAB_IS_LOCATING = + TENON_LOAD_PATH != "enlarged_tenon"`) precisely so a new path inherits the + safe locating size; mirroring it as a positive whitelist inverted that. + Mirror the negative form instead. """ with open(wsf.WING_SCAD, encoding="utf-8") as fh: src = fh.read() m = re.search(r'^TENON_LOAD_PATH\s*=\s*"([^"]+)"\s*;', src, re.M) - suffix = "_LOCATING" if (m and m.group(1) == "two_rod") else "_ENLARGED" + path = m.group(1) if m else "enlarged_tenon" + suffix = "_ENLARGED" if path == "enlarged_tenon" else "_LOCATING" return (wsf.scad_scalar(src, "WING_ROOT_TAB_W" + suffix), # chordwise -> hull Y wsf.scad_scalar(src, "WING_ROOT_TAB_H" + suffix), # thickness -> hull Z wsf.scad_scalar(src, "WING_ROOT_TAB_L" + suffix)) # insertion -> hull X @@ -332,13 +396,13 @@ def max_tenon_envelope(side="port"): z_c = WING_CHORD_LINE_Z face = wall_face_x(side) y_fwd = y_c - w0 / 2.0 - spar_aft = mci.WING_SPAR_Y + 8.3 / 2.0 + spar_aft = mci.WING_SPAR_Y + mci.WING_SPAR_BORE_D / 2.0 pay_edge = X_CL + PAYLOAD_W / 2.0 # payload port face, centred real = [(lbl, sol) for lbl, sol in obstructions(side) if lbl not in ("wing root tenon", "wing root mortise", - "spar bearing seat")] - real.append(("rotating spar tube", spar_tube(side))) + "spar socket boss")] + real.append(("fixed CF spar tube", spar_tube(side))) def clashes(y0, y1, depth): t = box(face - depth, face, y0, y1, z_c - h0 / 2, z_c + h0 / 2) @@ -408,9 +472,10 @@ def servo_body(side): ears are what the pad has to carry. Depth is the 40.5 mm HEIGHT -- the axis the output shaft runs along -- not the 20 mm width. """ - inboard = mci.PORT_INB if side == "port" else mci.STBD_INB sign = -1.0 if side == "port" else +1.0 - x0, x1 = sorted((inboard, inboard + sign * SERVO_DEPTH)) + face = (mci.PORT_INB if side == "port" else mci.STBD_INB) \ + + sign * mci.NSVMT_STANDOFF + x0, x1 = sorted((face, face + sign * SERVO_DEPTH)) return box(x0, x1, mci.NSVMT_Y - SERVO_EAR_SPAN / 2.0, mci.NSVMT_Y + SERVO_EAR_SPAN / 2.0, @@ -425,7 +490,9 @@ def servo_pad(side): reaches further toward the landing-gear bays. Checking only the servo body would miss that. """ - inb = mci.PORT_INB if side == "port" else mci.STBD_INB + sign = -1.0 if side == "port" else +1.0 + inb = (mci.PORT_INB if side == "port" else mci.STBD_INB) \ + + sign * mci.NSVMT_STANDOFF outb = mci.PORT_OUTB if side == "port" else mci.STBD_OUTB lo, hi = min(inb, outb), max(inb, outb) return box(lo, hi, @@ -433,11 +500,27 @@ def servo_pad(side): mci.NSVMT_Z - mci.NSVMT_PAD_H / 2, mci.NSVMT_Z + mci.NSVMT_PAD_H / 2) -def servo_horn(side): +def tilt_gear_pair(side): + """The 1:1 spur pair between the actuator and the drive shaft (Rev T1). + + Modelled as the two gear tip cylinders unioned, at the mesh plane -- which + sits `TILT_STAGE_PLANE_DX` INBOARD of the wall bracket precisely so the + O32.0 shaft gear does not graze the O30.1 spar socket boss (they are 25.70 mm + apart on centres against 31.05 mm of summed radii, so they would overlap by + 5.35 mm if they shared a plane). Checking the pair rather than one gear + matters: the actuator gear is the one that reaches down toward the + landing-gear bay tops, and the shaft gear is the one that reaches in toward + the spar boss. + """ inboard = mci.PORT_INB if side == "port" else mci.STBD_INB sign = -1.0 if side == "port" else +1.0 - x = inboard + sign * (SERVO_W + 4.0) - return xcyl(mci.NSVMT_Y, mci.NSVMT_Z, x - 4.0, x + 4.0, HORN_SWING_R, sections=64) + x0 = inboard + sign * mci.TILT_STAGE_PLANE_DX + x1 = x0 + sign * mci.TILT_STAGE_FACE_W + lo, hi = sorted((x0, x1)) + shaft_g = xcyl(mci.WING_SHAFT_Y, mci.WING_SHAFT_Z, lo, hi, + GEAR_ENVELOPE_R, sections=64) + act_g = xcyl(mci.NSVMT_Y, mci.NSVMT_Z, lo, hi, GEAR_ENVELOPE_R, sections=64) + return trimesh.util.concatenate([shaft_g, act_g]) def obstructions(side): @@ -447,9 +530,9 @@ def obstructions(side): lo, hi = min(inb, outb), max(inb, outb) out = [ ("servo body", servo_body(side)), - ("servo horn swing", servo_horn(side)), - ("spar bearing seat", xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, - lo, hi, mci.WING_SPAR_BOSS_OD / 2.0)), + ("tilt gear pair", tilt_gear_pair(side)), + ("spar socket boss", xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, + lo, hi, mci.WING_SPAR_BOSS_OD / 2.0)), ("wing root mortise", box( lo, hi, mci.WING_MORT_Y - mci.MORT_W / 2, mci.WING_MORT_Y + mci.MORT_W / 2, @@ -509,12 +592,25 @@ def penetration_check(shell_man, findings): # bounds below), but a stale label defeats the whole point of # printing the cut span for a human to sanity-check against the # wall brackets, so it is corrected to the real sweep bounds here. - ("spar bore, port", (-130.0, -55.0), - xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, -130.0, -55.0, + # REV T1: the spar corridor is STEPPED, so a single-diameter sweep is + # the wrong probe. Outboard of the socket's inboard end the bore is + # O20.4 (the spar itself); inboard of it the bore narrows to O16.3 (the + # spar's ID, carrying the 10 AWG bundle on into the bay). Sweeping + # O20.4 the whole way would read the 2.05 mm annulus of intended + # material between the two diameters as "BLIND -- wall not cut", which + # is the opposite of the truth. + ("spar socket, port", (mci.PORT_INB, -55.0), + xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, mci.PORT_INB, -55.0, mci.WING_SPAR_BORE_D / 2.0)), - ("spar bore, stbd", (-285.0, -210.0), - xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, -285.0, -210.0, + ("10 AWG bundle exit, port", (-130.0, mci.PORT_INB), + xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, -130.0, mci.PORT_INB, + mci.SPAR_WIRE_BORE_D / 2.0)), + ("spar socket, stbd", (-285.0, mci.STBD_INB), + xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, -285.0, mci.STBD_INB, mci.WING_SPAR_BORE_D / 2.0)), + ("10 AWG bundle exit, stbd", (mci.STBD_INB, -210.0), + xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, mci.STBD_INB, -210.0, + mci.SPAR_WIRE_BORE_D / 2.0)), ] # Boolean noise on a ~900 k-face shell leaves sub-mm^3 slivers in a corridor # that is genuinely open, so a hard zero is the wrong test. 1 mm^3 is far @@ -546,13 +642,20 @@ def pad_fit_check(): def spar_tube(side): - """The rotating spar itself -- solid, and it occupies its bore. + """The FIXED CF spar itself -- solid, and it occupies its bore. Anything else routed inside that bore collides with the spar, not with the - shell, so the shell test alone would miss it. + shell, so the shell test alone would miss it. Rev T1: the OD is read as + TILT_SPAR_OD (20.0), not derived from the bore -- the bond gap is 0.2 mm/side + now, not the rotating fit's 0.15, and hard-coding the old subtraction here + would model a 20.1 mm tube that does not exist. + + The spar STOPS at the socket's inboard end (PORT_INB / STBD_INB): the bay is + clear beyond it by owner ruling. The four 10 AWG conductors inside the bore + do not stop there, which is what the separate bundle-exit cut is for. """ with open(wsf.WING_SCAD, encoding="utf-8") as fh: - od = wsf.scad_scalar(fh.read(), "SPAR_BORE_OD") - 0.30 # bore = OD + 0.15/side + od = wsf.scad_scalar(fh.read(), "TILT_SPAR_OD") inb = mci.PORT_INB if side == "port" else mci.STBD_INB sign = 1.0 if side == "port" else -1.0 return xcyl(mci.WING_SPAR_Y, mci.WING_SPAR_Z, @@ -576,22 +679,24 @@ def check_routes(side, findings, shell_man): reports those as blockages when they are the opposite. The shell is ground truth for what material actually remains after the merge. - The two things not in the shell are added explicitly: the servo (not shell - geometry) and the rotating spar (occupies its own bore). + The two things not in the shell are added explicitly: the actuator and its + gear pair (not shell geometry) and the fixed spar (occupies its own bore). """ print(f"\n ROUTES vs real material -- {side}") print(f" {'route':>30s} {'obstruction':>26s} {'blocked mm^3':>13s} verdict") extra = [("published cargo shell", None), ("servo body", servo_body(side)), - ("servo horn swing", servo_horn(side)), - ("rotating spar tube", spar_tube(side)), + ("tilt gear pair", tilt_gear_pair(side)), + ("fixed CF spar tube", spar_tube(side)), ("wing root tenon", tenon(side))] for rlabel, rsolid in routes(side): rman = to_man(rsolid) for olabel, osolid in extra: oman = shell_man if osolid is None else to_man(osolid) - if olabel == "rotating spar tube" and rlabel.startswith("spar bore"): + if olabel == "fixed CF spar tube" and rlabel.startswith("spar bore"): continue # the spar is supposed to be in its own bore + if olabel == "tilt gear pair" and rlabel == "tilt drive-shaft bore": + continue # the gear is keyed to that shaft, by design inter = volume_of(rman ^ oman) if inter > BOOL_NOISE_TOL: verdict = "BLOCKED" @@ -606,10 +711,10 @@ def check_solids(side, findings): print(f"\n SOLIDS vs solids -- {side}") print(f" {'probe':>18s} {'neighbour':>26s} {'overlap mm^3':>13s} " f"{'gap mm':>8s} verdict") - probes = [("servo body", servo_body(side)), ("servo horn", servo_horn(side)), + probes = [("servo body", servo_body(side)), ("tilt gear pair", tilt_gear_pair(side)), ("servo PAD", servo_pad(side))] others = [(lbl, s) for lbl, s in obstructions(side) - if lbl not in ("servo body", "servo horn swing")] + if lbl not in ("servo body", "tilt gear pair")] for plabel, psolid in probes: for olabel, osolid in others: inter = volume_of(to_man(psolid) ^ to_man(osolid)) diff --git a/tools/wing_spar_carrythrough.py b/tools/wing_spar_carrythrough.py index 0515e48..9f8dfaa 100644 --- a/tools/wing_spar_carrythrough.py +++ b/tools/wing_spar_carrythrough.py @@ -89,12 +89,40 @@ LIMIT_FACTOR = 4.0 # 3 g gust + 1 g maneuver ULTIMATE_FACTOR = 1.5 -# --- spar section, 8 mm OD x 1.5 mm wall (5 mm ID) AISI 4130 --------------- -SPAR_OD, SPAR_ID = 8.0, 5.0 +# --- spar section ---------------------------------------------------------- +# REV T1 (2026-08-29, plan 003 KTD1/KTD4): the spar is a FIXED 20 x 16.3 mm +# roll-wrapped CARBON FIBRE tube, not the Rev R2 8 x 1.5 rotating 4130 shaft. +# Three things change together and they are not independent: +# +# * SECTION. Z goes 42.6 -> 438.9 mm^3, a 10.3x increase, because the tube +# is now sized by the WIRE BUNDLE it carries (tools/spar_bundle_fit.py), +# not by the torque it transmits. It transmits none: the nacelle pivots +# on its own trunnion ring, so the spar carries bending only. +# * MATERIAL. CF was rejected in TILT_SPAR_ANALYSIS.md SS3.5 on a FUNCTIONAL +# gate -- "delaminates at a keyway" -- not on strength. A fixed spar has +# no keyway and no bearing journal, so that gate does not apply and CF's +# mass advantage becomes available: 67.5 g/pair against 96.2 g for the +# steel it replaces, i.e. the bigger spar is also the LIGHTER one. +# * LOAD PATH. See report_spar_carrythrough_joint(). +# +# ALLOWABLE IS UNVERIFIED. 300 MPa is the same deliberately conservative +# cross-ply stand-in CF_ALLOW uses for the thwart plate, carried over because +# the repo still has no ASTM D3039/D695 certificate for any CF stock (plan 003 +# DEP-1; REFERENCES.md "requires verification"). Do not quote the FOS below +# as qualified. +SPAR_OD, SPAR_ID = 20.0, 16.3 SPAR_I = math.pi * (SPAR_OD ** 4 - SPAR_ID ** 4) / 64.0 # mm^4 SPAR_Z = SPAR_I / (SPAR_OD / 2.0) # mm^3 +SPAR_ALLOW = 300.0 # MPa -- REQUIRES VERIFICATION (DEP-1) +SPAR_RHO = 1.60e-3 # g/mm^3, roll-wrapped CF + +# Superseded Rev R2 section, retained so the comparison in the report is +# against a real previous figure rather than a remembered one. +LEGACY_OD, LEGACY_ID = 8.0, 5.0 +LEGACY_Z = (math.pi * (LEGACY_OD ** 4 - LEGACY_ID ** 4) / 64.0) / (LEGACY_OD / 2.0) +LEGACY_RHO = 7.85e-3 # g/mm^3, AISI 4130 # MPa, 4130 normalized (typical -- MMPDS; tracked in the root work-tracking -# file SS0.8). +# file SS0.8). Retained: the legacy comparison row still cites it. STEEL_YIELD = 460.0 # --- candidate thwart section --------------------------------------------- @@ -170,13 +198,16 @@ def report_loads(geom): print(f" span L (fuselage bearing -> wingtip) {geom['L']:6.1f} mm") print(f" overhang a (wingtip -> nacelle axis) {geom['a']:6.1f} mm") print(f" arm d (wall -> nacelle axis) {geom['d']:6.1f} mm") - print(f"\n section 8 x 1.5 4130: I {SPAR_I:.0f} mm^4 Z {SPAR_Z:.1f} mm^3") + print(f"\n section {SPAR_OD:.0f} x {SPAR_ID:.1f} CF (fixed, Rev T1): " + f"I {SPAR_I:.0f} mm^4 Z {SPAR_Z:.1f} mm^3") + print(f" superseded 8 x 1.5 4130 had Z {LEGACY_Z:.1f} mm^3 " + f"-- section modulus up {SPAR_Z / LEGACY_Z:.1f}x") print(f"\n{'case':>10s} {'R_tip N':>9s} {'R_fus N':>9s} " f"{'M_spar N.m':>11s} {'sigma MPa':>10s} {'FOS':>6s}") for tag, f in (("1 g", per_side_1g), ("limit", limit), ("ultimate", ultimate)): r_tip, r_fus, m_spar, sigma = spar_case(f, geom) print(f"{tag:>10s} {r_tip:9.1f} {r_fus:9.1f} {m_spar:11.2f} " - f"{sigma:10.1f} {STEEL_YIELD / sigma:6.1f}") + f"{sigma:10.1f} {SPAR_ALLOW / sigma:6.1f}") return limit, ultimate @@ -236,6 +267,10 @@ def scad_str(name): r_tip_ult, _rf, _m, _s = spar_case(ultimate, geom) m_ult = r_tip_ult * arm / 1000.0 # N.m, ultimate root moment + if load_path == "spar_carrythrough": + report_spar_carrythrough_joint(scad, m_ult, ultimate) + return + if load_path == "two_rod": report_two_rod_couple(src, m_ult) return @@ -243,6 +278,159 @@ def scad_str(name): report_enlarged_tenon(src, scad, m_ult) +def report_spar_carrythrough_joint(scad, m_ult, v_ult): + """Rev T1 default: the FIXED spar itself is the wing-root load path. + + WHAT CHANGED, AND WHY IT IS NOT JUST A BIGGER ROD + + Under Rev R2 the spar was a rotating drive shaft. It rode bearings at both + ends, so by definition it could not react a root MOMENT -- a bearing + transmits shear, not moment -- which is precisely why the root moment had + to be routed somewhere else: first into an enlarged tenon (CARGO-03c, found + at FOS 0.49), then into a two-rod bonded couple (U5/KTD1, FOS 4.14). Both + of those existed to work AROUND a spar that structurally could not help. + + Rev T1 removes that constraint at the source. The spar is fixed and bonded + over its full span, so it is a moment-carrying member, and the load path + becomes nacelle -> trunnion -> spar -> fuselage socket. The tenon and the + tie rods are no longer in it. That is not a strength upgrade to the old + joint; it is a different joint. + + WHY THE TIE RODS RETIRE RATHER THAN BEING KEPT AS BACKUP + + 1. The forward rod is physically impossible. It sat at station 14.0 at + D8.2 (spanning 9.9..18.1); the Rev T1 spar bore spans 17.80..38.20. + They overlap. There is no clearance version of the old joint. + 2. The aft rod is possible but purposeless. Its remaining job would be + reacting wing TORSION about the spar axis, and that load is negligible: + see the torsion check below. + 3. Keeping an unnecessary bonded rod is not free -- it is a second bonded + interface competing for the same root volume as the spar socket, and a + stress riser in the skin at the station where the section is already + thinnest. + + THE SOCKET MODEL + + A rigid pin in an elastic socket develops a roughly triangular pressure + distribution on each side of the reversal point, with the two resultants + landing at L/3 from each end -- an effective couple arm of 2L/3. That is + the standard conservative idealisation and it is what is used here: + + F = 3M / (2L) + V/2 + area = D . L/3 (projected bearing, the repo's own + CARGO-03c convention) + sigma = F / area + + Note sigma goes as 1/L^2, so socket LENGTH is the only effective lever -- + doubling it quarters the stress. Diameter appears only linearly. + + ALLOWABLE. 5 MPa, the repo's standing bond-limited CF-PETG figure + (docs/structural_analysis.md SS7.3), the same one CARGO-03c and the two-rod + couple were sized against. It is a conservative working placeholder that + predates any cited source. REF-MAT-001 gives a real ASTM D695 BULK + COMPRESSIVE figure for 20 %-CF-PETG of ~47-60 MPa, and bearing of a bonded + tube against a socket wall is a compressive mode rather than the bond/peel + mode 5 MPa was written to bound -- so this socket is very likely far more + conservative than it looks. It is NOT re-based here: overturning a + standing repo allowable is an owner decision gated on the LG-11 coupon + (root TODO SS1.1.4), not a side effect of a geometry change. The sweep + below shows what that coupon would buy. + """ + d_sock = scad("TILT_SPAR_OD") + allow = 5.0 + m_nmm = m_ult * 1000.0 + + def sigma_at(L): + f = 3.0 * m_nmm / (2.0 * L) + v_ult / 2.0 + return f, d_sock * L / 3.0, f / (d_sock * L / 3.0) + + print("\nSpar carry-through joint (Rev T1) -- the spar IS the root load path") + print(f" fixed CF spar {SPAR_OD:.0f} x {SPAR_ID:.1f}, Z {SPAR_Z:.1f} mm^3") + sig_spar = m_nmm / SPAR_Z + print(f" spar bending at the ultimate root moment ({m_ult:.2f} N.m):") + print(f" sigma {sig_spar:.2f} MPa FOS {SPAR_ALLOW / sig_spar:.1f} " + f"vs the {SPAR_ALLOW:.0f} MPa stand-in (UNVERIFIED, DEP-1)") + print(" this is the CANTILEVER bound -- the whole root moment taken by") + print(" the spar alone. The two-support bound (nacelle load reacted") + print(" between the wingtip clamp and the wall) gives only " + f"{v_ult * 38.3 / 1000.0:.2f} N.m,") + print(f" sigma {v_ult * 38.3 / SPAR_Z:.2f} MPa. The cantilever bound is " + "quoted because it") + print(" does not depend on the wing skin sharing load, which is not") + print(" characterised for a bonded printed skin.") + + print(f"\n fuselage-side socket, D{d_sock:.0f} bonded " + f"(projected bearing vs {allow:.0f} MPa):") + print(" THE BAY BOUNDS THIS, NOT THE DESIGNER. Owner requirement:") + print(" the centre of the cargo bay stays clear. The bay's clear span") + print(f" begins at hull X {mci.PORT_INB:.1f} and the wall skin sits at") + print(" X -81.33, so the socket has 18.67 mm of depth and no more.") + print(f" {'L mm':>6} {'F N':>8} {'area mm2':>9} " + f"{'sigma MPa':>10} {'FOS':>6}") + for L in (18.67, 30.0, 55.0): + f, a, sg = sigma_at(L) + tag = " <- all the bay allows" if L < 19 else " (would enter the bay)" + print(f" {L:6.2f} {f:8.1f} {a:9.1f} {sg:10.3f} " + f"{allow / sg:6.2f}{tag}") + print(" => the socket CANNOT carry the moment. Stress goes as 1/L^2 and") + print(" the bay has taken the length away. Shear, however, is fine:") + v_only = v_ult / (d_sock * 18.5) + print(f" sigma_shear = {v_ult:.1f} / (20 x 18.5) = {v_only:.3f} MPa" + f" -> FOS {allow / v_only:.0f}") + + # The moment goes to a bonded flange on the wall instead: area, not depth. + print("\n bonded ROOT FLANGE on the inner sidewall (moment path):") + print(" Reacting the moment over wall AREA needs no inboard reach at all") + print(" -- the flange lies flat against the wall and protrudes only its") + print(" own thickness (~5 mm, to X ~ -86, against a bay edge at -100).") + print(" Triangular pressure over height h, couple arm 2h/3:") + print(f" {'h mm':>6} {'w mm':>6} {'F N':>8} {'area mm2':>9} " + f"{'sigma MPa':>10} {'FOS':>6}") + for h, w in ((40.0, 40.0), (60.0, 50.0), (80.0, 60.0), (100.0, 60.0)): + f2 = 3.0 * m_nmm / (2.0 * h) + a2 = w * h / 3.0 + sel = " <- SPECIFIED" if (h, w) == (80.0, 60.0) else "" + print(f" {h:6.0f} {w:6.0f} {f2:8.1f} {a2:9.0f} {f2 / a2:10.3f} " + f"{allow / (f2 / a2):6.1f}{sel}") + print(" 80 x 60 is specified: FOS 29.2, and the cargo section is ~150 mm") + print(" tall inside at this station so 80 mm of height is available.") + print(" This is BETTER than the 55 mm socket it replaces (FOS 4.0), not a") + print(" compromise -- a flange trades an unfavourable 1/L^2 depth term") + print(" for a linear area term.") + + # The LG-11 coupon no longer decides whether this joint is buildable -- + # the flange already clears FOS 4.0 by 7x at the standing 5 MPa figure. It + # would only shrink the flange, which is a packaging convenience rather than + # a gate. Recorded so the coupon's remaining value is not overstated. + print("\n LG-11 coupon sensitivity (flange 80 x 60):") + for alt, why in ((5.0, "standing figure, structural_analysis.md SS7.3"), + (15.0, "the owner's <15 MPa fusion-strength decision rule"), + (47.0, "REF-MAT-001 ASTM D695 bulk compressive, 20 % CF-PETG")): + f2 = 3.0 * m_nmm / (2.0 * 80.0) + a2 = 60.0 * 80.0 / 3.0 + print(f" {alt:5.1f} MPa -> FOS {alt / (f2 / a2):5.1f} ({why})") + print(" The coupon no longer gates BUILDABILITY here -- it only sets") + print(" how small the flange could shrink.") + + # Torsion: the only job the retired aft tie rod could still have had. + q = 0.5 * 1.225 * (40.0 * 0.514444) ** 2 # Pa, 40 kt cruise + s_half = 19025e-6 / 2.0 # m^2, one wing panel + c_mean = 0.111 # m, mean chord + m_aero = 0.25 * q * s_half * c_mean # N.m, Cm ~ 0.25 + m_tors = m_aero * LIMIT_FACTOR * ULTIMATE_FACTOR + tau = m_tors * 1000.0 / (2.0 * math.pi * (d_sock / 2.0) ** 2 * 40.0) + print("\n wing torsion about the spar axis (the retired aft rod's only") + print(" remaining candidate job):") + ult_factor = LIMIT_FACTOR * ULTIMATE_FACTOR + print(f" Cm 0.25 at 40 kt -> {m_aero:.4f} N.m; " + f"ultimate (x{ult_factor:.0f}) {m_tors:.3f} N.m") + print(f" bond shear over a 40 mm socket {tau:.4f} MPa " + f"-> FOS {allow / tau:.0f} vs {allow:.0f} MPa") + print(" NOTE thrust contributes no torque here: the duct axis passes") + print(" through the pivot, which is on the spar axis (plan 003 R12).") + print(" -> the aft tie rod is not needed for torsion either. Retired.") + + def report_two_rod_couple(src, m_ult): """U5/KTD1 default: two bonded CF rods react the root moment as a couple. diff --git a/tools/wing_spar_station_fit.py b/tools/wing_spar_station_fit.py index 68bd829..5ce85b3 100644 --- a/tools/wing_spar_station_fit.py +++ b/tools/wing_spar_station_fit.py @@ -79,6 +79,33 @@ def surf_y(pts, xq): return 0.0 +def base_tc(upper, lower, samples=400): + """Maximum t/c of the tabulated section at t_scale = 1.0. + + Reported so the solved thickness scales can be quoted as an ACTUAL + thickness ratio rather than a bare multiplier -- 't_scale 2.19' means + nothing to an aerodynamicist, 'tip t/c 26.6 %' means the section is no + longer an S1223 (plan 003 RISK-1). + """ + return max(surf_y(upper, x / samples) - surf_y(lower, x / samples) + for x in range(1, samples)) + + +def camber_midline(upper, lower, station_mm, chord): + """Camber-midline height in mm at a station, with NO thickness scaling. + + This is the value the SCAD's spar_bore() centres on and the value the + fuselage side needs for SPAR_Z: s1223_section() opens the thickness + envelope about an UNSCALED camber line (Rev S1b), so the bore centre does + not move when THICKNESS_SCALE changes. Section.at()'s reported midline + DOES carry the scale factor (see its docstring), so it is the wrong number + to hand to the fuselage -- hence this separate, deliberately unscaled + helper. + """ + xq = station_mm / chord + return (surf_y(upper, xq) + surf_y(lower, xq)) / 2.0 * chord + + class Section: """One span station's S1223 section.""" @@ -107,17 +134,31 @@ def frac(self, station_mm): return station_mm / self.chord -def solve_tip_scale(tip, station, bore_d, target_wall): +def solve_scale(sec, station, bore_d, target_wall): """Smallest t_scale that holds `target_wall` over the bore at `station`. Thickness scales linearly, so bisection is overkill -- but the midline moves with it too, so solve numerically rather than by ratio. + + Works for EITHER end station. Through Rev S1b this solved the tip only, + because the tip was the only station a Rev-R2-class D8.3 bore could + threaten -- the root was 15.6 mm deep against an 8.3 mm bore and never + came close to the floor. The unified D20.4 spar + (docs/plans/2026-08-29-003-...) breaks that assumption outright: at the + 28 mm station the ROOT section is 15.60 mm deep against a bore that needs + 22.72 mm, so the root OML now moves too -- the first revision in which it + ever has. A tip-only solver would have reported the tip figure and said + nothing about a root that breaks out by 2.40 mm. Hence the generalised + name and the root row in main(). + + The caller's Section is MUTATED (its t_scale is left at the bisection's + last probe), so pass a throwaway instance -- same contract as before. """ lo, hi = 0.5, 6.0 for _ in range(80): mid_scale = (lo + hi) / 2.0 - tip.t_scale = mid_scale - _d, _m, wu, wd = tip.at(station) + sec.t_scale = mid_scale + _d, _m, wu, wd = sec.at(station) if min(wu, wd) - bore_d / 2.0 < target_wall: lo = mid_scale else: @@ -125,6 +166,14 @@ def solve_tip_scale(tip, station, bore_d, target_wall): return hi +# Retained under its original name: tools/wing_internal_clearance.py and the +# Rev S1b/U6 notes in wings_s1223_revo.scad both cite solve_tip_scale() by +# name. Renaming it silently would break those citations' traceability. +def solve_tip_scale(tip, station, bore_d, target_wall): + """Backward-compatible alias for solve_scale() -- see that docstring.""" + return solve_scale(tip, station, bore_d, target_wall) + + def row(sec, station, bore_d): depth, mid, wu, wd = sec.at(station) up, dn = wu - bore_d / 2.0, wd - bore_d / 2.0 @@ -150,6 +199,12 @@ def main(): ap.add_argument("--scan", action="store_true", help="find the aft-most station the as-built tip " "thickening still supports") + ap.add_argument("--t-root", type=float, default=None, + help="override THICKNESS_SCALE for the root section " + "(default: read from the wing SCAD)") + ap.add_argument("--t-tip", type=float, default=None, + help="override THICKNESS_SCALE_TIP for the tip section " + "(default: read from the wing SCAD)") args = ap.parse_args() src = open(WING_SCAD).read() @@ -157,8 +212,10 @@ def main(): lower = scad_points(src, "S1223_LOWER") c_root = scad_scalar(src, "WING_CHORD_ROOT") c_tip = scad_scalar(src, "WING_CHORD_TIP") - ts_root = scad_scalar(src, "THICKNESS_SCALE") - ts_tip = scad_scalar(src, "THICKNESS_SCALE_TIP") + ts_root = (args.t_root if args.t_root is not None + else scad_scalar(src, "THICKNESS_SCALE")) + ts_tip = (args.t_tip if args.t_tip is not None + else scad_scalar(src, "THICKNESS_SCALE_TIP")) cur = scad_scalar(src, "SPAR_BORE_STATION") sweep = scad_scalar(src, "WING_SWEEP_LE") wing_bore = scad_scalar(src, "SPAR_BORE_OD") @@ -231,11 +288,30 @@ def main(): tip = Section("tip", c_tip, ts_tip, upper, lower) print(row(root, st, args.bore)) print(row(tip, st, args.bore)) - need = solve_tip_scale(Section("tip", c_tip, ts_tip, upper, lower), + # BOTH ends are solved. See solve_scale()'s docstring for why the + # root row is no longer optional: a D20.4 bore breaks the root out + # too, so reporting the tip alone would understate the OML change. + need_root = solve_scale(Section("root", c_root, ts_root, upper, lower), + st, args.bore, args.min_wall) + need_tip = solve_scale(Section("tip", c_tip, ts_tip, upper, lower), st, args.bore, args.min_wall) - verdict = "as-built" if need <= ts_tip + 1e-6 else "MORE THAN as-built" - print(f" -> tip needs THICKNESS_SCALE_TIP >= {need:.3f} " - f"({verdict} {ts_tip:.2f})") + v_root = ("as-built" if need_root <= ts_root + 1e-6 + else "MORE THAN as-built") + v_tip = ("as-built" if need_tip <= ts_tip + 1e-6 + else "MORE THAN as-built") + print(f" -> root needs THICKNESS_SCALE >= {need_root:.3f} " + f"({v_root} {ts_root:.2f}) root t/c " + f"{need_root * base_tc(upper, lower):.1%}") + print(f" -> tip needs THICKNESS_SCALE_TIP >= {need_tip:.3f} " + f"({v_tip} {ts_tip:.2f}) tip t/c " + f"{need_tip * base_tc(upper, lower):.1%}") + # Unscaled camber-midline heights -- what spar_bore() centres on and + # what the fuselage-side SPAR_Z must be derived from. + m_root = camber_midline(upper, lower, st, c_root) + m_tip = camber_midline(upper, lower, st, c_tip) + print(f" -> bore centre on the UNSCALED camber midline: " + f"root +{m_root:.2f} mm / tip +{m_tip:.2f} mm above the " + f"chord line") if __name__ == "__main__":