Commit Graph

80 Commits

Author SHA1 Message Date
LittleSam129
06fadf6957 Update RX node preflight handoff 2026-09-18 13:09:09 +03:00
LittleSam129
8017849e24 Add RX laptop handoff workflow 2026-09-18 12:20:15 +03:00
LittleSam129
1ff8ccf27e RF-001: add hardware test plan 2026-09-17 15:27:14 +03:00
LittleSam129
3c31bac0da CLOCK-001: validate independent-clock QPSK receiver 2026-09-17 15:05:44 +03:00
LittleSam129
6df5f8aa39 VIDEO-005: add full offline QPSK IQ video receiver 2026-09-17 13:34:58 +03:00
LittleSam129
95f664076a Complete VIDEO-001 through VIDEO-004 software video studies 2026-09-16 18:27:35 +03:00
LittleSam129
af7c05f2da Lab067: localize remaining PHY header failures 2026-09-15 17:45:38 +03:00
LittleSam129
b27a5cf00e Lab066: validate 500 ksymbol/s full-packet OTA reception 2026-09-15 15:51:57 +03:00
LittleSam129
dacf04f2d0 Lab065: validate OTA gain improvement at TX VGA 20 dB 2026-09-15 13:42:08 +03:00
LittleSam129
cff226e0bf Lab064: measure OTA level linearity at TX VGA 10 dB 2026-09-15 12:03:48 +03:00
LittleSam129
4437917465 Lab063: localize Lab062 RF quality degradation offline 2026-09-14 15:11:46 +03:00
LittleSam129
f8ae6c6bfc Lab062: record invalidated 500 ksymbol/s RF packet trial 2026-09-14 13:58:42 +03:00
LittleSam129
25cc89f0c7 Lab061: integrate T2 into the strict receiver 2026-09-14 11:07:40 +03:00
LittleSam129
92b0e0f8dd Lab060: validate decision-directed carrier tracking 2026-09-11 14:13:51 +03:00
LittleSam129
e16ec23fdb Lab059: establish limits of preamble-only carrier estimation 2026-09-11 13:08:32 +03:00
LittleSam129
82f17499ac Lab058: characterize symbol quality in controlled ABAB recording 2026-09-11 11:49:48 +03:00
LittleSam129
5773136ebf Lab057: diagnose Lab054 symbol synchronization failures offline 2026-09-10 16:10:17 +03:00
LittleSam129
92477a4632 Lab056: compare 20 and 500 ksymbol/s in controlled RF session 2026-09-10 13:58:11 +03:00
LittleSam129
5add95ecb8 Lab055: audit received signal energy across symbol rates 2026-09-09 15:07:11 +03:00
LittleSam129
0368c6d3e9 Lab053: achieve real-time 5 fps software video receiver 2026-09-09 10:26:55 +03:00
LittleSam129
cc38900cae Lab052: suppress false preamble candidates 2026-09-08 17:29:52 +03:00
LittleSam129
9f33607d3a Lab051: accelerate 500 ksymbol/s streaming receiver 2026-09-08 16:04:09 +03:00
LittleSam129
60691a09f1 Lab050: model fresh-frame SDR video stream 2026-09-08 14:15:36 +03:00
LittleSam129
cdf5396e80 Lab049: define ELRS control and SDR video architecture 2026-09-08 13:26:06 +03:00
LittleSam129
ac6cc792b8 Lab048: accelerate BPSK receiver to real-time 2026-09-03 14:42:02 +03:00
LittleSam129
6fb7e4ec54 Lab047: transmit JPEG over real RF link 2026-09-03 13:13:23 +03:00
LittleSam129
b0111d2232 Docs: add laboratory index and result pointers 2026-09-02 16:19:27 +03:00
LittleSam129
52197a4d85 Lab046C: validate full 1400 MHz RF object transport 2026-09-02 16:18:40 +03:00
LittleSam129
d985030f04 Lab046C: recover short 1400 MHz capture offline 2026-09-02 12:23:14 +03:00
LittleSam129
9b906c622d Lab046C: analyze weak RF capture offline 2026-09-01 17:09:27 +03:00
LittleSam129
b6654671b4 Lab046C: record first continuous RF object attempt 2026-09-01 15:42:00 +03:00
LittleSam129
39fa4b139d Lab046B: validate continuous raw IQ frame reception 2026-09-01 14:36:51 +03:00
LittleSam129
e8d85ae4b7 Lab046A: validate continuous soft-symbol frame scanning 2026-09-01 13:18:42 +03:00
LittleSam129
1027eef53d Lab046: validate fragmented BPSK object transport 2026-09-01 11:39:45 +03:00
LittleSam129
62a376c811 Lab045: demonstrate real RF BPSK error correction
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-01 10:49:00 +03:00
LittleSam129
72f933a32b Lab044: validate arbitrary BPSK packet reception
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-31 14:39:12 +03:00
LittleSam129
4cee6e9b96 Lab043: establish reproducible HackRF BPSK working point 2026-08-31 12:18:44 +03:00
LittleSam129
42b0120eea Lab043: add hardware diagnostics and HackRF comparison tools 2026-08-21 17:33:57 +03:00
LittleSam129
f0fa7e8a46 Lab043: validate pilot-aided short BPSK link 2026-08-19 18:14:13 +03:00
LittleSam129
fa2e473c6d Lab042: validate Pluto image hardware loopback 2026-08-17 18:30:16 +03:00
LittleSam129
fce14f038e Lab042: transmit an image over the air, add the air loopback mode
First transmission through a real radio path in the project. A 4756-byte
JPEG went from transmitter to receiver over two antennas 60 cm apart at
225 MHz and reassembled byte-identical, reproduced across three runs.

