docs/qe-reports/01-code-quality-complexity.md
Project: wifi-densepose (ruview) Date: 2026-04-05 Analyzer: QE Code Complexity Analyzer v3 Scope: Full codebase -- Rust, Python, C firmware, TypeScript/React Native
This report analyzes code complexity across the entire wifi-densepose project -- 153,139 lines of Rust, 21,399 lines of Python, 7,987 lines of C firmware, and 7,457 lines of TypeScript/React Native. The analysis identified 231 Rust functions with cyclomatic complexity > 10, a single 4,846-line Rust file that constitutes the most critical hotspot in the entire codebase, and systematic code duplication patterns that inflate maintenance cost.
| Metric | Rust | Python | C Firmware | TypeScript |
|---|---|---|---|---|
| Source files | 379 | 63 | 32 | 71 |
| Total lines | 153,139 | 21,399 | 7,987 | 7,457 |
| Functions analyzed | 6,641 | 888 | 145 | 97 |
| CC > 10 | 231 (3.5%) | 16 (1.8%) | 22 (15.2%) | 3 (3.1%) |
| CC > 20 | 74 (1.1%) | 0 | 5 (3.4%) | 1 (1.0%) |
| Functions > 50 lines | 282 (4.2%) | 49 (5.5%) | 26 (17.9%) | 3 (3.1%) |
| Functions > 100 lines | 81 (1.2%) | 6 (0.7%) | 6 (4.1%) | 1 (1.0%) |
| Files > 500 lines | 92 (24%) | 11 (17%) | 4 (25%) | 1 (1.4%) |
| Files > 1000 lines | 24 (6%) | 0 | 1 (6%) | 0 |
| Max nesting > 4 | 215 (3.2%) | 7 (0.8%) | 4 (2.8%) | 2 (2.1%) |
The Python and TypeScript codebases are well-structured. The Rust codebase has pockets of extreme complexity concentrated in the sensing server, and the C firmware has proportionally the highest rate of complex functions.
| Crate | Files | Lines | Assessment |
|---|---|---|---|
| wifi-densepose-wasm-edge | 68 | 28,888 | Largest; 68 vendor modules with repetitive process_frame |
| wifi-densepose-mat | 43 | 19,572 | Mass casualty assessment; moderate complexity |
| wifi-densepose-sensing-server | 18 | 17,825 | CRITICAL -- contains the worst hotspot |
| wifi-densepose-signal | 28 | 16,194 | RuvSense multistatic modules; well-decomposed |
| wifi-densepose-train | 18 | 10,562 | Training pipeline; moderate complexity |
| wifi-densepose-wifiscan | 23 | 5,779 | Multi-BSSID pipeline; clean architecture |
| wifi-densepose-ruvector | 16 | 4,629 | Cross-viewpoint fusion |
| wifi-densepose-hardware | 11 | 4,005 | ESP32 TDM protocol |
| wifi-densepose-desktop | 15 | 3,309 | Tauri desktop app |
| wifi-densepose-nn | 7 | 2,959 | Neural network inference |
| wifi-densepose-core | 5 | 2,596 | Core types and traits |
| Other (6 crates) | 14 | 4,987 | Small, well-sized |
| Total | 267 | 121,306 (src only) |
| Rank | CC | Lines | Depth | Function | File | Line |
|---|---|---|---|---|---|---|
| 1 | 121 | 776 | 8 | main | sensing-server/src/main.rs | 4070 |
| 2 | 66 | 422 | 8 | udp_receiver_task | sensing-server/src/main.rs | 3504 |
| 3 | 55 | 278 | 5 | update | mat/src/tracking/tracker.rs | 171 |
| 4 | 50 | 184 | 8 | process_frame | wasm-edge/src/med_seizure_detect.rs | 157 |
| 5 | 47 | 232 | 6 | train_from_recordings | sensing-server/src/adaptive_classifier.rs | 284 |
| 6 | 42 | 381 | 5 | detect_format | mat/src/integration/csi_receiver.rs | 815 |
| 7 | 41 | 78 | 4 | deserialize_nvs_config | desktop/src/commands/provision.rs | 345 |
| 8 | 41 | 169 | 4 | process_frame | wasm-edge/src/sec_perimeter_breach.rs | 140 |
| 9 | 40 | 472 | 6 | real_training_loop | sensing-server/src/training_api.rs | 825 |
| 10 | 37 | 153 | 6 | process_frame | wasm-edge/src/bld_lighting_zones.rs | 118 |
| 11 | 37 | 178 | 7 | process_frame | wasm-edge/src/ret_table_turnover.rs | 134 |
| 12 | 36 | 154 | 7 | process_frame | wasm-edge/src/lrn_dtw_gesture_learn.rs | 145 |
| 13 | 34 | 167 | 4 | process_frame | wasm-edge/src/exo_breathing_sync.rs | 197 |
| 14 | 34 | 170 | 4 | process_frame | wasm-edge/src/exo_ghost_hunter.rs | 198 |
| 15 | 33 | 134 | 5 | process_frame | wasm-edge/src/ind_structural_vibration.rs | 137 |
| 16 | 33 | 90 | 4 | process_frame | wasm-edge/src/ais_prompt_shield.rs | 65 |
| 17 | 32 | 144 | 5 | process_frame | wasm-edge/src/ret_shelf_engagement.rs | 163 |
| 18 | 32 | 174 | 5 | process_frame | wasm-edge/src/exo_plant_growth.rs | 170 |
| 19 | 31 | 129 | 6 | process_frame | wasm-edge/src/bld_meeting_room.rs | 98 |
| 20 | 31 | 125 | 5 | process_frame | wasm-edge/src/ret_dwell_heatmap.rs | 116 |
sensing-server/src/main.rs (4,846 lines)This is the single worst file in the entire codebase. At 4,846 lines, it is 9.7x the project's 500-line guideline and contains:
God Object: AppStateInner (lines 424-525)
Monolithic main() function (lines 4070-4846)
udp_receiver_task() function (lines 3504-3926)
Systematic Code Duplication (6 instances):
smooth_and_classify / smooth_and_classify_node -- identical logic, differs
only in operating on AppStateInner vs NodeState (could use a trait)smooth_vitals / smooth_vitals_node -- same pattern, identical algorithm
duplicated for AppStateInner vs NodeStateSensingUpdate construction -- built identically in 6 different places
(WiFi task, WiFi fallback, simulate task, ESP32 CSI handler, ESP32 vitals
handler, broadcast tick)wasm-edge Vendor ModulesThe wifi-densepose-wasm-edge crate contains 68 files (28,888 lines), with
nearly every module implementing a process_frame function following the same
pattern. At least 20 of these have CC > 25. This is a textbook case for:
process_frame trait with shared scaffolding92 Rust files exceed the 500-line guideline. The worst offenders:
| Lines | File |
|---|---|
| 4,846 | sensing-server/src/main.rs |
| 1,946 | sensing-server/src/training_api.rs |
| 1,673 | wasm/src/mat.rs |
| 1,664 | train/src/metrics.rs |
| 1,523 | signal/src/ruvsense/pose_tracker.rs |
| 1,498 | sensing-server/src/embedding.rs |
| 1,430 | ruvector/src/crv/mod.rs |
| 1,401 | mat/src/integration/csi_receiver.rs |
| 1,360 | mat/src/integration/hardware_adapter.rs |
| 1,346 | signal/src/ruvsense/field_model.rs |
No circular dependencies detected. The dependency graph is clean and follows the documented crate publishing order. Maximum depth is 3 (CLI -> MAT -> core/signal/nn).
The Python codebase is significantly better structured than the Rust codebase. Only 16 functions (1.8%) exceed CC=10, and no function exceeds CC=20. The code follows clean separation of concerns with distinct layers (api, services, core, hardware, middleware, sensing).
| Rank | CC | Lines | Depth | Function | File | Line |
|---|---|---|---|---|---|---|
| 1 | 19 | 90 | 4 | estimate_poses | services/pose_service.py | 491 |
| 2 | 18 | 126 | 6 | _print_text_status | commands/status.py | 350 |
| 3 | 15 | 72 | 4 | websocket_events_stream | api/routers/stream.py | 156 |
| 4 | 14 | 100 | 3 | health_check | database/connection.py | 349 |
| 5 | 14 | 47 | 3 | get_overall_health | services/health_check.py | 384 |
| 6 | 13 | 52 | 3 | _authenticate_request | middleware/auth.py | 236 |
| 7 | 13 | 64 | 4 | _handle_preflight | middleware/cors.py | 89 |
| 8 | 13 | 84 | 4 | websocket_pose_stream | api/routers/stream.py | 69 |
| 9 | 13 | 65 | 4 | generate_signal_field | sensing/ws_server.py | 236 |
| 10 | 13 | 74 | 6 | create_collector | sensing/rssi_collector.py | 770 |
| Lines | File | Concern |
|---|---|---|
| 856 | services/pose_service.py | Pose estimation service -- acceptable for a service class |
| 843 | sensing/rssi_collector.py | RSSI collection with 3 collector implementations |
| 772 | tasks/monitoring.py | Background monitoring tasks |
| 640 | database/connection.py | Database connection management |
| 620 | cli.py | CLI command handler |
| 610 | tasks/backup.py | Backup task logic |
| 598 | tasks/cleanup.py | Cleanup task logic |
| 519 | sensing/ws_server.py | WebSocket server |
| 515 | hardware/csi_extractor.py | CSI data extraction |
| 510 | commands/status.py | Status reporting |
| 504 | middleware/error_handler.py | Error handling middleware |
_print_text_status (CC=18, 126 lines) in commands/status.py
is essentially a large formatting function that could be split into per-component
formattersThe C firmware has the highest proportion of complex functions (15.2% with CC>10). This is partly expected for embedded C, but several functions warrant attention.
| Rank | CC | Lines | Depth | Function | File | Line |
|---|---|---|---|---|---|---|
| 1 | 59 | 314 | 3 | nvs_config_load | nvs_config.c | 19 |
| 2 | 40 | 185 | 3 | process_frame | edge_processing.c | 708 |
| 3 | 25 | 125 | 5 | display_ui_update | display_ui.c | 259 |
| 4 | 22 | 94 | 3 | mock_timer_cb | mock_csi.c | 518 |
| 5 | 22 | 174 | 3 | app_main | main.c | 127 |
| 6 | 21 | 136 | 3 | rvf_parse | rvf_parser.c | 33 |
| 7 | 19 | 119 | 3 | wasm_runtime_load | wasm_runtime.c | 442 |
| 8 | 18 | 84 | 3 | send_vitals_packet | edge_processing.c | 554 |
| 9 | 17 | 74 | 4 | update_multi_person_vitals | edge_processing.c | 474 |
| 10 | 17 | 34 | 3 | ld2410_feed_byte | mmwave_sensor.c | 274 |
nvs_config_load (CC=59, 314 lines)This function in nvs_config.c has the highest complexity of any C function.
It loads 30+ configuration parameters from NVS flash storage, each with its own
error handling and default-value fallback. This is a classic case for:
edge_processing.c (1,067 lines)This is the only C file exceeding 1,000 lines. It implements the full dual-core
CSI processing pipeline (11 processing stages). The process_frame function
(CC=40, 185 lines) combines phase extraction, variance tracking, subcarrier
selection, bandpass filtering, BPM estimation, presence detection, and fall
detection in a single function.
The code documents that process_frame + update_multi_person_vitals combined
used 6.5-7.5 KB of the 8 KB task stack, necessitating static scratch buffers.
This indicates the functions are pushing resource limits and should be
decomposed for safety margin.
The UI codebase is the cleanest in the project. Only 3 functions exceed CC=10, no file exceeds 1,000 lines, and the component architecture follows React best practices with proper separation of screens, components, stores, and services.
GaussianSplatWebView.web.tsx (CC=70, 747 lines)This is the only significant complexity hotspot in the TypeScript codebase.
The GaussianSplatWebViewWeb component (CC=70, 467 lines) manages:
This component should be decomposed into:
poseStore.ts, matStore.ts, settingsStore.ts): Clean
state management with proper typinguseMatBridge, useOccupancyGrid, useGaussianBridge):
Good separation of WebSocket logic from UI componentsHotspots are ranked by a composite score combining complexity, file size, nesting depth, and duplication density.
| Rank | Risk | CC | Lines | File | Function | Primary Issue |
|---|---|---|---|---|---|---|
| 1 | 0.98 | 121 | 776 | sensing-server/main.rs:4070 | main | God function; CLI dispatch |
| 2 | 0.96 | -- | 4,846 | sensing-server/main.rs | (file) | God file; 9.7x guideline |
| 3 | 0.94 | 66 | 422 | sensing-server/main.rs:3504 | udp_receiver_task | 3 packet types monolithic |
| 4 | 0.90 | -- | 40+ fields | sensing-server/main.rs:424 | AppStateInner | God object |
| 5 | 0.87 | 59 | 314 | nvs_config.c:19 | nvs_config_load | Needs table-driven approach |
| 6 | 0.85 | 55 | 278 | mat/tracking/tracker.rs:171 | update | Complex tracking logic |
| 7 | 0.82 | 50 | 184 | wasm-edge/med_seizure_detect.rs:157 | process_frame | Deep nesting (8) |
| 8 | 0.80 | 70 | 467 | GaussianSplatWebView.web.tsx:277 | GaussianSplatWebViewWeb | Three.js god component |
| 9 | 0.78 | 47 | 232 | sensing-server/adaptive_classifier.rs:284 | train_from_recordings | Complex training logic |
| 10 | 0.76 | 42 | 381 | mat/csi_receiver.rs:815 | detect_format | Format detection chain |
| 11 | 0.75 | 40 | 472 | sensing-server/training_api.rs:825 | real_training_loop | Long training loop |
| 12 | 0.73 | 40 | 185 | edge_processing.c:708 | process_frame | 11-stage DSP in one func |
| 13 | 0.70 | -- | 6x | sensing-server/main.rs | SensingUpdate builds | Duplicated 6 times |
| 14 | 0.68 | 19 | 90 | services/pose_service.py:491 | estimate_poses | Highest Python CC |
| 15 | 0.65 | -- | 1,946 | sensing-server/training_api.rs | (file) | 3.9x guideline |
| 16 | 0.63 | -- | 1,673 | wasm/mat.rs | (file) | 3.3x guideline |
| 17 | 0.61 | -- | 1,664 | train/metrics.rs | (file) | 3.3x guideline |
| 18 | 0.59 | -- | 1,523 | signal/ruvsense/pose_tracker.rs | (file) | 3.0x guideline |
| 19 | 0.57 | 25 | 125 | display_ui.c:259 | display_ui_update | Deep nesting (5) |
| 20 | 0.55 | 28 | 106 | sensing-server/main.rs:2161 | estimate_persons_from_correlation | Complex graph algorithm |
| Smell | Location | Severity |
|---|---|---|
| God File | sensing-server/main.rs (4,846 lines) | CRITICAL |
| God Object | AppStateInner (40+ fields) | CRITICAL |
| God Function | main() (776 lines, CC=121) | CRITICAL |
| God Function | udp_receiver_task() (422 lines, CC=66) | HIGH |
| Pattern | Instances | Lines Duplicated | Severity |
|---|---|---|---|
smooth_and_classify / smooth_and_classify_node | 2 | ~50 per copy | HIGH |
smooth_vitals / smooth_vitals_node | 2 | ~50 per copy | HIGH |
SensingUpdate {} construction | 6 | ~40 per instance | HIGH |
| Person count estimation pattern | 3+ | ~15 per instance | MEDIUM |
frame_history capacity check | 6+ | ~3 per instance | LOW |
tracker_bridge::tracker_update call pattern | 5 | ~5 per instance | MEDIUM |
Estimated duplicated code in main.rs alone: ~450 lines (9.3% of file).
215 Rust functions exceed 4 levels of nesting. The worst cases:
main(): 8 levels (lines 4070-4846)udp_receiver_task(): 8 levels (lines 3504-3926)process_frame in wasm-edge: 7-8 levels43 Rust functions have more than 5 parameters. Notable:
process_frame variants in wasm-edge: 5-7 parameters eachextract_features_from_frame: 3 parameters but returns a 5-tupleThe wifi-densepose-wasm-edge crate has 68 files following a near-identical
pattern. At least 35 have a process_frame function with CC > 20. A trait-based
or macro-based approach would reduce this to a fraction of the code.
| Component | Score | Rating | Key Blockers |
|---|---|---|---|
| wifi-densepose-core | 85/100 | EASY | Pure types, no side effects |
| wifi-densepose-signal | 78/100 | EASY | Mostly pure computation |
| wifi-densepose-train | 72/100 | MODERATE | External dataset dependencies |
| wifi-densepose-mat | 68/100 | MODERATE | Integration with core+signal+nn |
| wifi-densepose-wifiscan | 75/100 | EASY | Platform-specific but well-abstracted |
| wifi-densepose-sensing-server | 32/100 | VERY DIFFICULT | God object, coupled state, async |
| wifi-densepose-wasm-edge | 55/100 | MODERATE | Repetitive but self-contained |
| archive/v1/src (Python) | 70/100 | MODERATE | Good DI, some tight coupling |
| firmware (C) | 40/100 | DIFFICULT | Hardware deps, global state |
| ui/mobile (TypeScript) | 72/100 | MODERATE | Component isolation is good |
Estimated effort: 3-5 days Impact: Reduces maintenance cost for the most-changed file in the project
Extract AppStateInner into bounded contexts:
SensingState -- frame history, features, classificationVitalSignState -- HR/BR smoothing, detector, buffersRecordingState -- recording lifecycle, file handlesTrainingState -- training status, configModelState -- loaded model, progressive loader, SONA profilesNodeRegistry -- per-node states, pose tracker, multistatic fuserExtract command handlers from main():
run_benchmark() (lines 4082-4089)run_export_rvf() (lines 4092-4142)run_pretrain() (lines 4145-4247)run_embed() (lines 4250-4312)run_build_index() (lines 4315-4357)run_train() (lines 4360-end)run_server() -- the remaining server startupExtract SensingUpdate builder:
Create a SensingUpdateBuilder that encapsulates the repeated 6-instance
construction pattern.
Unify node vs global variants via trait:
trait SmoothingState {
fn smoothed_motion(&self) -> f64;
fn set_smoothed_motion(&mut self, v: f64);
// ... etc
}
impl SmoothingState for AppStateInner { ... }
impl SmoothingState for NodeState { ... }
Then a single smooth_and_classify<S: SmoothingState>() replaces both copies.
Extract udp_receiver_task into packet-type handlers:
handle_vitals_packet()handle_wasm_packet()handle_csi_frame()nvs_config_load Table-Driven RefactorEstimated effort: 1 day Impact: Reduces CC from 59 to approximately 5
Replace the 314-line sequential NVS load with a descriptor table:
typedef struct {
const char *key;
nvs_type_t type;
void *dest;
size_t size;
const void *default_val;
} nvs_param_desc_t;
static const nvs_param_desc_t params[] = {
{"node_id", NVS_U8, &cfg->node_id, 1, &(uint8_t){1}},
// ... 30+ entries
};
process_frame Trait ExtractionEstimated effort: 2-3 days Impact: Reduces 28,888 lines by an estimated 30-40%
Define a common trait:
trait WasmEdgeModule {
fn name(&self) -> &str;
fn init(&mut self, config: &ModuleConfig);
fn process_frame(&mut self, ctx: &mut FrameContext) -> Vec<WasmEvent>;
}
Extract shared signal processing (phase extraction, variance tracking, BPM estimation) into reusable pipeline stages.
Estimated effort: 1 day Impact: Reduces CC from 70 to approximately 10-15 per component
Split into:
SceneManager -- Three.js initialization, camera, lightingSkeletonRenderer -- body parts, keypoints, bonesSignalFieldRenderer -- grid, heatmap visualizationuseFrameAdapter -- data parsing hookedge_processing.c Pipeline DecompositionEstimated effort: 1-2 days
Impact: Reduces process_frame CC from 40 to ~10; improves stack safety
Split into stage functions:
static void stage_phase_extract(frame_ctx_t *ctx);
static void stage_variance_update(frame_ctx_t *ctx);
static void stage_subcarrier_select(frame_ctx_t *ctx);
static void stage_bandpass_filter(frame_ctx_t *ctx);
static void stage_bpm_estimate(frame_ctx_t *ctx);
static void stage_presence_detect(frame_ctx_t *ctx);
static void stage_fall_detect(frame_ctx_t *ctx);
Estimated effort: 0.5 days
Impact: Reduces _print_text_status CC from 18 to ~5 per formatter
Split _print_text_status (126 lines) into per-component formatters:
_format_api_status, _format_hardware_status, _format_streaming_status, etc.
| Metric | Warn | Fail | Current Violations |
|---|---|---|---|
| File size | > 500 lines | > 1,000 lines | 92 warn, 25 fail |
| Function CC | > 15 | > 25 | ~150 warn, ~74 fail |
| Function lines | > 50 | > 100 | ~360 warn, ~94 fail |
| Nesting depth | > 4 | > 6 | ~215 warn, ~30 fail |
| Parameter count | > 5 | > 7 | ~43 warn, ~10 fail |
cargo clippy with custom lints or complexity-rsCC Range | Functions | Percentage | Bar
------------|-----------|------------|----------------------------------
1-5 | 5,728 | 86.2% | ####################################
6-10 | 682 | 10.3% | ####
11-15 | 107 | 1.6% | #
16-20 | 50 | 0.8% |
21-30 | 41 | 0.6% |
31-50 | 24 | 0.4% |
>50 | 9 | 0.1% |
CC Range | Functions | Percentage | Bar
------------|-----------|------------|----------------------------------
1-5 | 740 | 83.3% | ####################################
6-10 | 132 | 14.9% | ######
11-15 | 13 | 1.5% | #
16-20 | 3 | 0.3% |
CC Range | Functions | Percentage | Bar
------------|-----------|------------|----------------------------------
1-5 | 73 | 50.3% | ####################################
6-10 | 50 | 34.5% | #########################
11-15 | 6 | 4.1% | ###
16-20 | 8 | 5.5% | ####
21-30 | 3 | 2.1% | ##
>30 | 5 | 3.4% | ##
=> may include non-decision match armsReport generated by QE Code Complexity Analyzer v3 Codebase snapshot: commit 85434229 on branch qe-reports