Add bidirectional RF handoff coordination

This commit is contained in:
LittleSam129
2026-09-21 17:02:49 +03:00
parent 34f5ace545
commit bc55d0516d
14 changed files with 1978 additions and 556 deletions

View File

@@ -1,7 +1,7 @@
TASK_ID = RF001A_OTA_TX_001
TASK_TITLE = Transmit first RF-001A QPSK packet burst
TASK_ID = RF001A_OTA_TX_002
TASK_TITLE = Transmit one coordinated RF-001A QPSK burst
SOURCE_BRANCH = video-development
EXPECTED_HEAD = 1e3aa25d7007c7c8e84608cddb02f06f77e63396
EXPECTED_HEAD = 34f5ace54540f947311ada7e81a463c08abfd2d3
REQUIRES_APPROVAL = YES
HARDWARE_ACCESS = YES
RF_RX = NO
@@ -11,205 +11,209 @@ ALLOW_PUSH = NO
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 and current Git HEAD conflict, stop and report the conflict.
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
TASK =
TRANSMIT FIRST RF-001A QPSK PACKET BURST
TRANSMIT ONE COORDINATED RF-001A QPSK BURST
This is a new RED task. It does not inherit the RX task approval. Publication of this file is not permission to transmit.
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, the RX task, a previous watcher prompt or a previous RF run.
Before any RF TX, the user must:
## 1. Coordination gate
1. separately provide the exact authorization `AUTHORIZE RF TX`;
2. confirm the live RX status `RX READY FOR TX = YES` for task `RF001A_OTA_CAPTURE_001`.
Start the local model-free watcher before the RX task begins:
A normal approval, an earlier RF authorization, or an RX approval is insufficient.
tools\start_tx_watcher.cmd
## 1. Fixed RF-001A parameters
The watcher polls origin/rx-node every 5 seconds and accepts only a strict result for:
```text
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
```
RX TASK_ID = RF001A_OTA_CAPTURE_002
STATE = RX_READY_FOR_TX
RX_READY_FOR_TX = YES
PROCESS_ALIVE = YES
FILE_GROWING = YES
COMMON_CLOCK_VERIFIED = YES
RF_TX = 0
The waveform is one finite burst. Never add `-R`.
The READY publication must be no more than 30 seconds old. Its planned capture window must be 120 seconds. At least 10 seconds must remain.
## 2. Preconditions
When a valid new READY result appears, the watcher prints:
Before asking for RED authorization, prepare all read-only evidence:
RX IS READY
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 the user to confirm both TX and RX antennas connected, approximately 2 m LOS and the intended antenna geometry.
9. Require the current RX report to show `RX READY FOR TX = YES`, task ID `RF001A_OTA_CAPTURE_001`, a live capture PID, capture path, start time and expected end time.
10. Require current time to be before the RX expected end time.
TASK ID =
CAPTURE STARTED =
CAPTURE END EXPECTED =
Verify the prepared waveform without accessing radio hardware:
TX TASK =
RF001A_OTA_TX_002
```powershell
python -B -X utf8 tools/rf001a_prepare.py --verify-dir data/raw/rf001/rf001a_common_clock_001
```
RF TX REQUIRES RED APPROVAL
Then verify the actual hardware-format bytes through the existing receiver. The report path must be new and outside Git:
Enter:
AUTHORIZE RF TX
```powershell
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 data/raw/rf001/rf001a_common_clock_001/tx_metadata.json `
--report '<new unique path outside Git>\rf001a_tx_offline_verify.json'
```
Without the exact phrase AUTHORIZE RF TX, do not transmit.
Require the receiver report to contain `crc_valid_packets = 100`, `exact_packets = 100`, no missing packets and no duplicates.
After the phrase, the watcher fetches rx-node again and repeats all READY, freshness and capture-window checks. If the result changed or less than 10 seconds remain, it refuses authorization and requires a new RX capture.
The watcher never opens HackRF hardware. On success it writes one local authorization record under:
.local\tx_authorizations\
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.
## 2. Fixed RF-001A parameters
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
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 data/raw/rf001/rf001a_common_clock_001/tx_metadata.json --report <new-path-outside-git>\rf001a_tx_offline_verify.json
Require:
```text
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 = 75f3fa39bfa88b623b7e4e5a02e3a80b52185007397ea56ceaacc13353974b2
sample count = 8833828 complex samples
expected duration = 1.1042285 s
```
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 = 75f3fa39bfa88b623b7e4e5a02e3a80b52185007397ea56ceaacc13353974b2
sample count = 8833828 complex samples
expected duration = 1.1042285 s
If any value differs, if RX is not ready, or if the RX capture window has ended, do not transmit and STOP.
If any value differs, do not transmit.
Only after every precondition passes may the user provide the separate exact phrase `AUTHORIZE RF TX`.
## 4. Final check before opening TX hardware
## 3. One authorized TX execution
The controlled TX executor must read the authorization record immediately before TX and require:
After the separate RED authorization and the live `RX READY FOR TX = YES` confirmation, create a new unique local evidence directory outside Git. Refuse an existing directory and never overwrite earlier evidence.
- task_id = RF001A_OTA_TX_002;
- authorization = AUTHORIZE RF TX;
- rx_task_id = RF001A_OTA_CAPTURE_002;
- 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.
Use this single finite command:
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.
```text
hackrf_transfer.exe
-d 000000000000000097c063dc323b97d3
-t data/raw/rf001/rf001a_common_clock_001/tx_waveform.cs8
-f 1400000000
-s 8000000
-n 8833828
-x 20
-a 0
-p 0
-B
```
## 5. One authorized TX execution
Run it once, synchronously, and preserve the exact process exit code:
Only after every preceding condition passes may the executor create a new unique evidence directory outside Git and run exactly one finite command:
```powershell
$txRunId = '{0}_{1}' -f (Get-Date -Format 'yyyyMMdd_HHmmss'), ([guid]::NewGuid().ToString('N').Substring(0, 8))
$txEvidenceRoot = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'SDR_Rover_TX_Evidence'
$txEvidenceDirectory = Join-Path $txEvidenceRoot "RF001A_OTA_TX_001_$txRunId"
if (Test-Path -LiteralPath $txEvidenceDirectory) { throw "TX evidence directory already exists: $txEvidenceDirectory" }
New-Item -ItemType Directory -Path $txEvidenceDirectory -ErrorAction Stop | Out-Null
hackrf_transfer.exe
-d 000000000000000097c063dc323b97d3
-t data/raw/rf001/rf001a_common_clock_001/tx_waveform.cs8
-f 1400000000
-s 8000000
-n 8833828
-x 20
-a 0
-p 0
-B
$hackrfTransfer = 'C:\Users\user\Tools\HackRF\Library\bin\hackrf_transfer.exe'
$waveformPath = (Resolve-Path -LiteralPath 'data/raw/rf001/rf001a_common_clock_001/tx_waveform.cs8').Path
$stdoutPath = Join-Path $txEvidenceDirectory 'hackrf_transfer.stdout.log'
$stderrPath = Join-Path $txEvidenceDirectory 'hackrf_transfer.stderr.log'
$txArguments = @(
'-d', '000000000000000097c063dc323b97d3',
'-t', $waveformPath,
'-f', '1400000000',
'-s', '8000000',
'-n', '8833828',
'-x', '20',
'-a', '0',
'-p', '0',
'-B'
)
Run it once, synchronously, and preserve:
$txStartedAt = [DateTimeOffset]::Now
$txProcess = Start-Process -FilePath $hackrfTransfer -ArgumentList $txArguments `
-RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath `
-PassThru -Wait -WindowStyle Hidden
$txFinishedAt = [DateTimeOffset]::Now
$txProcess.Refresh()
$txExitCode = $txProcess.ExitCode
```
- 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.
Immediately write the exit code, process ID, timestamps, duration, command parameters, waveform size and waveform SHA256 to a new JSON metadata file in `$txEvidenceDirectory`. Preserve complete stdout and stderr.
Set RF TX STOPPED = YES only after the finite process exits. Never retry automatically. Never start a second burst.
Inspect every buffer-statistics line. Record:
## 6. Final TX report
```text
TX start time =
TX end time =
TX PID =
TX exit code =
TX underruns =
USB errors =
RF TX STOPPED =
YES / NO
```
TASK_ID =
RF001A_OTA_TX_002
Do not infer zero from a missing counter. Use `NOT_REPORTED` when no counter exists. Any nonzero intermediate or final underrun/error line remains evidence even if a later line reports zero.
RED AUTHORIZATION =
AUTHORIZE RF TX / MISSING
Set `RF TX STOPPED = YES` only after the finite process exits. Do not start a second burst for any reason. Do not retry automatically.
RX READY FOR TX =
YES / NO
## 4. Final TX report
RX RESULT HEAD =
```text
TASK_ID =
RF001A_OTA_TX_001
RX RESULT DIGEST =
RED AUTHORIZATION =
AUTHORIZE RF TX / MISSING
TX SERIAL =
RX READY FOR TX =
YES / NO
TX WAVEFORM SHA256 =
TX SERIAL =
TX START TIME =
TX WAVEFORM SHA256 =
TX END TIME =
TX START TIME =
TX EXIT CODE =
TX END TIME =
TX UNDERRUNS =
TX EXIT CODE =
USB ERRORS =
TX UNDERRUNS =
RF TX STOPPED =
YES / NO
USB ERRORS =
BURSTS TRANSMITTED =
0 / 1
RF TX STOPPED =
YES / NO
RF TX =
0
BURSTS TRANSMITTED =
0 / 1
At report time, RF TX = 0 means no transmission process remains active.
RF TX =
0
```
## 7. Prohibited actions
At report time, `RF TX = 0` means no transmission process remains active.
Do not:
## 5. Prohibited actions
- 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.
Do not transmit before the separate exact RED authorization. Do not inherit approval from the RX task. Do not use repeat mode, start a second burst, change frequency, gains, clock configuration, waveform, firmware, CPLD, Zadig or USB drivers. Do not commit or push from this task.
STOP after the single finite burst or any failed gate.