Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c8e66e4
Add clibrate_strobe_output.py script to web-server folder
peteattayek Mar 19, 2026
67b5cf0
Set current limits for both V3 LED and Old 100W LED
peteattayek Mar 19, 2026
5ebf706
Update PCB Assembly documentation with connection guide for V3
peteattayek Mar 19, 2026
f31c82b
Update assembly instructions to only say 1 rpi instead of 1-2
peteattayek Mar 19, 2026
c607151
Change ADC and DAC to use SPI1 instead of SPI0. Change DIAG pin to SP…
peteattayek Mar 20, 2026
25d5f81
Cleanup calibrate_strobe_output.py script
peteattayek Mar 20, 2026
9028d16
change old led current to 9A
peteattayek Mar 20, 2026
8e7b717
Add installation files for spi1 setup and Python runtime dependencies
peteattayek Mar 20, 2026
0eeab42
Update wiring diagram and documentation
peteattayek Mar 21, 2026
e9eb3d5
make kDAC_setting internal-only, not passed to C++ processes
connorgallopo Mar 21, 2026
b253925
route kDAC_setting to calibration_data.json instead of user_settings
connorgallopo Mar 21, 2026
76f469f
strobe calibration manager skeleton with hardware open/close
connorgallopo Mar 21, 2026
683d5c4
add calibration algorithm (_find_dac_start, _calibrate) + fix macOS t…
connorgallopo Mar 21, 2026
6e3d083
add async public API for strobe calibration (start, cancel, diagnosti…
connorgallopo Mar 21, 2026
9e0225f
wire strobe calibration API endpoints into server.py
connorgallopo Mar 21, 2026
365dfa0
add strobe calibration UI to calibration page
connorgallopo Mar 21, 2026
1e43577
use DigitalOutputDevice instead of LED, harden close and gc handling
connorgallopo Mar 21, 2026
839c7b0
remove standalone CLI calibration script, replaced by web UI
connorgallopo Mar 21, 2026
da8c767
move strobe deps to web server: pip for spidev/gpiozero, apt for syst…
connorgallopo Mar 21, 2026
eaa54dc
fix broken SVG image path in pcb-assembly docs
connorgallopo Mar 21, 2026
ef3e0be
move board version and enclosure to basic settings, fix strobe UI sta…
connorgallopo Mar 21, 2026
29aed9d
fix status polling data, null checks, settle timing, dac sweep edge case
connorgallopo Mar 21, 2026
c042fb0
add pre-flight board check, use for loop, surface specific failure me…
connorgallopo Mar 22, 2026
403dc87
use system lgpio instead of custom build, fixes python3-lgpio version…
connorgallopo Mar 23, 2026
1aaf231
always configure base system params in config.txt even without camera…
connorgallopo Mar 23, 2026
6a91461
fix config.txt setup when no cameras attached
connorgallopo Mar 23, 2026
1959f68
hardening
connorgallopo Mar 24, 2026
3a2b832
Add standard fix in place of #189
connorgallopo Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Software/web-server/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ def _is_calibration_field(self, key: str) -> bool:
"calibration.",
"kAutoCalibration",
"_ENCLOSURE_",
"kDAC_setting",
]
return any(pattern in key for pattern in calibration_patterns)

Expand Down
15 changes: 13 additions & 2 deletions Software/web-server/configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@
},
"gs_config.strobing.kConnectionBoardVersion": {
"category": "System",
"subcategory": "advanced",
"subcategory": "basic",
"basicSubcategory": "System",
"displayName": "Connection Board Version",
"description": "The original Ver. 1.0 board inverts the external shutter signal, so this setting tells the system whether to pre-invert the signal or not.",
Expand All @@ -281,9 +281,20 @@
"passedVia": "json",
"passedTo": "both"
},
"gs_config.strobing.kDAC_setting": {
"category": "Strobing",
"subcategory": "advanced",
"displayName": "Strobe DAC Setting",
"description": "Calibrated DAC value for strobe LED current control. Set by the strobe calibration tool.",
"type": "number",
"min": 0,
"max": 255,
"default": null,
"internal": true
},
"gs_config.system.kEnclosureVersion": {
"category": "System",
"subcategory": "advanced",
"subcategory": "basic",
"basicSubcategory": "System",
"displayName": "Enclosure Version",
"description": "The enclosure version (type) can affect various aspects of the system. For example, the distance of the reference ball in the calibration rig, as different enclosures position the cameras at different locations.",
Expand Down
2 changes: 2 additions & 0 deletions Software/web-server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ pillow==11.3.0
pyyaml==6.0.3
aiofiles==25.1.0
websockets==15.0.1
spidev>=3.6
gpiozero>=2.0
58 changes: 58 additions & 0 deletions Software/web-server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from managers import ConnectionManager, ShotDataStore
from parsers import ShotDataParser
from pitrac_manager import PiTracProcessManager
from strobe_calibration_manager import StrobeCalibrationManager
from testing_tools_manager import TestingToolsManager

logger = logging.getLogger(__name__)
Expand All @@ -46,6 +47,7 @@ def __init__(self):
self.pitrac_manager = PiTracProcessManager(self.config_manager)
self.calibration_manager = CalibrationManager(self.config_manager)
self.testing_manager = TestingToolsManager(self.config_manager)
self.strobe_calibration_manager = StrobeCalibrationManager(self.config_manager)
self.mq_conn: Optional[stomp.Connection] = None
self.listener: Optional[ActiveMQListener] = None
self.reconnect_task: Optional[asyncio.Task] = None
Expand Down Expand Up @@ -436,6 +438,62 @@ async def upload_test_image(file: UploadFile = File(...)) -> Dict[str, Any]:
logger.error(f"Error uploading test image: {e}")
return {"status": "error", "message": str(e)}

# Strobe calibration endpoints
@self.app.get("/api/strobe-calibration/status")
async def strobe_calibration_status() -> Dict[str, Any]:
"""Get current strobe calibration status"""
return self.strobe_calibration_manager.get_status()

@self.app.get("/api/strobe-calibration/settings")
async def strobe_calibration_settings() -> Dict[str, Any]:
"""Get saved strobe calibration settings"""
return await self.strobe_calibration_manager.get_saved_settings()

@self.app.post("/api/strobe-calibration/start")
async def start_strobe_calibration(request: Request) -> Dict[str, Any]:
"""Start strobe calibration as a background task"""
body = await request.json()
led_type = body.get("led_type", "v3")
target_current = body.get("target_current")
overwrite = body.get("overwrite", False)

task = asyncio.create_task(
self.strobe_calibration_manager.start_calibration(
led_type=led_type,
target_current=target_current,
overwrite=overwrite,
)
)
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)

