Harden RF handoff and stream OTA decode
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
TASK_ID = RF001A_OTA_CAPTURE_003
|
||||
TASK_TITLE = Start coordinated RF-001A common-clock OTA capture
|
||||
TASK_ID = RF001A_OTA_CAPTURE_004
|
||||
TASK_TITLE = Start hardened RF-001A common-clock OTA capture
|
||||
SOURCE_BRANCH = video-development
|
||||
EXPECTED_HEAD = bc55d0516dc1ee36cefa1ff096e7d86f60645303
|
||||
EXPECTED_HEAD = 168e03d9e778ab2ec63502552d385ec7be3ef677
|
||||
REQUIRES_APPROVAL = YES
|
||||
HARDWARE_ACCESS = YES
|
||||
RF_RX = YES
|
||||
@@ -10,70 +10,79 @@ ALLOW_COMMIT = NO
|
||||
ALLOW_PUSH = RX_RESULT_ONLY
|
||||
RISK_LEVEL = YELLOW
|
||||
|
||||
This file is the authoritative current task for the RX laptop.
|
||||
The RX assistant must read RX_NODE_STATE.md and RF_HANDOFF_CHANNEL_TEST.md first, then execute this file.
|
||||
If chat history conflicts with these files, the shared RF-001A spec or current Git HEAD, stop and report the conflict.
|
||||
---
|
||||
|
||||
# Current RX task
|
||||
# RF-001A RX capture 004
|
||||
|
||||
TASK =
|
||||
START COORDINATED RF-001A COMMON-CLOCK OTA CAPTURE
|
||||
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 is a new YELLOW task. Do not reuse approval or completion state from RF001A_OTA_CAPTURE_001, RF001A_OTA_CAPTURE_002 or any earlier task.
|
||||
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.
|
||||
|
||||
One approval for this task covers the read-only bidirectional channel check, other read-only checks, one 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. Software and Git gates
|
||||
|
||||
The preceding RF001A_OTA_CAPTURE_002 produced a real 120.1413067 s, 1920000000-byte capture with CLKIN detected before and after, zero reported overruns and SHA256 99a3e7cb51d3ad4e3b914b63da0695ed6658aa169110c32659aba995b542fa0c. Its READY publication failed, its exact child exit code was not recorded and no synchronized truth metadata was available. Treat that as historical evidence only.
|
||||
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:
|
||||
|
||||
## 1. Bidirectional handoff channel gate
|
||||
python -B tools/rx_agent_watcher.py --check-result-write --result-branch rx-node
|
||||
|
||||
Before offering this task for YELLOW approval, the RX watcher must fetch origin/rx-node and origin/tx-node and scan the acknowledgement history from oldest to newest. It must confirm both exact records:
|
||||
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`.
|
||||
|
||||
STATE = CHANNEL_READY_OBSERVED
|
||||
SOURCE_RX_COMMIT = 4cb6c8cad1ab5194755c2740c2a5a83933d099d8
|
||||
SOURCE_RX_DIGEST = 23fc54f1f788f068fd4a842daf0a445bd4ec192866b4d89406c74f1797a9952b
|
||||
If any gate fails, do not open HackRF and stop. In particular, a failed Git
|
||||
write preflight blocks the hardware task.
|
||||
|
||||
and:
|
||||
## 2. Optional offline check of capture 003
|
||||
|
||||
STATE = CHANNEL_COMPLETE_OBSERVED
|
||||
SOURCE_RX_COMMIT = 83d91ff5309777ab639a24f774e9d0b8825a11e9
|
||||
SOURCE_RX_DIGEST = 7d98ed2e70e98a087b968c0d65fb1db4277b81f251e075d20583de2e5002b2d6
|
||||
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:
|
||||
|
||||
Both acknowledgements must use TASK_ID RF_HANDOFF_CHANNEL_TEST_001, the original shared source HEAD and RF_TX = 0. Required watcher output:
|
||||
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
|
||||
|
||||
MAIN READY RECEIVED = CONFIRMED
|
||||
MAIN COMPLETE RECEIVED = CONFIRMED
|
||||
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.
|
||||
|
||||
Do not rerun the channel test. If origin/tx-node is missing or either exact acknowledgement is absent or invalid, do not access HackRF, do not start capture and STOP with RF TX = 0.
|
||||
If capture 003 is unavailable, report `CAPTURE_003 ANALYSIS = NOT_RUN` and
|
||||
continue only after all mandatory gates pass.
|
||||
|
||||
## 2. Preconditions
|
||||
## 3. Hardware preflight after YELLOW approval
|
||||
|
||||
After the same [A] approval for this exact TASK_ID and after the channel gate passes:
|
||||
1. Run `hackrf_info` and require RX serial
|
||||
`000000000000000097c063dc2e5327d3`.
|
||||
2. With `MASTER CLKOUT -> SLAVE CLKIN` connected, run:
|
||||
|
||||
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 and configs/rf/rf001a_truth_metadata.json exist. Require the tracked truth metadata test ID, seed, 100 packet hashes, sequence numbers, waveform SHA256 and PHY settings to agree with the shared RF-001A spec.
|
||||
8. With MASTER CLKOUT physically connected to SLAVE CLKIN, run:
|
||||
hackrf_clock.exe -d 000000000000000097c063dc2e5327d3 -i
|
||||
|
||||
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.
|
||||
|
||||
9. Preserve complete stdout and require the exact line:
|
||||
Do not reconfigure clocks. If CLKIN or the second Git preflight fails, stop
|
||||
before capture.
|
||||
|
||||
CLKIN status: clock signal detected
|
||||
## 4. Finite RX capture
|
||||
|
||||
10. Require the remote branch origin/rx-node to exist through both git fetch origin rx-node and git ls-remote --heads origin rx-node. 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.
|
||||
|
||||
If any condition fails, do not start capture. Report the failed condition, keep RF TX = 0, and STOP.
|
||||
|
||||
## 3. Authorized bounded RX capture
|
||||
|
||||
Only this finite receive operation is authorized:
|
||||
Use a unique directory outside Git. Never overwrite an earlier capture.
|
||||
|
||||
RX serial = 000000000000000097c063dc2e5327d3
|
||||
center frequency = 1400000000 Hz
|
||||
@@ -82,239 +91,114 @@ Only this finite receive operation is authorized:
|
||||
RX VGA = 24 dB
|
||||
RF amp = OFF
|
||||
antenna bias = OFF
|
||||
duration = 120 s
|
||||
sample count = 960000000 complex samples
|
||||
duration = 60 s
|
||||
sample count = 480000000 complex samples
|
||||
hardware format = cs8
|
||||
expected file size = 1920000000 bytes
|
||||
common clock = DETECTED
|
||||
expected file size = 960000000 bytes
|
||||
|
||||
The hackrf_transfer.exe arguments are fixed:
|
||||
Use these fixed `hackrf_transfer.exe` arguments:
|
||||
|
||||
-d 000000000000000097c063dc2e5327d3
|
||||
-r <unique_capture_path>\rf001a_ota_capture.cs8
|
||||
-f 1400000000
|
||||
-s 8000000
|
||||
-n 960000000
|
||||
-n 480000000
|
||||
-l 24
|
||||
-g 24
|
||||
-a 0
|
||||
-p 0
|
||||
-B
|
||||
|
||||
Do not add repeat mode. Do not change gain, clock, frequency or sample rate.
|
||||
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.
|
||||
|
||||
## 4. Background runner and durable evidence
|
||||
Within five seconds require the runner and child to be alive and the file to
|
||||
exist and grow. Otherwise publish no READY and stop.
|
||||
|
||||
Use the tracked tools/capture_process_runner.ps1. Write the exact hackrf_transfer argument array to a unique local JSON file outside Git. Start the runner itself as a controlled background PowerShell process and pass unique paths for the capture, complete stdout, complete stderr, capture_started.json and capture_result.json. The runner must:
|
||||
## 5. READY publication
|
||||
|
||||
- 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, refresh its process object and read ExitCode only after WaitForExit;
|
||||
- record its exact integer 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.
|
||||
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.
|
||||
|
||||
Start the runner as a controlled background process. Within five seconds, require all four facts:
|
||||
Publish it once with:
|
||||
|
||||
runner process alive = YES
|
||||
hackrf_transfer child alive = YES
|
||||
capture file created = YES
|
||||
capture file growing = YES
|
||||
python -B tools/rx_agent_watcher.py --publish-result <ready-json> --result-branch rx-node
|
||||
|
||||
If any fact is false, publish no READY state. Preserve local evidence, report RX CAPTURE STARTED = NO, keep RF TX = 0, and STOP.
|
||||
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:
|
||||
|
||||
## 5. Publish READY immediately
|
||||
READY publication = FAIL
|
||||
RX READY FOR TX = NO
|
||||
RF TX = 0
|
||||
|
||||
As soon as the child is alive and the file is growing, build a local JSON file outside Git with every field below:
|
||||
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.
|
||||
|
||||
{
|
||||
"schema_version": 1,
|
||||
"task_id": "RF001A_OTA_CAPTURE_003",
|
||||
"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"
|
||||
}
|
||||
|
||||
Publish it with the approved RX watcher state:
|
||||
|
||||
python -B tools/rx_agent_watcher.py --publish-result <ready-json-path> --result-branch rx-node
|
||||
|
||||
The publisher may change only:
|
||||
|
||||
docs/handoff/results/RX_RESULT.json
|
||||
docs/handoff/results/RX_RESULT.md
|
||||
|
||||
The publisher checks origin/rx-node through the real origin of the main checkout. It creates a temporary Git worktree from that repository, stages exactly the two allowlisted result paths, pushes HEAD:rx-node, confirms the new remote HEAD with git ls-remote and removes the worktree. It must not switch the source checkout, push video-development, or add raw IQ, video, waveform or large logs.
|
||||
|
||||
Use this order without exception: verify CLKIN, start capture, verify the child is alive, verify the file is growing, publish READY, verify RX RESULT REMOTE HEAD equals the published commit, and only then report RX READY FOR TX = YES.
|
||||
|
||||
If publication or remote confirmation fails, show the complete error and report RX READY FOR TX = NO. The finite capture may finish normally, but TX coordination does not start and RF TX remains 0.
|
||||
|
||||
After successful publication, immediately return control to the user without waiting for capture completion:
|
||||
|
||||
TASK_ID =
|
||||
RF001A_OTA_CAPTURE_003
|
||||
|
||||
RX CAPTURE STARTED =
|
||||
YES
|
||||
|
||||
RX READY FOR TX =
|
||||
YES
|
||||
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
|
||||
|
||||
RX RESULT PUBLISH =
|
||||
PASS
|
||||
Return control without waiting for capture completion.
|
||||
|
||||
RX RESULT REMOTE HEAD =
|
||||
## 6. Completion and offline decode
|
||||
|
||||
RF TX =
|
||||
0
|
||||
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.
|
||||
|
||||
The READY state describes a live receiver only. It does not authorize RF TX on either computer.
|
||||
Publish an honest schema-version-1 COMPLETE result with final values. Keep IQ,
|
||||
stdout, stderr and large logs outside Git.
|
||||
|
||||
## 6. Capture completion
|
||||
After `origin/tx-node` contains `TX_STARTED` and `TX_COMPLETE` for
|
||||
`RF001A_OTA_TX_004`, run the streaming decoder with RX/TX timestamps:
|
||||
|
||||
After the child exits, read capture_result.json and require an integer exit_code recorded by the runner. 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.
|
||||
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
|
||||
|
||||
Run the read-only clock check again:
|
||||
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.
|
||||
|
||||
hackrf_clock.exe -d 000000000000000097c063dc2e5327d3 -i
|
||||
## 7. Prohibited actions
|
||||
|
||||
Require CLKIN to remain detected. The expected complete capture has:
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
A shorter file is incomplete. Any reported nonzero error remains evidence. Do not retry automatically.
|
||||
|
||||
## 7. Publish final result
|
||||
|
||||
Build a second local JSON document with the same identity and timing fields. Set:
|
||||
|
||||
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
|
||||
|
||||
Use EXPECTED_EXTERNAL_TX only when the coordinated MAIN TX event is known to have occurred inside this capture window. Otherwise use UNKNOWN.
|
||||
|
||||
Publish with:
|
||||
|
||||
python -B tools/rx_agent_watcher.py --publish-result <final-json-path> --result-branch rx-node
|
||||
|
||||
Require RX RESULT REMOTE HEAD to equal the final result commit. If the push or confirmation fails, preserve the local JSON and logs, report the error, and do not claim publication success.
|
||||
|
||||
## 8. Offline decode
|
||||
|
||||
Use the tracked synchronized truth artifact without copying TX files manually:
|
||||
|
||||
python -B -X utf8 tools/qpsk_rf_rx.py --iq-file <capture-path> --iq-format cs8 --truth-metadata configs/rf/rf001a_truth_metadata.json --report <unique-path-outside-git>\rf001a_ota_decode.json
|
||||
|
||||
Report exact packets, missing packets, duplicates, BER and post-FEC errors from the generated report. Preserve a decoder failure as evidence; do not modify the receiver or invent results.
|
||||
|
||||
## 9. Prohibited actions
|
||||
|
||||
On the RX laptop, do not perform:
|
||||
|
||||
- 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.
|
||||
|
||||
## 10. Final RX report
|
||||
|
||||
TASK_ID =
|
||||
RF001A_OTA_CAPTURE_003
|
||||
|
||||
CHANNEL TEST =
|
||||
|
||||
RX CAPTURE COMPLETE =
|
||||
|
||||
EXIT CODE =
|
||||
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 =
|
||||
|
||||
CLKIN AFTER =
|
||||
|
||||
RF TX OBSERVED =
|
||||
|
||||
FINAL RX RESULT PUBLISH =
|
||||
|
||||
FINAL RX RESULT REMOTE HEAD =
|
||||
|
||||
OFFLINE DECODE =
|
||||
|
||||
EXACT PACKETS =
|
||||
|
||||
MISSING =
|
||||
|
||||
DUPLICATES =
|
||||
|
||||
BER =
|
||||
|
||||
POST-FEC ERRORS =
|
||||
|
||||
RF TX =
|
||||
0
|
||||
STREAMING DECODE = PASS / FAIL / NOT_RUN
|
||||
EXACT RF001A PACKETS =
|
||||
PEAK ANALYSIS MEMORY =
|
||||
RF TX = 0
|
||||
|
||||
STOP.
|
||||
|
||||
@@ -1,142 +1,96 @@
|
||||
TASK_ID = RF001A_OTA_TX_003
|
||||
TASK_TITLE = Transmit one coordinated RF-001A QPSK burst
|
||||
TASK_ID = RF001A_OTA_TX_004
|
||||
TASK_TITLE = Send one finite RF-001A burst into RX capture 004
|
||||
SOURCE_BRANCH = video-development
|
||||
EXPECTED_HEAD = bc55d0516dc1ee36cefa1ff096e7d86f60645303
|
||||
EXPECTED_HEAD = 168e03d9e778ab2ec63502552d385ec7be3ef677
|
||||
REQUIRES_APPROVAL = YES
|
||||
HARDWARE_ACCESS = YES
|
||||
RF_RX = NO
|
||||
RF_TX = YES
|
||||
ALLOW_COMMIT = NO
|
||||
ALLOW_PUSH = NO
|
||||
ALLOW_PUSH = TX_RESULT_ONLY
|
||||
RISK_LEVEL = RED
|
||||
|
||||
This file is the authoritative current task for the MAIN PC TX node.
|
||||
Read TX_NODE_STATE.md first. If chat history, this file, the shared RF-001A spec or current Git HEAD conflicts, stop and report the conflict.
|
||||
---
|
||||
|
||||
# Current TX task
|
||||
# RF-001A TX 004
|
||||
|
||||
TASK =
|
||||
TRANSMIT ONE COORDINATED RF-001A QPSK BURST
|
||||
This task is prepared only. Do not execute it now. Old approval for any earlier
|
||||
TASK_ID is invalid. The MAIN watcher waits for a fresh
|
||||
`RF001A_OTA_CAPTURE_004` READY event from `origin/rx-node`.
|
||||
|
||||
This is a new RED task. Publication of this file is not permission to transmit. It does not inherit any approval from RF001A_OTA_TX_001, RF001A_OTA_TX_002, the RX task, a previous watcher prompt or a previous RF run.
|
||||
## 1. Watcher gate
|
||||
|
||||
## 1. Coordination gate
|
||||
The watcher must require all of the following:
|
||||
|
||||
Start the local model-free watcher before the RX task begins:
|
||||
- RX TASK_ID = `RF001A_OTA_CAPTURE_004`;
|
||||
- state = `RX_READY_FOR_TX`;
|
||||
- source HEAD is the current `video-development` HEAD;
|
||||
- COMMON_CLOCK_VERIFIED = YES;
|
||||
- live child and growing capture are both true;
|
||||
- planned RX duration is 59–61 seconds;
|
||||
- READY is no more than 30 seconds old;
|
||||
- at least 10 seconds remain in the capture window;
|
||||
- the observed commit is the current valid `origin/rx-node` HEAD.
|
||||
|
||||
tools\start_tx_watcher.cmd
|
||||
The watcher then requests exactly:
|
||||
|
||||
The watcher polls origin/rx-node every 5 seconds. It processes every new result-branch commit from `last_processed_rx_node_commit` to the current `origin/rx-node` tip in chronological order. It accepts only a strict result for:
|
||||
|
||||
RX TASK_ID = RF001A_OTA_CAPTURE_003
|
||||
STATE = RX_READY_FOR_TX
|
||||
RX_READY_FOR_TX = YES
|
||||
PROCESS_ALIVE = YES
|
||||
FILE_GROWING = YES
|
||||
COMMON_CLOCK_VERIFIED = YES
|
||||
RF_TX = 0
|
||||
|
||||
The watcher records RF_HANDOFF_CHANNEL_TEST_001 READY and COMPLETE states without prompting for RED approval and never treats them as production readiness. A production READY publication must be no more than 30 seconds old. Its planned capture window must be 120 seconds. At least 10 seconds must remain. COMMON_CLOCK_VERIFIED must be YES, source_head must equal the expected video-development HEAD, and the result commit must be the current valid origin/rx-node HEAD.
|
||||
|
||||
When a valid new READY result appears, the watcher prints:
|
||||
|
||||
RX IS READY
|
||||
|
||||
TASK ID =
|
||||
CAPTURE STARTED =
|
||||
CAPTURE END EXPECTED =
|
||||
|
||||
TX TASK =
|
||||
RF001A_OTA_TX_003
|
||||
|
||||
RF TX REQUIRES RED APPROVAL
|
||||
|
||||
Enter:
|
||||
AUTHORIZE RF TX
|
||||
|
||||
Without the exact phrase AUTHORIZE RF TX, do not transmit.
|
||||
Any other response blocks TX. After the exact phrase, fetch `origin/rx-node`
|
||||
again and repeat every commit, digest, task, source HEAD, freshness, common
|
||||
clock and remaining-time check. A changed or stale result requires a new RX
|
||||
capture and a new exact RED approval.
|
||||
|
||||
After the phrase, the watcher fetches rx-node again and repeats the result commit, digest, task ID, source HEAD, READY, freshness, common-clock and capture-window checks. If the result changed or less than 10 seconds remain, it refuses authorization and requires a new RX capture.
|
||||
On success publish a schema-version-2 `TX_AUTHORIZED` event to `origin/tx-node`
|
||||
with the exact source RX commit and digest, TX TASK_ID `RF001A_OTA_TX_004`,
|
||||
`RF_TX_AUTHORIZED = YES`, `RF_TX_STARTED = NO` and `RF_TX = 0`. Also write the
|
||||
local authorization record. This event is permission evidence, not proof of a
|
||||
transmission.
|
||||
|
||||
The watcher never opens HackRF hardware. On success it writes one local authorization record under:
|
||||
## 2. Read-only TX preflight
|
||||
|
||||
.local\tx_authorizations\
|
||||
Before opening TX hardware:
|
||||
|
||||
The record has rf_tx_started = false and rf_tx = 0. It is the handoff to the separately controlled TX executor. It is not evidence that transmission occurred.
|
||||
1. Run `git pull --ff-only origin video-development`.
|
||||
2. Require branch `video-development`, a clean tracked worktree and local HEAD
|
||||
equal to `origin/video-development`; `EXPECTED_HEAD` must be its ancestor.
|
||||
3. Run `hackrf_info`; require TX serial
|
||||
`000000000000000097c063dc323b97d3`.
|
||||
4. Require the recorded MASTER CLKOUT command = PASS, both antennas connected,
|
||||
approximately 2 m distance, clear LOS, and video disabled.
|
||||
5. Verify the waveform without radio access:
|
||||
|
||||
## 2. Fixed RF-001A parameters
|
||||
python -B -X utf8 tools/rf001a_prepare.py --verify-dir data/raw/rf001/rf001a_common_clock_001
|
||||
|
||||
center frequency = 1400000000 Hz
|
||||
sample rate = 8000000 samples/s
|
||||
modulation = QPSK
|
||||
symbol rate = 500000 symbols/s
|
||||
inner FEC = 2/3
|
||||
payload = 256 bytes
|
||||
packet count = 100
|
||||
inter-packet gap = 5 ms
|
||||
digital carrier offset = +125000 Hz
|
||||
TX VGA = 20 dB
|
||||
RF amp = OFF
|
||||
antenna bias = OFF
|
||||
common 10 MHz reference = VERIFIED
|
||||
distance = approximately 2 m
|
||||
LOS = YES
|
||||
video = OFF
|
||||
6. Require:
|
||||
|
||||
The waveform is one finite burst. Never add repeat mode.
|
||||
|
||||
## 3. Read-only preflight
|
||||
|
||||
Before the watcher can request RED approval:
|
||||
|
||||
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 TX serial 000000000000000097c063dc323b97d3.
|
||||
7. Require the recorded MASTER CLKOUT command result = PASS.
|
||||
8. Require confirmation that both antennas are connected, the nodes are approximately 2 m apart, and LOS is clear.
|
||||
9. Verify the prepared waveform without radio access:
|
||||
|
||||
python -B -X utf8 tools/rf001a_prepare.py --verify-dir data/raw/rf001/rf001a_common_clock_001
|
||||
|
||||
10. Verify the actual cs8 bytes through the existing offline receiver:
|
||||
|
||||
python -B -X utf8 tools/qpsk_rf_rx.py --iq-file data/raw/rf001/rf001a_common_clock_001/tx_waveform.cs8 --iq-format cs8 --truth-metadata configs/rf/rf001a_truth_metadata.json --report <new-path-outside-git>\rf001a_tx_offline_verify.json
|
||||
|
||||
Require:
|
||||
|
||||
canonical waveform = PASS
|
||||
hardware-format cs8 = PASS
|
||||
offline hardware-format roundtrip = 100/100 exact
|
||||
TX waveform path = data/raw/rf001/rf001a_common_clock_001/tx_waveform.cs8
|
||||
TX waveform size = 17667656 bytes
|
||||
TX waveform SHA256 = 75f3fa39bfa4ee419f0676c54e4b9c486d7ff64bd0fe2fc06343b20c9d117ecd
|
||||
sample count = 8833828 complex samples
|
||||
expected duration = 1.1042285 s
|
||||
waveform path = data/raw/rf001/rf001a_common_clock_001/tx_waveform.cs8
|
||||
waveform size = 17667656 bytes
|
||||
waveform SHA256 = 75f3fa39bfa4ee419f0676c54e4b9c486d7ff64bd0fe2fc06343b20c9d117ecd
|
||||
sample count = 8833828 complex samples
|
||||
duration = 1.1042285 s
|
||||
offline exact packets = 100/100
|
||||
|
||||
If any value differs, do not transmit.
|
||||
|
||||
## 4. Final check before opening TX hardware
|
||||
## 3. Final executor gate
|
||||
|
||||
The controlled TX executor must read the authorization record immediately before TX and require:
|
||||
Immediately before hardware access, the controlled executor must read the
|
||||
local authorization and re-fetch `origin/rx-node`. Require:
|
||||
|
||||
- task_id = RF001A_OTA_TX_003;
|
||||
- authorization = AUTHORIZE RF TX;
|
||||
- rx_task_id = RF001A_OTA_CAPTURE_003;
|
||||
- dry_run = false;
|
||||
- action = MANUAL_EXECUTOR_GATE_OPEN;
|
||||
- rf_tx_started = false;
|
||||
- rf_tx = 0;
|
||||
- the authorization references the latest fetched rx-node result commit and digest;
|
||||
- current time is still inside the READY capture window with at least 10 seconds remaining.
|
||||
- TX TASK_ID = `RF001A_OTA_TX_004`;
|
||||
- RX TASK_ID = `RF001A_OTA_CAPTURE_004`;
|
||||
- authorization phrase = `AUTHORIZE RF TX`;
|
||||
- authorization binds the current READY commit and digest;
|
||||
- `rf_tx_started = false` and `rf_tx = 0` in the authorization record;
|
||||
- at least 10 seconds remain in the same READY window.
|
||||
|
||||
Fetch origin/rx-node once more and compare its current result digest and commit with the authorization record. Re-run the READY and time checks. If anything differs, if the result is stale, or if fewer than 10 seconds remain, do not transmit and require a new RX capture and a new exact RED approval.
|
||||
If any check fails, do not open HackRF.
|
||||
|
||||
## 5. One authorized TX execution
|
||||
## 4. One finite TX burst
|
||||
|
||||
Only after every preceding condition passes may the executor create a new unique evidence directory outside Git and run exactly one finite command:
|
||||
Only after all gates pass may the executor create a unique evidence directory
|
||||
outside Git and run exactly one finite command:
|
||||
|
||||
hackrf_transfer.exe
|
||||
-d 000000000000000097c063dc323b97d3
|
||||
@@ -149,71 +103,48 @@ Only after every preceding condition passes may the executor create a new unique
|
||||
-p 0
|
||||
-B
|
||||
|
||||
Run it once, synchronously, and preserve:
|
||||
Never add repeat mode and never retry automatically.
|
||||
|
||||
- exact process PID;
|
||||
- start and end UTC timestamps;
|
||||
- exact exit code;
|
||||
- complete stdout and stderr;
|
||||
- every TX underrun line;
|
||||
- every USB error;
|
||||
- waveform path, size and SHA256;
|
||||
- command parameters.
|
||||
At the exact process start, publish `TX_STARTED` to `origin/tx-node`, preserving
|
||||
the authorization binding and recording `TX_STARTED_AT` as accurately as the
|
||||
host permits. Set `RF_TX_STARTED = YES` and `RF_TX = 1` because this event is
|
||||
evidence of an actual started process.
|
||||
|
||||
Set RF TX STOPPED = YES only after the finite process exits. Never retry automatically. Never start a second burst.
|
||||
Write the strict schema-version-2 event JSON outside Git and publish it with:
|
||||
|
||||
## 6. Final TX report
|
||||
python -B tools/tx_agent_watcher.py --publish-tx-event <started-json> --acknowledgement-branch tx-node
|
||||
|
||||
TASK_ID =
|
||||
RF001A_OTA_TX_003
|
||||
After that exact process exits, publish `TX_COMPLETE` with `TX_FINISHED_AT`,
|
||||
the integer process exit code, underrun count and error count. Preserve the
|
||||
complete stdout and stderr outside Git. The event reports what happened; it
|
||||
does not by itself prove that packets were received. Captured IQ remains the
|
||||
source for exact packet verification.
|
||||
|
||||
RED AUTHORIZATION =
|
||||
AUTHORIZE RF TX / MISSING
|
||||
Publish the completion JSON with the same `--publish-tx-event` command. The
|
||||
publisher requires the matching `TX_AUTHORIZED` and `TX_STARTED` history.
|
||||
|
||||
RX READY FOR TX =
|
||||
YES / NO
|
||||
The only files permitted on `tx-node` are:
|
||||
|
||||
docs/handoff/results/TX_RESULT.json
|
||||
docs/handoff/results/TX_RESULT.md
|
||||
|
||||
Do not publish waveform, IQ, video, source code, local state or large logs.
|
||||
|
||||
## 5. Final report
|
||||
|
||||
TASK_ID = RF001A_OTA_TX_004
|
||||
RX READY FOR TX = YES / NO
|
||||
RX RESULT HEAD =
|
||||
|
||||
RX RESULT DIGEST =
|
||||
|
||||
TX SERIAL =
|
||||
|
||||
TX WAVEFORM SHA256 =
|
||||
|
||||
TX START TIME =
|
||||
|
||||
TX END TIME =
|
||||
|
||||
TX EXIT CODE =
|
||||
|
||||
TX AUTHORIZED EVENT = PASS / FAIL
|
||||
TX STARTED AT =
|
||||
TX FINISHED AT =
|
||||
TX PROCESS EXIT CODE =
|
||||
TX UNDERRUNS =
|
||||
TX ERRORS =
|
||||
TX COMPLETE EVENT = PASS / FAIL
|
||||
RF TX STOPPED = YES / NO
|
||||
|
||||
USB ERRORS =
|
||||
|
||||
RF TX STOPPED =
|
||||
YES / NO
|
||||
|
||||
BURSTS TRANSMITTED =
|
||||
0 / 1
|
||||
|
||||
RF TX =
|
||||
0
|
||||
|
||||
At report time, RF TX = 0 means no transmission process remains active.
|
||||
|
||||
## 7. Prohibited actions
|
||||
|
||||
Do not:
|
||||
|
||||
- transmit without the fresh READY state and exact RED phrase;
|
||||
- reuse an earlier authorization record;
|
||||
- use repeat mode;
|
||||
- transmit a second burst;
|
||||
- retry automatically;
|
||||
- change frequency, gains, clock configuration or waveform;
|
||||
- update firmware or CPLD;
|
||||
- use Zadig or change USB drivers;
|
||||
- commit or push from this task.
|
||||
|
||||
STOP after the single finite burst or any failed gate.
|
||||
STOP after the one finite burst and event publication. Firmware, CPLD, Zadig,
|
||||
USB driver changes, clock reconfiguration, a second burst, commit or push to
|
||||
`video-development` are forbidden.
|
||||
|
||||
@@ -12,7 +12,7 @@ The MAIN-to-RX acknowledgement channel uses the independent origin/tx-node branc
|
||||
|
||||
docs/handoff/NEXT_RX_TASK.md starts with these plain key-value fields:
|
||||
|
||||
TASK_ID = RF001A_OTA_CAPTURE_003
|
||||
TASK_ID = RF001A_OTA_CAPTURE_004
|
||||
TASK_TITLE = Start coordinated RF-001A common-clock OTA capture
|
||||
SOURCE_BRANCH = video-development
|
||||
EXPECTED_HEAD = <full 40-character base commit hash>
|
||||
@@ -73,11 +73,11 @@ After the task actually finishes, mark the local ID complete:
|
||||
|
||||
The coordinated RF-001A result is a strict JSON object. Unknown or missing keys are rejected. READY requires:
|
||||
|
||||
- task_id = RF001A_OTA_CAPTURE_003;
|
||||
- task_id = RF001A_OTA_CAPTURE_004;
|
||||
- state = RX_READY_FOR_TX;
|
||||
- rx_ready_for_tx, process_alive, file_growing and common_clock_verified all true;
|
||||
- a positive capture_pid;
|
||||
- 120-second planned duration;
|
||||
- 60-second planned duration;
|
||||
- rf_tx = 0;
|
||||
- final capture fields set to null.
|
||||
|
||||
@@ -95,6 +95,12 @@ Timestamps use timezone-aware ISO-8601. source_head is the actual local 40-chara
|
||||
|
||||
## Publishing to rx-node
|
||||
|
||||
Before any production hardware access, run
|
||||
`python -B tools/rx_agent_watcher.py --check-result-write`. It performs an
|
||||
authenticated fetch, a no-change `git push --dry-run` of the existing rx-node
|
||||
HEAD to the same ref, and a second remote HEAD check. Failure blocks capture.
|
||||
The check creates no commit and no READY event.
|
||||
|
||||
Publish a prepared local JSON file with:
|
||||
|
||||
python -B tools/rx_agent_watcher.py --publish-result <result-json> --result-branch rx-node
|
||||
@@ -114,7 +120,14 @@ It checks the branch with git fetch and git ls-remote through the real origin co
|
||||
docs/handoff/results/RX_RESULT.json
|
||||
docs/handoff/results/RX_RESULT.md
|
||||
|
||||
It checks the staged path set, commits, pushes HEAD:rx-node, and confirms that git ls-remote returns the new commit. It then removes the temporary worktree. It never switches the source checkout. It rejects any additional staged path and enforces a 64 KiB limit per result file.
|
||||
It checks the staged path set, creates one immutable commit, pushes that exact
|
||||
commit hash to rx-node, and confirms that `git ls-remote` returns it. After a
|
||||
recognized transient network or authentication error, it first checks whether
|
||||
the commit already reached the remote. If not, it fetches and may retry that
|
||||
same commit once. It never creates a second READY commit or changes the payload
|
||||
between attempts. It then removes the temporary worktree. It never switches
|
||||
the source checkout. It rejects any additional staged path and enforces a
|
||||
64 KiB limit per result file.
|
||||
|
||||
Raw IQ, video, waveform files, large logs, secrets and machine-local state remain outside Git. A missing result branch or failed push is reported and never authorizes TX.
|
||||
|
||||
@@ -132,7 +145,12 @@ For each successfully handled channel-test event, the MAIN watcher publishes a s
|
||||
|
||||
The tx-node publisher stages exactly `docs/handoff/results/TX_RESULT.json` and `docs/handoff/results/TX_RESULT.md`, checks the staged path allowlist and enforces 64 KiB per file. A push failure prevents the rx-node cursor from advancing. Duplicate acknowledgements are suppressed by their source binding. No local state, code, IQ, video, waveform or large log is published.
|
||||
|
||||
A successful gate writes one local authorization record under .local/tx_authorizations/. The record has rf_tx_started = false and rf_tx = 0. The separately controlled TX executor must re-fetch and compare the result commit and digest immediately before opening hardware.
|
||||
A successful gate publishes a schema-version-2 `TX_AUTHORIZED` event and writes
|
||||
one local authorization record under `.local/tx_authorizations/`. The record
|
||||
has `rf_tx_started = false` and `rf_tx = 0`. The separately controlled TX
|
||||
executor must re-fetch and compare the result commit and digest immediately
|
||||
before opening hardware. It then publishes `TX_STARTED` at process start and
|
||||
`TX_COMPLETE` after exit with UTC timestamps, exit code, underruns and errors.
|
||||
|
||||
Duplicate result digests are not shown repeatedly.
|
||||
|
||||
@@ -147,6 +165,6 @@ Duplicate result digests are not shown repeatedly.
|
||||
|
||||
## Bidirectional channel and dry-run contracts
|
||||
|
||||
The completed preflight channel command published synthetic READY and COMPLETE results with TASK_ID RF_HANDOFF_CHANNEL_TEST_001 through origin/rx-node. The MAIN watcher observed both without a RED prompt. The RX watcher now requires exact matching acknowledgements in origin/tx-node before RF001A_OTA_CAPTURE_003 can pass its software gate.
|
||||
The completed preflight channel command published synthetic READY and COMPLETE results with TASK_ID RF_HANDOFF_CHANNEL_TEST_001 through origin/rx-node. The MAIN watcher observed both without a RED prompt. The RX watcher now requires exact matching acknowledgements in origin/tx-node before RF001A_OTA_CAPTURE_004 can pass its software gate.
|
||||
|
||||
Automated coordination tests use a temporary local bare Git repository and independent rx-node and tx-node branches. They cover exact commit and digest binding, duplicate suppression, two commits between polls, stale acknowledgement rejection, missing tx-node, push failure, non-fast-forward protection and a full fake two-way flow without accessing the real remote or SDR hardware and without transmitting RF.
|
||||
|
||||
@@ -10,7 +10,7 @@ The watcher stays in a visible console and polls origin/video-development every
|
||||
|
||||
## Idle
|
||||
|
||||
While no new TASK_ID exists, the watcher performs only git fetch, reads the remote task file and compares IDs with .local/rx_agent_state.json. There is no model execution or token use. For RF001A_OTA_CAPTURE_003 it also verifies the required historical acknowledgements on origin/tx-node before offering the task for approval.
|
||||
While no new TASK_ID exists, the watcher performs only git fetch, reads the remote task file and compares IDs with .local/rx_agent_state.json. There is no model execution or token use. For RF001A_OTA_CAPTURE_004 it also verifies the required historical acknowledgements on origin/tx-node and performs the authenticated no-change rx-node write preflight before offering the task for approval.
|
||||
|
||||
## New task
|
||||
|
||||
@@ -41,7 +41,9 @@ When the task is actually complete, run:
|
||||
|
||||
## Publish READY
|
||||
|
||||
For RF001A_OTA_CAPTURE_003, first require the watcher to print:
|
||||
For RF001A_OTA_CAPTURE_004, first require the watcher to print:
|
||||
|
||||
RX-NODE WRITE PREFLIGHT = PASS
|
||||
|
||||
MAIN READY RECEIVED = CONFIRMED
|
||||
MAIN COMPLETE RECEIVED = CONFIRMED
|
||||
@@ -65,6 +67,14 @@ The command fails if:
|
||||
|
||||
A failed publication is not READY evidence and cannot authorize TX.
|
||||
|
||||
Immediately before hardware access, repeat the same non-mutating check:
|
||||
|
||||
python -B tools/rx_agent_watcher.py --check-result-write --result-branch rx-node
|
||||
|
||||
The READY publisher retries only one recognized transient failure. It checks
|
||||
the remote before retrying and pushes the same immutable commit; it does not
|
||||
create a replacement READY commit.
|
||||
|
||||
## Verified model-free channel evidence
|
||||
|
||||
The completed RF_HANDOFF_CHANNEL_TEST_001 evidence is bound to READY commit `4cb6c8cad1ab5194755c2740c2a5a83933d099d8` and COMPLETE commit `83d91ff5309777ab639a24f774e9d0b8825a11e9`. The RX watcher accepts the gate only after origin/tx-node contains the matching MAIN acknowledgements. This read-only check performs no capture and keeps RF TX at 0.
|
||||
|
||||
@@ -6,7 +6,7 @@ The publisher creates both docs/handoff/results/RX_RESULT.json and docs/handoff/
|
||||
|
||||
{
|
||||
"schema_version": 1,
|
||||
"task_id": "RF001A_OTA_CAPTURE_003",
|
||||
"task_id": "RF001A_OTA_CAPTURE_004",
|
||||
"state": "RX_READY_FOR_TX",
|
||||
"rx_ready_for_tx": true,
|
||||
"process_alive": true,
|
||||
@@ -14,7 +14,7 @@ The publisher creates both docs/handoff/results/RX_RESULT.json and docs/handoff/
|
||||
"capture_pid": 1234,
|
||||
"published_at": "2026-09-21T12:00:02+00:00",
|
||||
"capture_started_at": "2026-09-21T12:00:00+00:00",
|
||||
"expected_capture_end": "2026-09-21T12:02:00+00:00",
|
||||
"expected_capture_end": "2026-09-21T12:01:00+00:00",
|
||||
"capture_path": "C:\\Users\\Professional\\Documents\\SDR_Rover_Captures\\<run>\\rf001a_ota_capture.cs8",
|
||||
"common_clock_verified": true,
|
||||
"source_head": "<40-character lowercase Git HEAD>",
|
||||
|
||||
@@ -44,19 +44,37 @@ The acknowledgement operation is ordinary Python and Git. It does not invoke Cha
|
||||
|
||||
RF_HANDOFF_CHANNEL_TEST_001 READY and COMPLETE states are recorded and acknowledged without a RED prompt and never open the production gate.
|
||||
|
||||
For a fresh valid RF001A_OTA_CAPTURE_003 READY result, the watcher checks the expected source HEAD, current origin/rx-node commit, common clock and capture window. It then prints the task ID, capture start and expected end and asks for:
|
||||
For a fresh valid RF001A_OTA_CAPTURE_004 READY result, the watcher checks the expected source HEAD, current origin/rx-node commit, common clock and capture window. It then prints the task ID, capture start and expected end and asks for:
|
||||
|
||||
AUTHORIZE RF TX
|
||||
|
||||
Any other input blocks authorization.
|
||||
|
||||
After the exact phrase, the watcher fetches rx-node again. It requires the same result digest and commit, the expected source HEAD, a fresh publication, an active 120-second capture window, verified common clock and at least 10 seconds remaining. An expired historical READY is recorded as observed but cannot create an authorization.
|
||||
After the exact phrase, the watcher fetches rx-node again. It requires the same result digest and commit, the expected source HEAD, a fresh publication, an active 60-second capture window, verified common clock and at least 10 seconds remaining. An expired historical READY is recorded as observed but cannot create an authorization.
|
||||
|
||||
On success it writes:
|
||||
|
||||
.local\tx_authorizations\RF001A_OTA_TX_003_authorized.json
|
||||
.local\tx_authorizations\RF001A_OTA_TX_004_authorized.json
|
||||
|
||||
This record opens the gate for the separately controlled TX executor. The watcher itself does not run hackrf_transfer. The record explicitly says rf_tx_started = false and rf_tx = 0.
|
||||
The watcher also publishes `TX_AUTHORIZED` to origin/tx-node. This record opens
|
||||
the gate for the separately controlled TX executor. The watcher itself does
|
||||
not run hackrf_transfer. The record explicitly says `rf_tx_started = false`
|
||||
and `rf_tx = 0`.
|
||||
|
||||
At actual process start the executor publishes `TX_STARTED` with the most
|
||||
accurate available UTC timestamp. After process exit it publishes
|
||||
`TX_COMPLETE` with finish time, integer exit code, underruns and errors. These
|
||||
events preserve the exact RX READY commit and digest binding. They do not
|
||||
replace verification of packets from captured IQ.
|
||||
|
||||
The executor writes each strict schema-version-2 event JSON outside Git, then
|
||||
uses the model-free publisher:
|
||||
|
||||
python -B tools/tx_agent_watcher.py --publish-tx-event <event-json> --acknowledgement-branch tx-node
|
||||
|
||||
The publisher requires a clean, current `video-development` checkout. A
|
||||
`TX_STARTED` event requires a matching `TX_AUTHORIZED` in tx-node. A
|
||||
`TX_COMPLETE` event also requires the matching `TX_STARTED` timestamp.
|
||||
|
||||
The TX executor must re-fetch rx-node and compare the result commit and digest immediately before hardware access.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user