Files
SDR-Rover/docs/handoff/NEXT_RX_TASK.md
2026-09-22 17:43:58 +03:00

205 lines
7.8 KiB
Markdown

TASK_ID = RF001A_OTA_CAPTURE_004
TASK_TITLE = Start hardened RF-001A common-clock OTA capture
SOURCE_BRANCH = video-development
EXPECTED_HEAD = 168e03d9e778ab2ec63502552d385ec7be3ef677
REQUIRES_APPROVAL = YES
HARDWARE_ACCESS = YES
RF_RX = YES
RF_TX = NO
ALLOW_COMMIT = NO
ALLOW_PUSH = RX_RESULT_ONLY
RISK_LEVEL = YELLOW
---
# RF-001A RX capture 004
This file is the authoritative current task for the RX laptop. Read
`RX_NODE_STATE.md` first. If chat history conflicts with these files or the
current Git HEAD, stop and report the conflict.
This task is prepared only. A previous approval does not apply to this new
TASK_ID. Do not access HackRF until the user approves this exact YELLOW task.
## 1. Software and Git gates
1. Run `git pull --ff-only origin video-development`.
2. Require branch `video-development` and a clean tracked worktree.
3. Require local HEAD to equal `origin/video-development`; `EXPECTED_HEAD`
must be its ancestor.
4. Confirm the historical channel-test READY and COMPLETE acknowledgements
from `origin/tx-node` through the RX watcher.
5. Before opening HackRF, run the authenticated no-change publication check:
python -B tools/rx_agent_watcher.py --check-result-write --result-branch rx-node
Require `RX-NODE WRITE PREFLIGHT = PASS`. This command fetches the branch,
performs `git push --dry-run` of its existing HEAD to the same ref, and
confirms that the remote HEAD did not change. It creates no commit and no
production READY event.
6. Run the targeted tests, Ruff, Pyright and `git diff --check` according to
the tracked project instructions.
7. Validate `configs/rf/rf001a_truth_metadata.json`: waveform SHA256, seed,
PHY settings, 100 sequence numbers and 100 packet hashes must agree with
`configs/rf/rf001a_common_clock.json`.
If any gate fails, do not open HackRF and stop. In particular, a failed Git
write preflight blocks the hardware task.
## 2. Optional offline check of capture 003
If the existing `RF001A_OTA_CAPTURE_003` file is present locally, analyze it
read-only with the streaming cs8 decoder before starting new hardware work:
python -B -X utf8 tools/qpsk_rf_rx.py --iq-file <capture-003-path>\rf001a_ota_capture.cs8 --iq-format cs8 --truth-metadata configs/rf/rf001a_truth_metadata.json --report <new-path-outside-git>\capture003_streaming_report.json
No RF-001A transmission occurred during capture 003. The expected exact
packet count is 0. CRC-valid background candidates must not be reported as
exact unless their complete packet SHA256 matches the tracked truth metadata.
The analysis must use memmap/chunks and must not materialize the full 1.92 GB
capture as complex64.
If capture 003 is unavailable, report `CAPTURE_003 ANALYSIS = NOT_RUN` and
continue only after all mandatory gates pass.
## 3. Hardware preflight after YELLOW approval
1. Run `hackrf_info` and require RX serial
`000000000000000097c063dc2e5327d3`.
2. With `MASTER CLKOUT -> SLAVE CLKIN` connected, run:
hackrf_clock.exe -d 000000000000000097c063dc2e5327d3 -i
3. Require `CLKIN status: clock signal detected`.
4. Repeat the Git write preflight immediately before the finite RX command.
5. Require antennas connected, approximately 2 m distance, clear LOS, and
video disabled.
6. Require at least 1500000000 free bytes on the capture volume and record the
measured value.
Do not reconfigure clocks. If CLKIN or the second Git preflight fails, stop
before capture.
## 4. Finite RX capture
Use a unique directory outside Git. Never overwrite an earlier capture.
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
Use these fixed `hackrf_transfer.exe` arguments:
-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
Use `tools/capture_process_runner.ps1` as a controlled background process.
Preserve its complete stdout, stderr, start JSON and result JSON outside Git.
The runner must start exactly one finite child, record the actual child PID and
UTC start time, set the expected end to start plus 60 seconds, wait for that
exact process, record its integer exit code, size and duration, and calculate
SHA256 after the output file closes. Never retry the capture automatically.
Within five seconds require the runner and child to be alive and the file to
exist and grow. Otherwise publish no READY and stop.
## 5. READY publication
Create the strict schema-version-1 READY JSON outside Git for TASK_ID
`RF001A_OTA_CAPTURE_004`. Use the actual child PID and UTC timestamps,
`expected_capture_end = capture_started_at + 60 s`, current source HEAD,
`common_clock_verified = true`, `rf_tx = 0`, and null final result fields.
Publish it once with:
python -B tools/rx_agent_watcher.py --publish-result <ready-json> --result-branch rx-node
The publisher creates one immutable READY commit. On a recognized transient
network or authentication failure it checks the remote branch and may retry
the exact same commit once. It never creates a second READY commit or changes
the payload between attempts. If the remote already contains that commit, the
publication is successful. A second failure means:
READY publication = FAIL
RX READY FOR TX = NO
RF TX = 0
Do not publish READY after the capture window has ended. After the process
exits, publish only a `CAPTURE_COMPLETE` result with the actual exit code,
size, duration and error counters, including nonzero failure values.
After the remote HEAD confirms the READY commit, immediately print:
TASK_ID = RF001A_OTA_CAPTURE_004
RX CAPTURE STARTED = YES
RX READY FOR TX = YES
CAPTURE PID =
CAPTURE PATH =
CAPTURE START TIME =
EXPECTED CAPTURE END =
RX RESULT HEAD =
RF TX = 0
Return control without waiting for capture completion.
## 6. Completion and offline decode
After the child exits, record exact exit code, actual size, duration, overruns,
USB errors, file-write errors and SHA256. A complete capture is exactly
960000000 bytes; a smaller file is incomplete. Run the CLKIN check again and
require detected status.
Publish an honest schema-version-1 COMPLETE result with final values. Keep IQ,
stdout, stderr and large logs outside Git.
After `origin/tx-node` contains `TX_STARTED` and `TX_COMPLETE` for
`RF001A_OTA_TX_004`, run the streaming decoder with RX/TX timestamps:
python -B -X utf8 tools/qpsk_rf_rx.py --iq-file <capture-path>\rf001a_ota_capture.cs8 --iq-format cs8 --truth-metadata configs/rf/rf001a_truth_metadata.json --capture-started-at <CAPTURE_STARTED_AT> --tx-started-at <TX_STARTED_AT> --tx-finished-at <TX_FINISHED_AT> --report <new-path-outside-git>\rf001a_streaming_report.json
The decoder uses a two-second margin before and after the timestamped burst.
Without complete timestamps it uses the streaming power locator. Chunk overlap
is derived from the real 256-byte RF-001 waveform and duplicate detections from
overlap are suppressed.
## 7. Prohibited actions
RF TX, firmware update, CPLD update, Zadig, USB driver changes, clock
reconfiguration, commit or push to `video-development`, repeated capture, raw
IQ/video/waveform publication and large-log publication are forbidden.
Final report:
TASK_ID = RF001A_OTA_CAPTURE_004
RX-NODE WRITE PREFLIGHT = PASS / FAIL
COMMON CLOCK = VERIFIED / NOT_VERIFIED
RX CAPTURE COMPLETE = YES / NO
CAPTURE SIZE =
CAPTURE DURATION =
RX OVERRUNS =
USB ERRORS =
FILE WRITE ERRORS =
RAW IQ SHA256 =
STREAMING DECODE = PASS / FAIL / NOT_RUN
EXACT RF001A PACKETS =
PEAK ANALYSIS MEMORY =
RF TX = 0
STOP.