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.
This commit is contained in:
LittleSam129
2026-08-17 11:50:04 +03:00
parent 13f183f7c7
commit fce14f038e
11 changed files with 441 additions and 49 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,11 @@
index,packet_bytes,frame_bits,correlation_score,sample_phase,carrier_offset_hz,carrier_coherence,cfo_correction_applied,error_vector_magnitude,frame_found,header_parsed,crc_valid,fragment_recovered
0,536,4384,0.9995083676316812,15,-0.8161804653898344,0.9999521460387033,False,0.013939054015288657,True,True,True,True
1,536,4384,0.9994247234007716,49,0.034058565997241885,0.9999449532988285,False,0.007533311365459869,True,True,True,True
2,536,4384,0.9995067920215722,121,-0.4024654150100275,0.9999698768639075,False,0.007802389734928202,True,True,True,True
3,536,4384,0.9995973624847003,79,0.05360429263743072,0.9999563681724133,False,0.008067411996806204,True,True,True,True
4,536,4384,0.9995573203179389,37,-0.7726047795897841,0.999951969069221,False,0.008830283803028178,True,True,True,True
5,536,4384,0.9994972026358666,35,0.14518673176235097,0.9999740428064059,False,0.007829934600060385,True,True,True,True
6,536,4384,0.9994993201100448,33,-0.6575975246787777,0.9999581020309138,False,0.008192298206454165,True,True,True,True
7,536,4384,0.9995337736352732,97,-0.32146831121434616,0.9999654759558527,False,0.00773812855638587,True,True,True,True
8,536,4384,0.9995377136427829,90,-0.32692415971800415,0.9999706666958124,False,0.008197030505111991,True,True,True,True
9,172,1472,0.9994478378475372,53,-0.6175162038574342,0.9999730687021144,False,0.011683449034017782,True,True,True,True
1 index packet_bytes frame_bits correlation_score sample_phase carrier_offset_hz carrier_coherence cfo_correction_applied error_vector_magnitude frame_found header_parsed crc_valid fragment_recovered
2 0 536 4384 0.9995083676316812 15 -0.8161804653898344 0.9999521460387033 False 0.013939054015288657 True True True True
3 1 536 4384 0.9994247234007716 49 0.034058565997241885 0.9999449532988285 False 0.007533311365459869 True True True True
4 2 536 4384 0.9995067920215722 121 -0.4024654150100275 0.9999698768639075 False 0.007802389734928202 True True True True
5 3 536 4384 0.9995973624847003 79 0.05360429263743072 0.9999563681724133 False 0.008067411996806204 True True True True
6 4 536 4384 0.9995573203179389 37 -0.7726047795897841 0.999951969069221 False 0.008830283803028178 True True True True
7 5 536 4384 0.9994972026358666 35 0.14518673176235097 0.9999740428064059 False 0.007829934600060385 True True True True
8 6 536 4384 0.9994993201100448 33 -0.6575975246787777 0.9999581020309138 False 0.008192298206454165 True True True True
9 7 536 4384 0.9995337736352732 97 -0.32146831121434616 0.9999654759558527 False 0.00773812855638587 True True True True
10 8 536 4384 0.9995377136427829 90 -0.32692415971800415 0.9999706666958124 False 0.008197030505111991 True True True True
11 9 172 1472 0.9994478378475372 53 -0.6175162038574342 0.9999730687021144 False 0.011683449034017782 True True True True

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -0,0 +1,87 @@
Lab042 — передача JPEG через кабельную петлю на одном PlutoSDR
Режим прогона
- Канал: air.
- Трасса: две антенны по воздуху, расстояние 60 см, аттенюатора нет.
- Несущая 225.000 МГц — внутри целевого диапазона 200-250 МГц, закреплённого Записью 018.
- Усиление передатчика -10.0 дБ, приёмника 50.0 дБ, режим ручной.
- Антенны разнесены на 60 см. Аттенюатора в цепи нет: приёмник защищён ослаблением на трассе.
- Использованы штыри, рассчитанные на 108 МГц. На рабочей частоте они работают как полуволновые, согласование плохое, но для этой дистанции достаточное.
- Уровни в эфирной трассе не контролируются так же строго, как в кабельной: возможны переотражения от предметов в помещении. Прогон служит проверкой тракта устройства, а не измерением.
Параметры тракта
- Символьная скорость 20000 симв/с, отсчётов на символ 128.
- Частота дискретизации 2560000 Гц. Минимум устройства 2083333 Гц.
- Скругление 0.35, длина фильтра 10 символов, защитный интервал 16 символов.
- Значения перенесены из Lab018 без изменений, кроме передискретизации: она поднята с 32 до 128,
потому что частота 640 000 Гц из Lab018 ниже предела устройства втрое.
Передаваемое изображение
- Источник data/raw/lab011_scene.jpg, 320x180, качество 30.
- Размер JPEG 4756 байт.
- Фрагментов 10 по 512 байт данных.
- Пакет протокола от 172 до 536 байт, радиокадр от 1472 до 4384 бит.
- Последний фрагмент изображения короче остальных, поэтому его кадр меньше. Размер каждого кадра приведён в lab042_frames.csv.
- Всего передано 5292544 отсчётов, что соответствует 2.067 с эфира.
Результат приёма
- Кадров найдено 10 из 10.
- Кадров с несошедшейся CRC32: 0.
- Фрагментов восстановлено 10, доля 100.000%.
- Изображение собрано: да.
- Совпадает с переданным побайтово: да.
- Размер принятого изображения 4756 байт.
Качество сигнала
- Средняя нормированная корреляция при поиске кадра 0.99951.
- Средняя оценка ухода несущей -0.368 Гц.
- Средняя величина вектора ошибки 0.898%.
Скорость
- Длительность сигнала в эфире 2.067 с.
- Канальная скорость 18.404 кбит/с — полезные байты, делённые на время эфира.
- Полное время работы программы 16.638 с, что даёт 2.287 кбит/с.
- В программном режиме вторая величина отражает быстродействие вычислений, а не пропускную способность канала.
Кадры
номер | корреляция | фаза | уход несущей, Гц | достоверность оценки | вектор ошибки, % | найден | заголовок | CRC32 | фрагмент
0 | 0.99951 | 15 | -0.816 | 0.99995 | 1.394 | да | да | да | да
1 | 0.99942 | 49 | 0.034 | 0.99994 | 0.753 | да | да | да | да
2 | 0.99951 | 121 | -0.402 | 0.99997 | 0.780 | да | да | да | да
3 | 0.99960 | 79 | 0.054 | 0.99996 | 0.807 | да | да | да | да
4 | 0.99956 | 37 | -0.773 | 0.99995 | 0.883 | да | да | да | да
5 | 0.99950 | 35 | 0.145 | 0.99997 | 0.783 | да | да | да | да
6 | 0.99950 | 33 | -0.658 | 0.99996 | 0.819 | да | да | да | да
7 | 0.99953 | 97 | -0.321 | 0.99997 | 0.774 | да | да | да | да
8 | 0.99954 | 90 | -0.327 | 0.99997 | 0.820 | да | да | да | да
9 | 0.99945 | 53 | -0.618 | 0.99997 | 1.168 | да | да | да | да
Функциональные проверки
- ПРОЙДЕНО — 01. Размер подготовленного JPEG: JPEG занимает 4756 байт при пределе 5000, качество 30
- ПРОЙДЕНО — 02. Разбиение и сборка изображения: 10 фрагментов собираются в исходные байты
- ПРОЙДЕНО — 03. Кадр несёт пакет с CRC32: все 10 кадров несут пакет с сошедшейся CRC32
- ПРОЙДЕНО — 04. Длина сформированного кадра: кадр занимает 566528 отсчётов, что совпадает с расчётом
- ПРОЙДЕНО — 05. Масштабирование в целые отсчёты: пиковая амплитуда 1601 не превышает предела 16384
- ПРОЙДЕНО — 06. Программная петля восстанавливает изображение: программная петля вернула 4756 байт, идентичных исходным
- ПРОЙДЕНО — 07. Обнаружение искажённого кадра: одиночная битовая ошибка в данных кадра обнаружена контрольной суммой
- ПРОЙДЕНО — 08. Потеря фрагмента: потеря фрагмента не портит остальные, сборка требует всех
- ПРОЙДЕНО — 09. Достижимость частоты дискретизации: 2560000 Гц не ниже предела устройства 2083333 Гц
- ПРОЙДЕНО — 10. Настройки устройства в пределах: усиления, полоса 200000 Гц и несущая 435000000 Гц внутри допустимых пределов, сигнал занимает 27000 Гц
- ПРОЙДЕНО — 11. Потолок усиления в эфирном режиме: предел зависит от расстояния: -19.5 дБ на 5 см против 6.0 дБ на 200 см
Ограничения
- Передатчик и приёмник принадлежат одному устройству и работают от общего опорного
генератора, поэтому уход несущей между ними мал и не характеризует реальную линию.
- Повторная передача потерянных фрагментов не выполняется: потери фиксируются, но не восстанавливаются.
- Исправление стираний не подключалось.
- Разведение на два устройства и выход в эфир — предмет Lab043 и Lab044.
Созданные файлы
- data/processed/lab042/lab042_received.jpg
- data/processed/lab042/lab042_summary.csv
- data/processed/lab042/lab042_frames.csv
- data/processed/lab042/lab042_report.txt
- data/processed/lab042/lab042_constellation.png
- data/processed/lab042/lab042_spectrum.png
- data/processed/lab042/lab042_frame_correlation.png
- data/processed/lab042/lab042_fragment_delivery.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -0,0 +1,2 @@
loopback_mode,path_description,antenna_distance_cm,source_image,image_width,image_height,jpeg_quality,jpeg_bytes,fragment_data_size,fragment_count,packet_bytes_max,packet_bytes_min,frame_bits_max,frame_bits_min,symbol_rate,samples_per_symbol,sample_rate_hz,carrier_hz,transmitted_samples,air_time_seconds,frames_received,frames_failed_crc,fragments_recovered,fragment_delivery_fraction,image_reassembled,image_identical,received_image_bytes,transfer_seconds,channel_bitrate_kbps,wall_clock_bitrate_kbps,mean_correlation_score,mean_carrier_offset_hz,mean_error_vector_magnitude,tx_gain_db,rx_gain_db,attenuator_db
air,"две антенны по воздуху, расстояние 60 см, аттенюатора нет",60.0,data/raw/lab011_scene.jpg,320,180,30,4756,512,10,536,172,4384,1472,20000,128,2560000,225000000,5292544,2.0674,10,0,10,1.0,True,True,4756,16.637855200009653,18.40379220276676,2.286833221146072,0.9995110413728169,-0.3681907269061185,0.00898132918175413,-10.0,50.0,0.0
1 loopback_mode path_description antenna_distance_cm source_image image_width image_height jpeg_quality jpeg_bytes fragment_data_size fragment_count packet_bytes_max packet_bytes_min frame_bits_max frame_bits_min symbol_rate samples_per_symbol sample_rate_hz carrier_hz transmitted_samples air_time_seconds frames_received frames_failed_crc fragments_recovered fragment_delivery_fraction image_reassembled image_identical received_image_bytes transfer_seconds channel_bitrate_kbps wall_clock_bitrate_kbps mean_correlation_score mean_carrier_offset_hz mean_error_vector_magnitude tx_gain_db rx_gain_db attenuator_db
2 air две антенны по воздуху, расстояние 60 см, аттенюатора нет 60.0 data/raw/lab011_scene.jpg 320 180 30 4756 512 10 536 172 4384 1472 20000 128 2560000 225000000 5292544 2.0674 10 0 10 1.0 True True 4756 16.637855200009653 18.40379220276676 2.286833221146072 0.9995110413728169 -0.3681907269061185 0.00898132918175413 -10.0 50.0 0.0