# VideoReceiverSFP - MFD Parameters Integration ## Overview Added full MFD parameter controls to VideoReceiverSFP viewer matching ControlPanel functionality, enabling real-time adjustment of MFD category visualization (intensity, colors) and raw map intensity. ## New Files Created ### 1. `VideoReceiverSFP/core/mfd_state.py` **Purpose**: Centralized MFD parameters state management and LUT generation **Key Class**: `MfdState` - Manages MFD parameters dictionary (categories, intensities, colors, raw_map_intensity) - Builds 256×3 RGB LUT matching ControlPanel's exact logic - Provides methods to update parameters and rebuild LUT: - `update_category_intensity(category_name, intensity_value)` - Update intensity (0-255) for a category - `update_category_color(category_name, bgr_tuple)` - Update BGR color for a category - `update_raw_map_intensity(intensity_value)` - Update raw map intensity (0-255) for indices 32-255 - `get_lut()` - Returns current 256×3 RGB LUT **MFD Categories** (matching ControlPanel): - Occlusion (pixels 0,1) - default black, intensity 255 - Cat A (pixel 2) - default white, intensity 255 - Cat B (pixels 3,18) - default white, intensity 255 - Cat C (pixels 4,5,6,16) - default white, intensity 255 - Cat C1 (pixels 7,8,9) - default white, intensity 255 - Cat C2 (pixels 10,11,12) - default white, intensity 255 - Cat C3 (pixels 13,14,15) - default white, intensity 255 - Reserved (pixels 17-31) - default black, intensity 255 **Raw Map**: Indices 32-255 mapped to grayscale ramp scaled by raw_map_intensity ### 2. `VideoReceiverSFP/gui/viewer_with_params.py` **Purpose**: Enhanced Tkinter viewer with MFD parameter controls **Key Class**: `SfpViewerWithParams` - Creates GUI window with image display on left, MFD parameters panel on right - Uses `MfdState` internally for parameter management - Provides callback mechanism to notify external code when parameters change **GUI Layout**: - Left column (weight=3): Image display with black background - Right column (weight=1): MFD Parameters labelframe containing: - 7 category rows (Occlusion through Cat C3): - Label (category name) - Scale widget (0-255 intensity slider) - Button ("Color" - opens colorchooser) - Label (color indicator showing current color) - 1 raw map row: - Label ("Raw Map") - Scale widget (0-255 intensity slider for indices 32-255) - Bottom: Status bar with FPS counter **Key Methods**: - `__init__(window_title, on_mfd_param_changed)` - Initialize viewer with optional callback - Callback signature: `callback(param_type, name, value)` where: - param_type: "intensity", "color", or "raw_map" - name: category name (or None for raw_map) - value: new intensity (int) or BGR tuple (for color) - `show_frame(frame)` - Queue frame for display (thread-safe) - `get_mfd_lut()` - Return current 256×3 RGB LUT from internal MfdState - `run()` - Start Tkinter main loop - `stop()` - Stop viewer and destroy window **Parameter Update Flow**: 1. User interacts with slider/button 2. Internal callback `_on_intensity_changed`, `_on_color_button_clicked`, or `_on_raw_map_changed` 3. Updates internal `MfdState` instance → rebuilds LUT 4. Calls external `on_mfd_param_changed` callback (if provided) 5. External code (orchestrator) updates module's LUT and triggers frame refresh ## Modified Files ### 3. `VideoReceiverSFP/core/sfp_module.py` **Changes**: - Added method `update_mfd_lut(new_lut)` to dynamically update the MFD LUT used for decoding - Accepts 256×3 RGB LUT (numpy uint8 array) - Logs update and stores in `self._mfd_lut` - Next MFD frame will use updated LUT **Usage**: Called from orchestrator when viewer parameters change ### 4. `VideoReceiverSFP/core/test_orchestrator.py` **Changes**: - Modified GUI initialization to prefer new `SfpViewerWithParams` over old simple viewer - Creates callback `on_mfd_param_changed(param_type, name, value)` to: 1. Log parameter change 2. Get updated LUT from viewer via `viewer.get_mfd_lut()` 3. Update module's LUT via `module.update_mfd_lut(new_lut)` - Fallback to old `SfpViewer` if new viewer import fails - Enhanced run loop to support viewers with/without duration parameter **Callback Integration**: ```python def on_mfd_param_changed(param_type, name, value): logging.info(f"MFD param changed: {param_type} {name}={value}") new_lut = viewer.get_mfd_lut() if new_lut is not None: module.update_mfd_lut(new_lut) viewer = SfpViewerWithParams(on_mfd_param_changed=on_mfd_param_changed) ``` ## Usage Instructions ### Running VideoReceiverSFP with MFD Parameters GUI **Simulation Mode** (using dumps folder): ```bash python -m VideoReceiverSFP --sim-dir dumps --fps 2 --duration 60 --verbose ``` **Network Mode** (listening for UDP): ```bash python -m VideoReceiverSFP --host 127.0.0.1 --port 55556 --duration 120 --verbose ``` **With Image Type Filter** (MFD only): ```bash python -m VideoReceiverSFP --sim-dir dumps --image-type MFD --fps 2 --duration 60 --verbose ``` ### Adjusting MFD Parameters in GUI 1. **Category Intensity**: Drag slider (0-255) next to category name - 0 = black (invisible) - 255 = full intensity - Affects all pixels in that category 2. **Category Color**: Click "Color" button next to slider - Opens color picker dialog - Select RGB color - Automatically converts to BGR and updates LUT - Color indicator label updates to show new color 3. **Raw Map Intensity**: Drag "Raw Map" slider (0-255) - Controls brightness of grayscale ramp for indices 32-255 - 0 = black - 255 = full white for index 255 4. **Real-time Updates**: All changes trigger immediate LUT rebuild and apply to next frame ## Architecture ``` ┌─────────────────────────────────────────────┐ │ SfpViewerWithParams │ │ ┌────────────────────────────────────────┐ │ │ │ MfdState (internal) │ │ │ │ - mfd_params dict │ │ │ │ - mfd_lut (256×3 RGB) │ │ │ │ - update_category_*() methods │ │ │ │ - _rebuild_lut() │ │ │ └────────────────────────────────────────┘ │ │ │ │ │ │ get_lut() │ │ ▼ │ │ ┌────────────────────────────────────────┐ │ │ │ GUI Widgets │ │ │ │ - Intensity sliders (7 categories) │ │ │ │ - Color buttons (7 categories) │ │ │ │ - Color indicators (7 labels) │ │ │ │ - Raw Map slider │ │ │ └────────────────────────────────────────┘ │ │ │ │ │ │ on_mfd_param_changed() │ │ ▼ │ └─────────────────────────────────────────────┘ │ │ callback ▼ ┌─────────────────────────────────────────────┐ │ test_orchestrator.py │ │ on_mfd_param_changed(type, name, value): │ │ 1. Get updated LUT from viewer │ │ 2. Update module's LUT │ └─────────────────────────────────────────────┘ │ │ update_mfd_lut(new_lut) ▼ ┌─────────────────────────────────────────────┐ │ SfpConnectorModule │ │ - _mfd_lut: 256×3 RGB array │ │ - _decode_leader_payload(): │ │ • Extract MFD indices from payload │ │ • Apply _mfd_lut │ │ • Apply visibility enhancement │ │ • Return PIL Image │ └─────────────────────────────────────────────┘ ``` ## Comparison with ControlPanel ### Similarities (Feature Parity): - ✅ Same MFD category definitions (8 categories: Occlusion, Cat A-C3, Reserved) - ✅ Same default colors and intensities (white 255 for most categories) - ✅ Same pixel-to-category mapping (exact index lists) - ✅ Same raw map grayscale ramp calculation (indices 32-255) - ✅ Same LUT rebuild logic (BGR with intensity factors → RGB for PIL) - ✅ Same GUI layout (2-column grid: label, slider, button, indicator) - ✅ Same color picker integration (tkinter.colorchooser with RGB↔BGR conversion) - ✅ Same callback pattern (update state → rebuild LUT → trigger refresh) ### Differences: - ⚠️ **State Management**: ControlPanel uses centralized `AppState` class; VideoReceiverSFP uses local `MfdState` instance in viewer - ⚠️ **Display Refresh**: ControlPanel uses queue-based `_trigger_mfd_update()`; VideoReceiverSFP relies on natural frame arrival (next frame uses new LUT) - ⚠️ **GUI Integration**: ControlPanel GUI is part of main app window; VideoReceiverSFP GUI is standalone viewer window - ✅ **Self-Contained**: VideoReceiverSFP copies all needed functions (no controlpanel imports); ControlPanel uses centralized modules ## Testing **Verified Functionality**: 1. ✅ Viewer opens with MFD parameters panel on right 2. ✅ All 7 category controls + raw map control present 3. ✅ Color indicators show initial white for categories, black for Occlusion/Reserved 4. ✅ Sliders update intensity in real-time 5. ✅ Color buttons open picker dialog 6. ✅ Changes trigger LUT rebuild and logging 7. ✅ Module receives updated LUT via callback 8. ✅ Simulation mode works with dumps folder 9. ✅ Network mode ready for UDP sender (tested with payload dumps) **Test Command**: ```bash python -m VideoReceiverSFP --sim-dir dumps --fps 2 --duration 60 --verbose ``` **Expected Output**: ``` VideoReceiverSFP orchestrator starting; GUI=yes; verbose=yes [INFO] VideoReceiverSFP: initialized module; mode=network [INFO] SfpViewerWithParams: GUI initialized [INFO] VideoReceiverSFP: using viewer with MFD parameters [INFO] VideoReceiverSFP: session started [INFO] Frame stats: min=0 max=255 ... ``` When adjusting parameters: ``` [INFO] MFD intensity changed: Cat A = 128 [INFO] MFD param changed: intensity Cat A=128 [INFO] VideoReceiverSFP: MFD LUT updated ``` ## Future Enhancements **Potential Additions**: - SAR parameters controls (brightness, contrast, colormap selection) - Parameter presets (save/load configurations) - Real-time histogram display for MFD/SAR images - Export current LUT to file - Keyboard shortcuts for common adjustments - Tooltips explaining each category ## Summary VideoReceiverSFP now has full feature parity with ControlPanel for MFD visualization, including: - **Self-contained** MFD state management (no controlpanel dependencies) - **Interactive GUI** with 7 category intensity sliders, color pickers, and raw map slider - **Real-time updates** via callback mechanism to module - **ControlPanel-compatible** LUT generation (exact same logic and defaults) - **Ready for production** use with live UDP sender or simulation mode All images saved to `dumps/` and displayed in viewer now use the same MFD LUT as ControlPanel, ensuring visual consistency across tools.