The cable loopback still needs an SMA male-to-male adapter that has not
arrived, so this air run is a track verification, not the controlled
measurement: room levels are not held the way a cable holds them. Its
purpose was to exercise the device wrapper the models cannot cover.

Four defects the software model passed but the hardware exposed, each
reproduced and fixed:

- a 566 528-sample frame did not fit the 262 144 receive buffer, and the
  sample stream is not continuous across separate reads, so a frame on the
  seam was destroyed. Reception now uses one buffer sized to the frame
- carrier correction was always applied, but with a shared reference the
  true offset is near zero and the 80-symbol marker estimate is noise;
  correcting by it rotated phase along the whole frame, 65% bit errors
  measured. Ported should_apply_cfo_correction from Lab023
- receiver gain of 20 dB left the signal at 0.0003 of full scale. Levels
  set by measurement: RX 50 dB, TX -10 dB give zero bit errors with margin
- the error-vector metric returned zero on a failed receive, reporting a
  perfect signal where there was none, the same class of defect as the
  written-in figures of Lab041. Replaced with an explicit not-computed value

The air TX gain ceiling is now derived from the link budget by antenna
distance rather than a constant. Three loopback modes: software, cable,
air. PROJECT_LOG entry 019 records the run and the defects.

Software and cable results stay reproducible; 125 tests pass, gate passes.
2026-08-17 11:50:04 +03:00
LittleSam129
13f183f7c7 Run the core tests from the gate and finish the layout rename
The gate now runs pytest as well, so one command answers whether the code
base is broken: syntax, protocol imports, the 125 core tests, then the lab
functional suites. It fails when a test fails, verified by feeding it a
deliberately broken test.

Two leftovers from moving the labs into experiments/ are fixed: the lab
command still pointed at tests/, and the hook only watched protocol and
tests, so edits under experiments/ and tools/ triggered nothing.

Profiling the 3.6 s gate: 0.24 s syntax, 0.12 s imports, 0.61 s pytest,
2.7 s lab functional suites, of which Lab042's software loopback is 1.14 s
because it pushes a whole image through the chain. The pytest addition is
the small part. prepare_jpeg is now cached, since six checks call it and
each call re-encoded the image at several qualities.

README described the gate without the tests it now runs.
2026-08-11 13:40:05 +03:00
LittleSam129
a37ad1f7b3 Lab042: transmit a JPEG through a cable loopback
First lab in the project that switches the transmitter on. Lab001-Lab041
built the whole chain but never called into an SDR to send anything.

Two modes. Software closes the channel in memory and needs no hardware;
it is the reference the hardware run is measured against, so the
difference between them is what the radio path contributes. Hardware
drives one PlutoSDR with its transmit output looped back through a 30 dB
attenuator into its own receive input.

Everything outside the device wrapper is reused from protocol/ unchanged:
image_fragments, packet and the primitives in bpsk_radio.

Software run: a 4756-byte JPEG in 10 fragments, 5 272 064 samples, 2.059 s
of signal, 10 of 10 fragments recovered, image byte-identical, repeatable
across runs. Channel rate 18.475 kbit/s, below the 20 kbit/s symbol rate
because of framing and guards.

Three defects found by the verifier and fixed:

