param( [Parameter(Mandatory = $true)][string]$DataRoot, [Parameter(Mandatory = $true)][string]$OutputRoot, [Parameter(Mandatory = $true)][string]$RtkCapture, [Parameter(Mandatory = $true)][string]$ImuCapture, [string]$LidarObject = "frontlidar", [string]$Timezone = "+08:00", [string[]]$StationNames = @(), [int]$Stride = 1, [double]$RtkMaxDtMs = 150.0, [double]$ImuBeforeMs = 100.0, [double]$ImuAfterMs = 100.0, [switch]$SkipLidarExport, [switch]$SkipSerialParsing ) $ErrorActionPreference = "Stop" $RepoRoot = Split-Path -Parent $PSScriptRoot $Exporter = Join-Path $RepoRoot "tools\frontlidar_dlog_export.py" $Builder = Join-Path $RepoRoot "tools\build_multisensor_npz.py" $Parser = Join-Path $RepoRoot "tools\rscap_v2\parse_rtk_imu_v2.py" $Auditor = Join-Path $RepoRoot "tools\rscap_v2\audit_capture_v2.py" $ExportRoot = Join-Path $OutputRoot "export" $ParsedRoot = Join-Path $OutputRoot "parsed" $CombinedRoot = Join-Path $OutputRoot "combined" function Run-Python { param([string]$Stage, [string[]]$Arguments) Write-Host "[$Stage]" & python @Arguments if ($LASTEXITCODE -ne 0) { throw "$Stage failed with Python exit code $LASTEXITCODE" } } foreach ($Path in @($DataRoot, $RtkCapture, $ImuCapture)) { if (-not (Test-Path -LiteralPath $Path)) { throw "Input does not exist: $Path" } } if ($Stride -lt 1) { throw "Stride must be at least 1" } if ($StationNames.Count -gt 0) { $Stations = @($StationNames | ForEach-Object { Get-Item -LiteralPath (Join-Path $DataRoot $_) }) } else { $Stations = @(Get-ChildItem -LiteralPath $DataRoot -Directory | Where-Object { (Test-Path -LiteralPath (Join-Path $_.FullName "dobject")) -and (Test-Path -LiteralPath (Join-Path $_.FullName "dobject_recording")) } | Sort-Object Name) } if ($Stations.Count -eq 0) { throw "No station directory containing dobject and dobject_recording was found" } New-Item -ItemType Directory -Force -Path $OutputRoot | Out-Null if (-not $SkipSerialParsing) { New-Item -ItemType Directory -Force -Path $ParsedRoot | Out-Null Run-Python "capture audit" @($Auditor, $RtkCapture, $ImuCapture, "--out", (Join-Path $OutputRoot "capture_audit.json")) Run-Python "RTK/IMU parse" @($Parser, "--rtk", $RtkCapture, "--imu", $ImuCapture, "--out", $ParsedRoot) } foreach ($Station in $Stations) { $StationOut = Join-Path $ExportRoot $Station.Name if (-not $SkipLidarExport) { Run-Python "LiDAR station $($Station.Name)" @( $Exporter, "--dlog", $Station.FullName, "--out", $StationOut, "--object", $LidarObject, "--format", "npz", "--timezone", $Timezone, "--stride", "$Stride", "--compress", "--skip-rtk", "--write-reports", "--resume" ) } if (-not (Test-Path -LiteralPath (Join-Path $StationOut "frames"))) { throw "Exported frame directory is absent for station $($Station.Name): $StationOut" } } $BuildArgs = @($Builder) foreach ($Station in $Stations) { $Frames = Join-Path (Join-Path $ExportRoot $Station.Name) "frames" $BuildArgs += @("--lidar", "$($Station.Name)=$Frames") } $BuildArgs += @( "--rtk", (Join-Path $ParsedRoot "rtk.jsonl"), "--imu", (Join-Path $ParsedRoot "imu.jsonl"), "--out", $CombinedRoot, "--rtk-max-dt-ms", "$RtkMaxDtMs", "--imu-before-ms", "$ImuBeforeMs", "--imu-after-ms", "$ImuAfterMs", "--overwrite" ) Run-Python "LiDAR/RTK/IMU association" $BuildArgs Write-Host "Completed stations: $($Stations.Count)" Write-Host "Combined NPZ: $CombinedRoot"