Add bidirectional RF handoff coordination
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
TASK_ID = RF001A_OTA_CAPTURE_001
|
||||
TASK_TITLE = Capture first RF-001A QPSK OTA transmission
|
||||
TASK_ID = RF001A_OTA_CAPTURE_002
|
||||
TASK_TITLE = Start coordinated RF-001A common-clock OTA capture
|
||||
SOURCE_BRANCH = video-development
|
||||
EXPECTED_HEAD = 1e3aa25d7007c7c8e84608cddb02f06f77e63396
|
||||
EXPECTED_HEAD = 34f5ace54540f947311ada7e81a463c08abfd2d3
|
||||
REQUIRES_APPROVAL = YES
|
||||
HARDWARE_ACCESS = YES
|
||||
RF_RX = YES
|
||||
@@ -17,369 +17,263 @@ If chat history conflicts with these files, the shared RF-001A spec or current G
|
||||
# Current RX task
|
||||
|
||||
TASK =
|
||||
CAPTURE FIRST RF-001A QPSK OTA TRANSMISSION
|
||||
START COORDINATED RF-001A COMMON-CLOCK OTA CAPTURE
|
||||
|
||||
This is a new YELLOW task. Do not reuse approval or completion state from any earlier task ID.
|
||||
This is a new YELLOW task. Do not reuse approval or completion state from RF001A_OTA_CAPTURE_001 or any earlier task.
|
||||
|
||||
## Accepted preflight evidence
|
||||
|
||||
The user supplied these completed preflight results:
|
||||
|
||||
```text
|
||||
COMMON CLOCK VERIFIED = YES
|
||||
CLKIN BEFORE SHORT RX = DETECTED
|
||||
SHORT RX = PASS, 65536 samples, overruns 0
|
||||
CLKIN AFTER SHORT RX = DETECTED
|
||||
CLKIN AFTER 20 s CAPTURE = DETECTED
|
||||
CAPTURE SIZE = 320000000 bytes
|
||||
CAPTURE DURATION = 20.00486 s
|
||||
RX OVERRUNS = 0
|
||||
USB ERRORS = 0
|
||||
FILE ERRORS = 0
|
||||
RAW IQ SHA256 = c0abdc2a41a0265a8037b4a5296d20b843eb75bcee83a6f2fac5813a533b7147
|
||||
RX HARDWARE PREFLIGHT = PASS WITH PROCESS-EXIT-CODE OBSERVABILITY LIMITATION
|
||||
```
|
||||
|
||||
The exact child process exit code was not retained. Do not repeat the 20-second preflight solely to recover that missing value. This task fixes exit-code observability for the new OTA capture.
|
||||
One approval for this task covers its read-only checks, bounded RF RX capture, and publication of the two small allowlisted result files to the existing rx-node branch. It never authorizes RF TX.
|
||||
|
||||
## 1. Preconditions
|
||||
|
||||
After one explicit `[A] Approve` for this task:
|
||||
After the user chooses [A] Approve for this exact TASK_ID:
|
||||
|
||||
1. Run `git pull --ff-only origin video-development`.
|
||||
2. Require branch `video-development`.
|
||||
3. Require `git status --short --untracked-files=no` to produce no output.
|
||||
4. Require local HEAD to equal `origin/video-development` and `EXPECTED_HEAD` to be its ancestor.
|
||||
5. Run `hackrf_info` and preserve complete stdout.
|
||||
6. Require RX serial `000000000000000097c063dc2e5327d3`.
|
||||
7. Run the official read-only clock check and preserve complete stdout:
|
||||
1. Run git pull --ff-only origin video-development.
|
||||
2. Require branch video-development.
|
||||
3. Require git status --short --untracked-files=no to produce no output.
|
||||
4. Require local HEAD to equal origin/video-development and EXPECTED_HEAD to be its ancestor.
|
||||
5. Run hackrf_info and preserve complete stdout.
|
||||
6. Require RX serial 000000000000000097c063dc2e5327d3.
|
||||
7. Confirm that configs/rf/rf001a_common_clock.json exists and matches the fixed RF-001A frequency, sample rate, modulation and common-clock plan.
|
||||
8. With MASTER CLKOUT physically connected to SLAVE CLKIN, run:
|
||||
|
||||
```powershell
|
||||
& '<verified hackrf_clock.exe path>' -d 000000000000000097c063dc2e5327d3 -i
|
||||
```
|
||||
hackrf_clock.exe -d 000000000000000097c063dc2e5327d3 -i
|
||||
|
||||
Require the exact line `CLKIN status: clock signal detected`. If it is absent, do not start the capture; keep `RF TX = 0` and STOP. Do not reconfigure the clock.
|
||||
9. Preserve complete stdout and require the exact line:
|
||||
|
||||
Create a new unique capture directory outside the Git checkout. Refuse any existing directory and never overwrite an earlier capture. Before starting, require at least `1100000000` free bytes on the target volume and record the measured free-byte count.
|
||||
CLKIN status: clock signal detected
|
||||
|
||||
## 2. Authorized finite RX capture
|
||||
10. Require the remote branch origin/rx-node to exist. Do not create it in this task.
|
||||
11. Create a unique capture directory outside Git. Refuse an existing directory and never overwrite an earlier capture.
|
||||
12. Require at least 2500000000 free bytes on the target volume. Record the measured free-byte count.
|
||||
|
||||
Only this capture is authorized:
|
||||
If any condition fails, do not start capture. Report the failed condition, keep RF TX = 0, and STOP.
|
||||
|
||||
```text
|
||||
RX serial = 000000000000000097c063dc2e5327d3
|
||||
center frequency = 1400000000 Hz
|
||||
sample rate = 8000000 samples/s
|
||||
RX LNA = 24 dB
|
||||
RX VGA = 24 dB
|
||||
RF amp = OFF
|
||||
antenna bias = OFF
|
||||
duration = 60 s
|
||||
sample count = 480000000 complex samples
|
||||
hardware format = cs8
|
||||
expected file size = 960000000 bytes
|
||||
common clock = DETECTED
|
||||
```
|
||||
## 2. Authorized bounded RX capture
|
||||
|
||||
The required `hackrf_transfer.exe` arguments are:
|
||||
Only this finite receive operation is authorized:
|
||||
|
||||
```text
|
||||
-d 000000000000000097c063dc2e5327d3
|
||||
-r <unique_capture_path>\rf001a_ota_capture.cs8
|
||||
-f 1400000000
|
||||
-s 8000000
|
||||
-n 480000000
|
||||
-l 24
|
||||
-g 24
|
||||
-a 0
|
||||
-p 0
|
||||
-B
|
||||
```
|
||||
RX serial = 000000000000000097c063dc2e5327d3
|
||||
center frequency = 1400000000 Hz
|
||||
sample rate = 8000000 samples/s
|
||||
RX LNA = 24 dB
|
||||
RX VGA = 24 dB
|
||||
RF amp = OFF
|
||||
antenna bias = OFF
|
||||
duration = 120 s
|
||||
sample count = 960000000 complex samples
|
||||
hardware format = cs8
|
||||
expected file size = 1920000000 bytes
|
||||
common clock = DETECTED
|
||||
|
||||
## 3. Durable process and exit-code recording
|
||||
The hackrf_transfer.exe arguments are fixed:
|
||||
|
||||
Use a local runner outside Git. The runner owns the `hackrf_transfer` process, records its actual PID immediately, waits for that exact child, and writes the exact child `ExitCode` to an atomic result JSON file. This remains observable even if the assistant turn or PowerShell parent that started the runner ends.
|
||||
-d 000000000000000097c063dc2e5327d3
|
||||
-r <unique_capture_path>\rf001a_ota_capture.cs8
|
||||
-f 1400000000
|
||||
-s 8000000
|
||||
-n 960000000
|
||||
-l 24
|
||||
-g 24
|
||||
-a 0
|
||||
-p 0
|
||||
-B
|
||||
|
||||
Prepare paths:
|
||||
Do not add repeat mode. Do not change gain, clock, frequency or sample rate.
|
||||
|
||||
```powershell
|
||||
$runId = '{0}_{1}' -f (Get-Date -Format 'yyyyMMdd_HHmmss'), ([guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
$captureRoot = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'SDR_Rover_Captures'
|
||||
$captureDirectory = Join-Path $captureRoot "RF001A_OTA_CAPTURE_001_$runId"
|
||||
if (Test-Path -LiteralPath $captureDirectory) { throw "Capture directory already exists: $captureDirectory" }
|
||||
New-Item -ItemType Directory -Path $captureDirectory -ErrorAction Stop | Out-Null
|
||||
## 3. Background runner and durable evidence
|
||||
|
||||
$targetRoot = [System.IO.Path]::GetPathRoot([System.IO.Path]::GetFullPath($captureDirectory))
|
||||
$targetDrive = Get-PSDrive -Name $targetRoot.Substring(0, 1)
|
||||
if ($targetDrive.Free -lt 1100000000) { throw "Insufficient free space: $($targetDrive.Free) bytes" }
|
||||
Use a local runner outside Git. The runner must:
|
||||
|
||||
$capturePath = Join-Path $captureDirectory 'rf001a_ota_capture.cs8'
|
||||
$stdoutPath = Join-Path $captureDirectory 'hackrf_transfer.stdout.log'
|
||||
$stderrPath = Join-Path $captureDirectory 'hackrf_transfer.stderr.log'
|
||||
$startedStatusPath = Join-Path $captureDirectory 'capture_started.json'
|
||||
$resultStatusPath = Join-Path $captureDirectory 'capture_result.json'
|
||||
$runnerPath = Join-Path $captureDirectory 'capture_runner.ps1'
|
||||
$runnerStdoutPath = Join-Path $captureDirectory 'capture_runner.stdout.log'
|
||||
$runnerStderrPath = Join-Path $captureDirectory 'capture_runner.stderr.log'
|
||||
$hackrfTransfer = '<verified absolute path to hackrf_transfer.exe>'
|
||||
```
|
||||
- start exactly one hackrf_transfer child;
|
||||
- record its PID and start time immediately;
|
||||
- record the expected end time as start time plus 120 seconds;
|
||||
- redirect complete stdout and stderr to unique local files;
|
||||
- wait for that exact child;
|
||||
- record its exact exit code, finish time, duration and output size;
|
||||
- calculate SHA256 after the file is closed;
|
||||
- write started and final JSON atomically;
|
||||
- never start a second capture automatically.
|
||||
|
||||
Write this runner to `$runnerPath`:
|
||||
Start the runner as a controlled background process. Within five seconds, require all four facts:
|
||||
|
||||
```powershell
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$HackrfTransfer,
|
||||
[Parameter(Mandatory = $true)][string]$CapturePath,
|
||||
[Parameter(Mandatory = $true)][string]$StdoutPath,
|
||||
[Parameter(Mandatory = $true)][string]$StderrPath,
|
||||
[Parameter(Mandatory = $true)][string]$StartedStatusPath,
|
||||
[Parameter(Mandatory = $true)][string]$ResultStatusPath
|
||||
)
|
||||
runner process alive = YES
|
||||
hackrf_transfer child alive = YES
|
||||
capture file created = YES
|
||||
capture file growing = YES
|
||||
|
||||
function Write-JsonAtomically {
|
||||
param([Parameter(Mandatory = $true)][object]$Value,
|
||||
[Parameter(Mandatory = $true)][string]$Path)
|
||||
$temporaryPath = "$Path.tmp"
|
||||
$Value | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $temporaryPath -Encoding Ascii
|
||||
Move-Item -LiteralPath $temporaryPath -Destination $Path -Force
|
||||
}
|
||||
If any fact is false, publish no READY state. Preserve local evidence, report RX CAPTURE STARTED = NO, keep RF TX = 0, and STOP.
|
||||
|
||||
$arguments = @(
|
||||
'-d', '000000000000000097c063dc2e5327d3',
|
||||
'-r', $CapturePath,
|
||||
'-f', '1400000000',
|
||||
'-s', '8000000',
|
||||
'-n', '480000000',
|
||||
'-l', '24',
|
||||
'-g', '24',
|
||||
'-a', '0',
|
||||
'-p', '0',
|
||||
'-B'
|
||||
)
|
||||
## 4. Publish READY immediately
|
||||
|
||||
$startedAt = [DateTimeOffset]::Now
|
||||
$transferProcess = $null
|
||||
$exitCode = $null
|
||||
$runnerError = $null
|
||||
As soon as the child is alive and the file is growing, build a local JSON file outside Git with every field below:
|
||||
|
||||
try {
|
||||
$transferProcess = Start-Process -FilePath $HackrfTransfer -ArgumentList $arguments `
|
||||
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath `
|
||||
-PassThru -WindowStyle Hidden
|
||||
|
||||
Write-JsonAtomically -Path $StartedStatusPath -Value ([ordered]@{
|
||||
state = 'RUNNING'
|
||||
capture_pid = $transferProcess.Id
|
||||
started_at = $startedAt.ToString('o')
|
||||
capture_path = $CapturePath
|
||||
})
|
||||
|
||||
$transferProcess.WaitForExit()
|
||||
$transferProcess.Refresh()
|
||||
$exitCode = $transferProcess.ExitCode
|
||||
} catch {
|
||||
$runnerError = $_.Exception.ToString()
|
||||
} finally {
|
||||
$finishedAt = [DateTimeOffset]::Now
|
||||
$captureSize = if (Test-Path -LiteralPath $CapturePath) {
|
||||
(Get-Item -LiteralPath $CapturePath).Length
|
||||
} else {
|
||||
0
|
||||
{
|
||||
"schema_version": 1,
|
||||
"task_id": "RF001A_OTA_CAPTURE_002",
|
||||
"state": "RX_READY_FOR_TX",
|
||||
"rx_ready_for_tx": true,
|
||||
"process_alive": true,
|
||||
"file_growing": true,
|
||||
"capture_pid": <actual positive child PID>,
|
||||
"published_at": "<current UTC ISO-8601 time>",
|
||||
"capture_started_at": "<actual UTC ISO-8601 start time>",
|
||||
"expected_capture_end": "<start time plus 120 seconds>",
|
||||
"capture_path": "<absolute local capture path>",
|
||||
"common_clock_verified": true,
|
||||
"source_head": "<current local 40-character Git HEAD>",
|
||||
"rf_tx": 0,
|
||||
"exit_code": null,
|
||||
"file_size": null,
|
||||
"duration_seconds": null,
|
||||
"overruns": null,
|
||||
"usb_errors": null,
|
||||
"file_errors": null,
|
||||
"sha256": null,
|
||||
"clkin_after": "NOT_RUN",
|
||||
"rf_tx_observed": "UNKNOWN"
|
||||
}
|
||||
|
||||
$sha256 = $null
|
||||
try {
|
||||
if (Test-Path -LiteralPath $CapturePath) {
|
||||
$sha256 = (Get-FileHash -LiteralPath $CapturePath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
} catch {
|
||||
$errorParts = @($runnerError, $_.Exception.ToString()) | Where-Object { $null -ne $_ -and $_ -ne '' }
|
||||
$runnerError = $errorParts -join [Environment]::NewLine
|
||||
}
|
||||
Publish it with the approved RX watcher state:
|
||||
|
||||
Write-JsonAtomically -Path $ResultStatusPath -Value ([ordered]@{
|
||||
state = 'COMPLETE'
|
||||
capture_pid = if ($null -ne $transferProcess) { $transferProcess.Id } else { $null }
|
||||
started_at = $startedAt.ToString('o')
|
||||
finished_at = $finishedAt.ToString('o')
|
||||
duration_seconds = ($finishedAt - $startedAt).TotalSeconds
|
||||
exit_code = $exitCode
|
||||
capture_path = $CapturePath
|
||||
capture_size_bytes = $captureSize
|
||||
sha256 = $sha256
|
||||
runner_error = $runnerError
|
||||
})
|
||||
}
|
||||
python -B tools/rx_agent_watcher.py --publish-result <ready-json-path> --result-branch rx-node
|
||||
|
||||
if ($null -eq $exitCode) { exit 255 }
|
||||
exit $exitCode
|
||||
```
|
||||
The publisher may change only:
|
||||
|
||||
Start the runner as a controlled background process. Quote every path passed through `-ArgumentList`:
|
||||
docs/handoff/results/RX_RESULT.json
|
||||
docs/handoff/results/RX_RESULT.md
|
||||
|
||||
```powershell
|
||||
$currentPowerShell = (Get-Process -Id $PID).Path
|
||||
$runnerArguments = @(
|
||||
'-NoLogo',
|
||||
'-NoProfile',
|
||||
'-NonInteractive',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-File', ('"{0}"' -f $runnerPath),
|
||||
'-HackrfTransfer', ('"{0}"' -f $hackrfTransfer),
|
||||
'-CapturePath', ('"{0}"' -f $capturePath),
|
||||
'-StdoutPath', ('"{0}"' -f $stdoutPath),
|
||||
'-StderrPath', ('"{0}"' -f $stderrPath),
|
||||
'-StartedStatusPath', ('"{0}"' -f $startedStatusPath),
|
||||
'-ResultStatusPath', ('"{0}"' -f $resultStatusPath)
|
||||
)
|
||||
$runnerProcess = Start-Process -FilePath $currentPowerShell -ArgumentList $runnerArguments `
|
||||
-RedirectStandardOutput $runnerStdoutPath -RedirectStandardError $runnerStderrPath `
|
||||
-PassThru -WindowStyle Hidden
|
||||
```
|
||||
It uses a temporary checkout of the existing rx-node branch. It must not switch the source checkout, push video-development, or add raw IQ, video, waveform or large logs.
|
||||
|
||||
Wait only long enough to prove that the child process is alive and the file is growing:
|
||||
If publication fails, show the complete error. Do not claim RX READY FOR TX and do not trigger TX automatically. The finite capture may finish normally, but RF TX remains 0.
|
||||
|
||||
```powershell
|
||||
$startDeadline = (Get-Date).AddSeconds(5)
|
||||
$startedStatus = $null
|
||||
$captureBytes = 0
|
||||
$captureAlive = $false
|
||||
After successful publication, immediately return control to the user without waiting for capture completion:
|
||||
|
||||
do {
|
||||
Start-Sleep -Milliseconds 100
|
||||
$runnerProcess.Refresh()
|
||||
if (Test-Path -LiteralPath $startedStatusPath) {
|
||||
$startedStatus = Get-Content -Raw -LiteralPath $startedStatusPath | ConvertFrom-Json
|
||||
$captureProcess = Get-Process -Id ([int]$startedStatus.capture_pid) -ErrorAction SilentlyContinue
|
||||
$captureAlive = $null -ne $captureProcess
|
||||
}
|
||||
if (Test-Path -LiteralPath $capturePath) {
|
||||
$captureBytes = (Get-Item -LiteralPath $capturePath).Length
|
||||
}
|
||||
} until ($runnerProcess.HasExited -or ($captureAlive -and $captureBytes -gt 0) -or (Get-Date) -ge $startDeadline)
|
||||
```
|
||||
TASK_ID =
|
||||
RF001A_OTA_CAPTURE_002
|
||||
|
||||
The start passes only when:
|
||||
RX CAPTURE STARTED =
|
||||
YES
|
||||
|
||||
```text
|
||||
runner process alive = YES
|
||||
hackrf_transfer child alive = YES
|
||||
capture file created = YES
|
||||
capture file growing = YES
|
||||
```
|
||||
RX READY FOR TX =
|
||||
YES
|
||||
|
||||
If any check fails, report `RX CAPTURE STARTED = NO`, preserve all local files, keep `RF TX = 0`, and STOP.
|
||||
CAPTURE PID =
|
||||
|
||||
## 4. Immediate READY report
|
||||
CAPTURE PATH =
|
||||
|
||||
When all start checks pass, immediately return control to the user with:
|
||||
CAPTURE START TIME =
|
||||
|
||||
```text
|
||||
TASK_ID =
|
||||
RF001A_OTA_CAPTURE_001
|
||||
EXPECTED CAPTURE END =
|
||||
|
||||
RX CAPTURE STARTED =
|
||||
YES
|
||||
RX RESULT PUBLISH =
|
||||
PASS
|
||||
|
||||
RX READY FOR TX =
|
||||
YES
|
||||
RF TX =
|
||||
0
|
||||
|
||||
CAPTURE PID =
|
||||
<actual hackrf_transfer child PID>
|
||||
The READY state describes a live receiver only. It does not authorize RF TX on either computer.
|
||||
|
||||
CAPTURE PATH =
|
||||
<absolute path>
|
||||
## 5. Capture completion
|
||||
|
||||
CAPTURE START TIME =
|
||||
<started_status started_at>
|
||||
After the child exits, read the durable final status. Preserve all local logs and inspect every line for RX overruns, USB errors and file write errors. Do not infer zero when a tool did not report a counter; use JSON null and report NOT_REPORTED.
|
||||
|
||||
EXPECTED END TIME =
|
||||
<started_at plus 60 seconds>
|
||||
Run the read-only clock check again:
|
||||
|
||||
RF TX =
|
||||
0
|
||||
```
|
||||
hackrf_clock.exe -d 000000000000000097c063dc2e5327d3 -i
|
||||
|
||||
Do not wait for the 60-second capture to finish before sending `RX READY FOR TX = YES`. This status means only that RX is actively recording. It does not authorize RF TX on the RX laptop or MAIN PC.
|
||||
Require CLKIN to remain detected. The expected complete capture has:
|
||||
|
||||
## 5. Completion evidence
|
||||
exit code = 0
|
||||
file size = 1920000000 bytes
|
||||
duration = consistent with 120 s
|
||||
RX overruns = 0 or NOT_REPORTED
|
||||
USB errors = 0 or NOT_REPORTED
|
||||
file write errors = 0 or NOT_REPORTED
|
||||
CLKIN after capture = DETECTED
|
||||
|
||||
After the capture window, read `capture_result.json`. The exact child process exit code must be present. Also preserve complete stdout, stderr and runner logs.
|
||||
A shorter file is incomplete. Any reported nonzero error remains evidence. Do not retry automatically.
|
||||
|
||||
Require and report:
|
||||
## 6. Publish final result
|
||||
|
||||
- process exit code;
|
||||
- actual capture size;
|
||||
- actual duration;
|
||||
- SHA256;
|
||||
- every RX overrun counter or event from the complete logs;
|
||||
- every USB error;
|
||||
- every file write error;
|
||||
- CLKIN status after capture.
|
||||
Build a second local JSON document with the same identity and timing fields. Set:
|
||||
|
||||
Run the read-only clock check again. Require `CLKIN status: clock signal detected`.
|
||||
state = CAPTURE_COMPLETE
|
||||
rx_ready_for_tx = false
|
||||
process_alive = false
|
||||
file_growing = false
|
||||
exit_code = <exact integer>
|
||||
file_size = <actual integer bytes>
|
||||
duration_seconds = <actual number>
|
||||
overruns = <integer or null when not reported>
|
||||
usb_errors = <integer or null when not reported>
|
||||
file_errors = <integer or null when not reported>
|
||||
sha256 = <lowercase 64-character SHA256>
|
||||
clkin_after = DETECTED or NOT_DETECTED
|
||||
rf_tx_observed = EXPECTED_EXTERNAL_TX or UNKNOWN
|
||||
published_at = <current UTC ISO-8601 time>
|
||||
rf_tx = 0
|
||||
|
||||
The capture is complete only when:
|
||||
Use EXPECTED_EXTERNAL_TX only when the coordinated MAIN TX event is known to have occurred inside this capture window. Otherwise use UNKNOWN.
|
||||
|
||||
```text
|
||||
exit code = 0
|
||||
capture size = 960000000 bytes
|
||||
duration = consistent with 60 s
|
||||
RX overruns = 0
|
||||
USB errors = 0
|
||||
file write errors = 0
|
||||
CLKIN after capture = DETECTED
|
||||
```
|
||||
Publish with:
|
||||
|
||||
Do not infer zero from a missing counter. Use `NOT_REPORTED` when the tool did not report a value. Treat every nonzero intermediate or final overrun/error line as evidence; do not keep only the last counter. Do not retry automatically.
|
||||
python -B tools/rx_agent_watcher.py --publish-result <final-json-path> --result-branch rx-node
|
||||
|
||||
## 6. Offline decode
|
||||
If the push fails, preserve the local JSON and logs, report the error, and do not claim publication success.
|
||||
|
||||
Use the saved OTA IQ without changing the receiver for this capture:
|
||||
## 7. Offline decode
|
||||
|
||||
```powershell
|
||||
python -B -X utf8 tools/qpsk_rf_rx.py `
|
||||
--iq-file '<absolute capture path>' `
|
||||
--iq-format cs8 `
|
||||
--truth-metadata data/raw/rf001/rf001a_common_clock_001/tx_metadata.json `
|
||||
--report '<new unique path outside Git>\rf001a_ota_decode.json'
|
||||
```
|
||||
Do not start automatic decode unless synchronized truth metadata for this exact transmitted waveform is available and consistent. If it is absent, preserve the IQ file and report:
|
||||
|
||||
Require the existing truth metadata for the transmitted waveform. If it is absent or inconsistent, preserve the IQ file and report `OFFLINE DECODE = BLOCKED_BY_TRUTH_METADATA`; do not alter the receiver or invent results.
|
||||
OFFLINE DECODE = BLOCKED_BY_TRUTH_METADATA
|
||||
|
||||
Report at minimum:
|
||||
Do not modify the receiver or invent results.
|
||||
|
||||
```text
|
||||
DETECTED PREAMBLES =
|
||||
CRC VALID PACKETS =
|
||||
EXACT PACKETS =
|
||||
MISSING PACKETS =
|
||||
DUPLICATES =
|
||||
OUT OF ORDER =
|
||||
RAW BER =
|
||||
RAW BER SCOPE =
|
||||
POST-FEC ERROR RATE =
|
||||
CFO MEDIAN =
|
||||
CFO P95 =
|
||||
TIMING OFFSET =
|
||||
TIMING DRIFT =
|
||||
RX CLIPPING =
|
||||
```
|
||||
## 8. Prohibited actions
|
||||
|
||||
Use the existing receiver JSON fields directly. Derive `OUT OF ORDER` only from `packet_sequences`. Derive timing summaries only from `phase_and_timing_estimates`; if the available data does not support a value, report `NOT_MEASURED`. Do not fabricate a metric.
|
||||
On the RX laptop, do not perform:
|
||||
|
||||
Classify this first smoke test:
|
||||
- RF TX;
|
||||
- continuous RX;
|
||||
- automatic capture retry;
|
||||
- firmware or CPLD update;
|
||||
- hackrf_spiflash;
|
||||
- Zadig or USB driver changes;
|
||||
- clock reconfiguration;
|
||||
- commit or push to video-development;
|
||||
- creation of rx-node;
|
||||
- publication of raw IQ, video, waveform or large logs.
|
||||
|
||||
```text
|
||||
99-100 exact = GOOD
|
||||
95-98 exact = PARTIAL / DIAGNOSTIC
|
||||
<95 exact = FAIL FOR VIDEO PROGRESSION
|
||||
```
|
||||
## 9. Final RX report
|
||||
|
||||
One run is not statistical reliability evidence.
|
||||
TASK_ID =
|
||||
RF001A_OTA_CAPTURE_002
|
||||
|
||||
## 7. Prohibited actions
|
||||
RX CAPTURE COMPLETE =
|
||||
|
||||
On the RX laptop, do not perform RF TX, firmware or CPLD updates, Zadig operations, USB driver changes, clock reconfiguration, continuous RX, automatic capture retries, commits to `video-development`, or pushes to `video-development`.
|
||||
EXIT CODE =
|
||||
|
||||
`ALLOW_PUSH = RX_RESULT_ONLY` permits only a separately approved small result publication under the handoff protocol. Raw IQ, large logs and local runner files remain outside Git.
|
||||
CAPTURE SIZE =
|
||||
|
||||
CAPTURE DURATION =
|
||||
|
||||
RX OVERRUNS =
|
||||
|
||||
USB ERRORS =
|
||||
|
||||
FILE WRITE ERRORS =
|
||||
|
||||
RAW IQ SHA256 =
|
||||
|
||||
CLKIN AFTER =
|
||||
|
||||
RF TX OBSERVED =
|
||||
|
||||
FINAL RX RESULT PUBLISH =
|
||||
|
||||
OFFLINE DECODE =
|
||||
|
||||
RF TX =
|
||||
0
|
||||
|
||||
STOP.
|
||||
|
||||
Reference in New Issue
Block a user