- the guard interval was only appended after the frame. Lab018 puts one on
  both sides, and the leading one is the one that matters: the first
  samples of a transmission are unreliable, so the preamble would have
  landed exactly on the transient. Invisible in software mode, which has
  no converter and no transient
- transmission was started before reception, contradicting section 8 of
  the specification and its own trap 3. The receiver now runs first and
  the first buffer is discarded, as in Lab024a
- packet and frame size were reported from the first frame only, while
  the tenth is 172 bytes against 536. Sizes are now per frame in the CSV
  and the report gives the honest range

Also fixed before verification: the JPEG did not fit its own 5000-byte
limit, so quality is now lowered until it does and reported as a result;
and the wall-clock rate was presented as a channel rate, which in software
mode measures computation, not a channel.

Artifacts are left out deliberately. data/processed/lab042 currently holds
the software reference; the committed results should be the real
experiment, which needs the cable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 13:28:47 +03:00
LittleSam129
46b10b1243 Add the GitLab CI pipeline
Three stages, all fast: the gate, then the core tests, then style.

Only the calculation environment is installed, no pyadi-iio or libiio, so
the pipeline needs no hardware. Laboratory experiments stay out of it:
they run for minutes, need inputs from data/raw that are not in the
repository, and some need a PlutoSDR attached. Those remain manual.

The style job is allow_failure for now. Lab001-Lab041 use a distinctive
layout with one argument per line, and normalising it would mean touching
50 files whose results are already recorded. The restriction lifts once
we agree on the rules.

Test results are published as a JUnit report so failures show up in the
merge request rather than only in the job log.

Both jobs verified locally: gate passes, 125 tests pass in 0.26 s.

Registering a runner on git.arconlab.ru is the one part that has to be
done on the server side.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 15:38:35 +03:00
LittleSam129
0ae04ae4a8 Close the remaining audit findings
Environment, README, gitignore and the source self-parsing in Lab041.

requirements.txt and requirements-hardware.txt pin the versions actually
verified today on Python 3.13.1, split so the project comes up on a
machine with no SDR: pyadi-iio and libiio are only needed from Lab042 on.

The gitignore contradiction is fixed in the direction that keeps the
project reproducible. data/raw/ was ignored while six files were tracked
anyway, and four of them are the source images Lab007, Lab009, Lab011 and
Lab012 read as input. Untracking them would have broken those labs, so
the rule now carries explicit exceptions instead. Recorded in the README:
lab026_rover_source.mp4 is genuinely absent and Lab026 and Lab027 cannot
run without it.

README replaced. It claimed the next task was to write the first protocol
module, 41 labs later. It now states what exists, that the transmitter
has never been switched on, how to install either environment, how to run
tests, the gate and a lab, and that rover/, ground_station/ and gnuradio/
are still empty. Added rule 9: every number in a report must come from
the current run, transferred values must name their source.

Lab041 no longer parses its own source to check that plot labels are in
Russian. That belonged to presentation, not to model correctness, and
turned the experiment into an analyser of its own text; ast, inspect and
re are no longer imported. Rerun: 180 combinations, 24/24 checks, five
CSV and seven PNG, unchanged.

125 tests pass, gate passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 15:33:14 +03:00
LittleSam129
92289bdf7c Cover control messages and session state; record the RF decision
Two more suites, 69 checks, bringing the fast tests to 125 in 0.3 s.

test_control_messages: roundtrip for all ten message types, a guard that
fails if a type is added without a test, wrong version, unknown type,
truncation, the control-state range rules including negative and
non-finite speed, and sequence comparison across the 32-bit wrap and the
ambiguous 2^31 boundary.

test_session_state: the safety properties that previously only ran inside
Lab041. Safe boot on both sides, status and authorization not being a
movement command, a new operator command being required, stale session,
boot and epoch identifiers, replay, sequence wrap, ambiguity, persisted
emergency intent surviving a restart, acknowledgement not clearing it,
ordinary commands not releasing the latch, idempotent reset, and reset
not restoring the previous command.

Two of these were literal zeros in the Lab041 report and measured
nothing: negative speed and speed above the limit are now genuinely
exercised against the encoder.

PROJECT_LOG entry 018 records the RF architecture decision: 200-250 MHz
with frequency hopping, a directional ground antenna and spread spectrum
for the command channel, with the rejected alternatives and why.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 15:29:47 +03:00
LittleSam129
2da80d0908 Add the first real test suite for protocol/
56 fast checks, 0.2 s, covering packet.py and the new bpsk_radio.py.