return {"status": "started"}

@self.app.post("/api/strobe-calibration/cancel")
async def cancel_strobe_calibration() -> Dict[str, Any]:
"""Cancel a running strobe calibration"""
self.strobe_calibration_manager.cancel()
return {"status": "ok"}

@self.app.get("/api/strobe-calibration/diagnostics")
async def strobe_calibration_diagnostics() -> Dict[str, Any]:
"""Read strobe hardware diagnostics"""
return await self.strobe_calibration_manager.read_diagnostics()

@self.app.post("/api/strobe-calibration/set-dac")
async def strobe_set_dac(request: Request) -> Dict[str, Any]:
"""Manually set the DAC value"""
body = await request.json()
value = body.get("value")
if value is None or not isinstance(value, int):
return {"status": "error", "message": "Integer 'value' is required"}
return await self.strobe_calibration_manager.set_dac_manual(value)

@self.app.get("/api/strobe-calibration/dac-start")
async def strobe_dac_start() -> Dict[str, Any]:
"""Get the safe DAC starting value"""
return await self.strobe_calibration_manager.get_dac_start()

@self.app.get("/api/cameras/detect")
async def detect_cameras() -> Dict[str, Any]:
"""Auto-detect connected cameras"""
Expand Down
102 changes: 97 additions & 5 deletions Software/web-server/static/css/calibration.css
Original file line number Diff line number Diff line change
Expand Up @@ -582,29 +582,121 @@
color: var(--error);
}

/* Strobe Calibration */
.strobe-calibration {
background: var(--bg-card);
border-radius: 12px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow: var(--shadow-md);
}

.strobe-header {
margin-bottom: 1.5rem;
}

.strobe-header h3 {
color: var(--text-primary);
margin-bottom: 1rem;
}

.strobe-status-bar {
display: flex;
gap: 2rem;
background: var(--bg-hover);
padding: 0.75rem 1rem;
border-radius: 6px;
border: 1px solid var(--border-color);
}

.strobe-controls {
margin-bottom: 1.5rem;
}

.strobe-control-row {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}

.strobe-label {
color: var(--text-secondary);
font-weight: 600;
min-width: 80px;
}

.strobe-select {
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--bg-hover);
color: var(--text-primary);
font-size: 0.95rem;
cursor: pointer;
}

.strobe-calibration .progress-bar {
margin-bottom: 0.5rem;
}

.strobe-calibration h4 {
color: var(--text-primary);
margin-bottom: 1rem;
}

#strobe-result-area .result-card {
border-width: 2px;
border-style: solid;
border-color: var(--border-color);
transition: border-color 0.3s ease;
}

#strobe-diagnostics-area {
margin-top: 1.5rem;
}

#strobe-diagnostics-area h4 {
color: var(--text-primary);
margin-bottom: 1rem;
}

#strobe-diagnostics-warning {
margin-top: 1rem;
}

/* Mobile Responsive */
@media (max-width: 768px) {
.main-content {
padding: 1rem;
}

.calibration-wizard {
padding: 1rem;
}

.wizard-steps {
margin-bottom: 2rem;
}

.step-label {
font-size: 0.8rem;
}

.camera-options {
grid-template-columns: 1fr;
}

.result-cards {
grid-template-columns: 1fr;
}

.strobe-status-bar {
flex-direction: column;
gap: 0.5rem;
}

.strobe-control-row {
flex-wrap: wrap;
}
}
Loading
Loading