test_bpsk_radio exists mainly to prove the extraction changed nothing. It
loads the original function definitions out of Lab018, Lab019 and Lab023
in isolation, compiling only the wanted AST nodes so the labs' module
level file writing never runs, then compares outputs: bit conversion,
modulation, shaping filter, frame marker and the carrier estimator over
random phase and frequency offsets.

It also covers the frame functions written fresh for the module:
roundtrip at four payload sizes, size and type rejection, broken sync
word, truncation, carrying a real CRC32 packet, and a shaped-signal
search that recovers the packet through matched filtering.

test_packet covers roundtrip across all four message types and five
payload sizes, sync word, length arithmetic, sequence range, truncation,
oversized payload, and single-bit corruption at six positions, which is
the property CRC32 is there to provide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 14:38:12 +03:00
LittleSam129
c486039053 Split experiments from tests
The tests/ directory held 50 laboratory programs and no tests. They model
channels, run hundreds of repetitions and write CSV, PNG and reports;
calling that a test suite blocked introducing a real one, because any
pytest run would have collected the labs and re-executed every
experiment.

- move all 50 lab programs to experiments/ with git mv, preserving history
- rewrite the 38 cross-imports between labs from tests.labNNN to
  experiments.labNNN
- leave tests/ empty for actual fast checks of protocol/
- point quick_gate and the hook at the new layout and add experiments/ to
  the syntax sweep
- update the paths quoted in the Lab042 specification and the verifier
  agent definition

This also defuses the import-time work finding without touching 41 files:
the labs still create directories and write files on import, but nothing
imports them now except the gate, which does so deliberately.

Gate passes: syntax clean, protocol imports, 15 lab modules import, 2
functional suites run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 14:34:58 +03:00
LittleSam129
c9569164e0 Add protocol/bpsk_radio: reusable BPSK radio primitives
Lab042 cannot reuse the DSP from the earlier labs the way its
specification assumed. Three blockers turned up on inspection:

- importing lab018, lab019 or lab023 creates directories and writes .npy,
  .png and report files at module level, so importing them silently
  re-runs their experiments
- build_radio_frame() takes no arguments and encodes a fixed text message
- decode_radio_frame() returns a status string rather than the payload

So the verified primitives move into a module that is safe to import and
does no work of its own. Function bodies are carried over verbatim, and
the source lab is named above each one:

- from Lab023: bytes_to_bits, bits_to_bytes, bpsk_modulate,
  bpsk_demodulate, estimate_carrier_parameters, correct_phase_and_frequency
- from Lab019: build_frame_marker, find_radio_frame
- from Lab018: root_raised_cosine_taps

build_radio_frame and parse_radio_frame are written fresh here to carry
arbitrary payloads; the on-air structure is unchanged, still preamble,
sync word, length, packet.

Verified against the originals by loading their function definitions in
isolation and comparing outputs: 31 comparisons, no divergence, including
the CFO estimator over random phase and frequency offsets.

End-to-end check with no hardware: a 5828-byte JPEG through fragments,
packets, frames, shaping at 128 samples per symbol and matched filtering
recovers 12 of 12 fragments and reassembles byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 14:16:32 +03:00
LittleSam129
1e4adc0a98 Lab042: correct the sample rate against the real device
Queried the Pluto over IIO before writing any code. Three findings, one
of which invalidated the specification.

The AD9361 will not sample below 2 083 333 Hz, so the 640 kHz taken from
Lab018 is unreachable by a factor of three. Going lower needs the on-chip
FIR decimator, which would add an untested mechanism to the very first
transmit. Instead raise oversampling from 32 to 128 samples per symbol:
the symbol rate stays at 20 000, rolloff, preamble, sync word and guard
are untouched, and the rate becomes 2 560 000 Hz. Only the device
interface changes, not the signal.

Consequence recorded: the shaping filter grows to 1281 taps, so the
convolution has to go through scipy.signal.fftconvolve.

Also from the device:
- it reports as PlutoSDR Rev.C (Z7010-AD9361) with a single RX and a
  single TX buffer channel, so adi.Pluto is right and the adi.ad9361
  fallback is unnecessary
- the chip is a full AD9361: RX tunes 70 MHz to 6 GHz, TX from 46.9 MHz,
  so the sub-350 MHz target band is available without a firmware hack.
  That closes the open question flagged for Lab044
- firmware is v0.32, left alone for now

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 14:06:44 +03:00