Compare commits

..

3 Commits

Author SHA1 Message Date
LittleSam129
22c2eeab8c Lab035: add proactive video frame admission 2026-08-03 15:42:16 +03:00
LittleSam129
b63e36abdb Lab034: evaluate stale video frame dropping 2026-08-03 13:17:55 +03:00
LittleSam129
21751f41dd Lab033: add priority link scheduler 2026-08-03 12:21:49 +03:00
41 changed files with 4391 additions and 0 deletions

View File

@@ -163,3 +163,79 @@ git version 2.51.1.windows.1
- Режим 8+3 не принят из-за отсутствия устойчивого преимущества.
- Подтверждено, что продолжительные помехи нельзя компенсировать только увеличением числа проверочных пакетов.
- Следующий этап посвящён совместной передаче команд, телеметрии и видео с приоритетным обслуживанием.
---
# Запись 008
## Дата
3 августа 2026 года
## Тема
Завершение Lab033: общий пакет канала и приоритетное обслуживание.
## Выполнено
- Добавлен общий 32-байтный заголовок канала для команд, телеметрии и видео.
- Общая предложенная нагрузка составляет 258,214 кбит/с.
- Подтверждено, что FIFO не обеспечивает требуемую задержку команд.
- Выбран строгий приоритет с заменой устаревших состояний.
- Аварийная команда имеет наивысший приоритет.
- Скорость 260 кбит/с практически полностью загружена.
- При 230 кбит/с видеоданные накапливаются и устаревают.
- Итоговое восстановление всех кадров после освобождения очереди не означает работу видео в реальном времени.
- Расчёты и графики выполнены с Matplotlib 3.11.1.
- Следующий этап посвящён отбрасыванию устаревших видеокадров.
---
# Запись 009
## Дата
3 августа 2026 года
## Тема
Завершение Lab034: реактивное отбрасывание устаревших видеокадров.
## Выполнено
- Сравнены непрерывные FEC-блоки и блоки, выровненные по составным кадрам.
- Выравнивание увеличивает видеопоток на 5,975 кбит/с, или на 2,633%.
- При 300 кбит/с удаление кадров не требуется.
- При 260 кбит/с ограничения 1000 и 1500 мс не срабатывают, а ограничение 500 мс уже чрезмерно.
- При 230 кбит/с реактивное удаление устаревших кадров уменьшает очередь, но резко снижает частоту обновления.
- Ограничение 500 мс оставляет только 3 из 63 кадров.
- Ограничение 1500 мс оставляет 27 из 63 кадров.
- Подтверждено, что короткая очередь сама по себе не означает свежее изображение.
- Текущая реактивная политика не принята.
- Следующий этап посвящён упреждающему допуску кадров и обслуживанию видео целыми кадрами.
---
# Запись 010
## Дата
3 августа 2026 года
## Тема
Завершение Lab035: упреждающий допуск и обслуживание видео целыми составными кадрами.
## Выполнено
- Lab035 исследует упреждающий допуск и обслуживание видео целыми составными кадрами.
- Начатый кадр всегда передаётся полностью.
- Команды и телеметрия могут передаваться между его пакетами.
- Реактивное удаление Lab034 окончательно не принято.
- Основная политика — хранение только самого свежего не начатого кадра.
- При 230 кбит/с опубликовано 56 из 63 кадров с частотой около 2,6 кадра/с.
- Бесполезно переданных видеобайтов нет.
- Очередь из двух кадров увеличивает возраст изображения.
- Прогноз 1000 мс не дал преимущества над более простой политикой.
- Прогноз 500 мс чрезмерно снижает частоту обновления.
- Следующая лабораторная проверяет долговременную устойчивость и изменение пропускной способности канала.

View File

@@ -0,0 +1,37 @@
channel_kbps,scheduler,traffic_class,offered_load_kbps,created_packets,transmitted_packets,replaced_packets,remaining_at_source_end_packets,mean_start_delay_ms,p95_start_delay_ms,max_start_delay_ms,mean_total_delay_ms,p95_total_delay_ms,max_total_delay_ms,deadline_misses,deadline_miss_fraction,mean_blocking_delay_ms,max_blocking_delay_ms
300.0,fifo,emergency,0.02465489566613162,1,1,0,0,0.0,0.0,0.0,1.7066666666671892,1.7066666666671892,1.7066666666671892,0,0.0,0.0,0.0
300.0,fifo,control,10.256436597110755,416,416,0,2,104.3649927884586,236.92691666666235,294.47966666665957,106.0716594551255,238.6335833333294,296.1863333333259,203,0.4879807692307692,5.879864583331442,16.212999999996036
300.0,fifo,telemetry,7.692327447833066,208,208,0,1,98.47181249999751,231.12033333332866,294.26699999999425,101.03181249999754,233.68033333332914,296.8269999999933,0,0.0,5.797325320511048,16.212999999996036
300.0,fifo,video,240.24038523274479,1078,1078,0,15,128.3044211502762,254.33199999999601,309.6799999999931,143.7311125541102,264.7599999999962,325.89333333332604,0,0.0,0.0026771799628383768,0.16033333332998723
300.0,strict_priority,emergency,0.02465489566613162,1,1,0,0,0.0,0.0,0.0,1.7066666666671892,1.7066666666671892,1.7066666666671892,0,0.0,0.0,0.0
300.0,strict_priority,control,10.256436597110755,416,416,0,0,7.733262019229404,15.759666666666755,16.106333333334,9.439928685896334,17.46633333333346,17.813000000000745,0,0.0,7.7291594551268386,16.106333333334
300.0,strict_priority,telemetry,7.692327447833066,208,208,0,0,9.864120192306657,17.546883333334474,17.813000000000745,12.424120192306697,20.10688333333437,20.37300000000064,0,0.0,8.149248397434599,16.106333333334
300.0,strict_priority,video,240.24038523274479,1078,1078,0,15,135.37173098330103,269.12799999999464,323.36000000000143,150.79842238713505,279.70533333332844,333.4933333333332,0,0.0,0.0026771799628976987,0.16033333333353994
300.0,latest_state,emergency,0.02465489566613162,1,1,0,0,0.0,0.0,0.0,1.7066666666671892,1.7066666666671892,1.7066666666671892,0,0.0,0.0,0.0
300.0,latest_state,control,10.256436597110755,416,416,0,0,7.733262019229404,15.759666666666755,16.106333333334,9.439928685896334,17.46633333333346,17.813000000000745,0,0.0,7.7291594551268386,16.106333333334
300.0,latest_state,telemetry,7.692327447833066,208,208,0,0,9.864120192306657,17.546883333334474,17.813000000000745,12.424120192306697,20.10688333333437,20.37300000000064,0,0.0,8.149248397434599,16.106333333334
300.0,latest_state,video,240.24038523274479,1078,1078,0,15,135.37173098330103,269.12799999999464,323.36000000000143,150.79842238713505,279.70533333332844,333.4933333333332,0,0.0,0.0026771799628976987,0.16033333333353994
260.0,fifo,emergency,0.02465489566613162,1,1,0,0,0.0,0.0,0.0,1.969230769230279,1.969230769230279,1.969230769230279,0,0.0,0.0,0.0
260.0,fifo,control,10.256436597110755,416,416,0,7,167.10386353552764,331.0900769231244,458.97469230786925,169.07309430475814,333.0593076923547,460.9439230770995,298,0.7163461538461539,7.857339866886274,18.615384615390695
260.0,fifo,telemetry,7.692327447833066,208,208,0,4,160.36350517753885,328.8285384616073,460.9439230770995,163.31735133138503,331.7823846154539,463.8977692309467,0,0.0,7.732144230791379,18.615384615390695
260.0,fifo,video,240.24038523274479,1078,1078,0,21,175.94024482662746,343.1754384616482,473.6000000001752,193.74027336951343,357.6154384615683,492.3076923078682,0,0.0,3.3139568289080654,18.257076923131166
260.0,strict_priority,emergency,0.02465489566613162,1,1,0,0,0.0,0.0,0.0,1.969230769230279,1.969230769230279,1.969230769230279,0,0.0,0.0,0.0
260.0,strict_priority,control,10.256436597110755,416,416,0,0,8.313035133156095,16.784365384612144,18.69776923077815,10.282265902386632,18.753596153843198,20.66700000000843,0,0.0,8.308301405345444,18.69776923077815
260.0,strict_priority,telemetry,7.692327447833066,208,208,0,0,10.909659023688215,19.493946153856623,20.66700000000843,13.863505177534366,22.44779230770204,23.620846153853847,0,0.0,8.930960798836377,18.69776923077815
260.0,strict_priority,video,240.24038523274479,1078,1078,0,23,187.65972534610552,365.73226153856297,480.49230769247583,205.4597538889915,380.5630769230985,499.20000000016887,0,0.0,3.9965313971947523,18.25707692318801
260.0,latest_state,emergency,0.02465489566613162,1,1,0,0,0.0,0.0,0.0,1.969230769230279,1.969230769230279,1.969230769230279,0,0.0,0.0,0.0
260.0,latest_state,control,10.256436597110755,416,416,0,0,8.313035133156095,16.784365384612144,18.69776923077815,10.282265902386632,18.753596153843198,20.66700000000843,0,0.0,8.308301405345444,18.69776923077815
260.0,latest_state,telemetry,7.692327447833066,208,208,0,0,10.909659023688215,19.493946153856623,20.66700000000843,13.863505177534366,22.44779230770204,23.620846153853847,0,0.0,8.930960798836377,18.69776923077815
260.0,latest_state,video,240.24038523274479,1078,1078,0,23,187.65972534610552,365.73226153856297,480.49230769247583,205.4597538889915,380.5630769230985,499.20000000016887,0,0.0,3.9965313971947523,18.25707692318801
230.0,fifo,emergency,0.02465489566613162,1,1,0,0,878.8869565216775,878.8869565216775,878.8869565216775,881.1130434781998,881.1130434781998,881.1130434781998,1,1.0,0.4173913042819777,0.4173913042819777
230.0,fifo,control,10.256436597110755,416,416,0,42,1172.0617892976043,2238.3086956520533,2606.2956521737738,1174.2878762541263,2240.5347826085754,2608.5217391302963,414,0.9951923076923077,9.870735785906158,21.13043478255605
230.0,fifo,telemetry,7.692327447833066,208,208,0,21,1161.6391304347285,2222.267826086846,2608.5217391302963,1164.978260869511,2225.6069565216276,2611.860869565078,177,0.8509615384615384,9.380434782561796,21.043478260802218
230.0,fifo,video,240.24038523274479,1078,1078,0,118,1186.612664354224,2267.033969565095,2618.4808260868167,1206.7344357505297,2288.1817956520513,2639.628652173773,0,0.0,8.20556586266141,21.136565217338088
230.0,strict_priority,emergency,0.02465489566613162,1,1,0,0,9.32173913036749,9.32173913036749,9.32173913036749,11.547826086889756,11.547826086889756,11.547826086889756,0,0.0,9.32173913036749,9.32173913036749
230.0,strict_priority,control,10.256436597110755,416,416,0,1,10.02424749162144,19.62173913038057,21.113043478251825,12.250334448143445,21.84782608690261,23.33913043477409,0,0.0,10.018896321052877,21.113043478251825
230.0,strict_priority,telemetry,7.692327447833066,208,208,0,0,12.175083612023139,21.772173913030322,23.33913043477409,15.51421404680579,25.111304347813142,26.678260869555714,0,0.0,9.938294314364006,21.113043478251825
230.0,strict_priority,video,240.24038523274479,1078,1078,0,126,1273.2989271597767,2364.9321739130005,2626.272130434721,1293.4206985560818,2379.542608695629,2647.4199565216772,0,0.0,9.628813422585635,19.802565217331036
230.0,latest_state,emergency,0.02465489566613162,1,1,0,0,9.32173913036749,9.32173913036749,9.32173913036749,11.547826086889756,11.547826086889756,11.547826086889756,0,0.0,9.32173913036749,9.32173913036749
230.0,latest_state,control,10.256436597110755,416,416,0,1,10.02424749162144,19.62173913038057,21.113043478251825,12.250334448143445,21.84782608690261,23.33913043477409,0,0.0,10.018896321052877,21.113043478251825
230.0,latest_state,telemetry,7.692327447833066,208,208,0,0,12.175083612023139,21.772173913030322,23.33913043477409,15.51421404680579,25.111304347813142,26.678260869555714,0,0.0,9.938294314364006,21.113043478251825
230.0,latest_state,video,240.24038523274479,1078,1078,0,126,1273.2989271597767,2364.9321739130005,2626.272130434721,1293.4206985560818,2379.542608695629,2647.4199565216772,0,0.0,9.628813422585635,19.802565217331036
1 channel_kbps scheduler traffic_class offered_load_kbps created_packets transmitted_packets replaced_packets remaining_at_source_end_packets mean_start_delay_ms p95_start_delay_ms max_start_delay_ms mean_total_delay_ms p95_total_delay_ms max_total_delay_ms deadline_misses deadline_miss_fraction mean_blocking_delay_ms max_blocking_delay_ms
2 300.0 fifo emergency 0.02465489566613162 1 1 0 0 0.0 0.0 0.0 1.7066666666671892 1.7066666666671892 1.7066666666671892 0 0.0 0.0 0.0
3 300.0 fifo control 10.256436597110755 416 416 0 2 104.3649927884586 236.92691666666235 294.47966666665957 106.0716594551255 238.6335833333294 296.1863333333259 203 0.4879807692307692 5.879864583331442 16.212999999996036
4 300.0 fifo telemetry 7.692327447833066 208 208 0 1 98.47181249999751 231.12033333332866 294.26699999999425 101.03181249999754 233.68033333332914 296.8269999999933 0 0.0 5.797325320511048 16.212999999996036
5 300.0 fifo video 240.24038523274479 1078 1078 0 15 128.3044211502762 254.33199999999601 309.6799999999931 143.7311125541102 264.7599999999962 325.89333333332604 0 0.0 0.0026771799628383768 0.16033333332998723
6 300.0 strict_priority emergency 0.02465489566613162 1 1 0 0 0.0 0.0 0.0 1.7066666666671892 1.7066666666671892 1.7066666666671892 0 0.0 0.0 0.0
7 300.0 strict_priority control 10.256436597110755 416 416 0 0 7.733262019229404 15.759666666666755 16.106333333334 9.439928685896334 17.46633333333346 17.813000000000745 0 0.0 7.7291594551268386 16.106333333334
8 300.0 strict_priority telemetry 7.692327447833066 208 208 0 0 9.864120192306657 17.546883333334474 17.813000000000745 12.424120192306697 20.10688333333437 20.37300000000064 0 0.0 8.149248397434599 16.106333333334
9 300.0 strict_priority video 240.24038523274479 1078 1078 0 15 135.37173098330103 269.12799999999464 323.36000000000143 150.79842238713505 279.70533333332844 333.4933333333332 0 0.0 0.0026771799628976987 0.16033333333353994
10 300.0 latest_state emergency 0.02465489566613162 1 1 0 0 0.0 0.0 0.0 1.7066666666671892 1.7066666666671892 1.7066666666671892 0 0.0 0.0 0.0
11 300.0 latest_state control 10.256436597110755 416 416 0 0 7.733262019229404 15.759666666666755 16.106333333334 9.439928685896334 17.46633333333346 17.813000000000745 0 0.0 7.7291594551268386 16.106333333334
12 300.0 latest_state telemetry 7.692327447833066 208 208 0 0 9.864120192306657 17.546883333334474 17.813000000000745 12.424120192306697 20.10688333333437 20.37300000000064 0 0.0 8.149248397434599 16.106333333334
13 300.0 latest_state video 240.24038523274479 1078 1078 0 15 135.37173098330103 269.12799999999464 323.36000000000143 150.79842238713505 279.70533333332844 333.4933333333332 0 0.0 0.0026771799628976987 0.16033333333353994
14 260.0 fifo emergency 0.02465489566613162 1 1 0 0 0.0 0.0 0.0 1.969230769230279 1.969230769230279 1.969230769230279 0 0.0 0.0 0.0
15 260.0 fifo control 10.256436597110755 416 416 0 7 167.10386353552764 331.0900769231244 458.97469230786925 169.07309430475814 333.0593076923547 460.9439230770995 298 0.7163461538461539 7.857339866886274 18.615384615390695
16 260.0 fifo telemetry 7.692327447833066 208 208 0 4 160.36350517753885 328.8285384616073 460.9439230770995 163.31735133138503 331.7823846154539 463.8977692309467 0 0.0 7.732144230791379 18.615384615390695
17 260.0 fifo video 240.24038523274479 1078 1078 0 21 175.94024482662746 343.1754384616482 473.6000000001752 193.74027336951343 357.6154384615683 492.3076923078682 0 0.0 3.3139568289080654 18.257076923131166
18 260.0 strict_priority emergency 0.02465489566613162 1 1 0 0 0.0 0.0 0.0 1.969230769230279 1.969230769230279 1.969230769230279 0 0.0 0.0 0.0
19 260.0 strict_priority control 10.256436597110755 416 416 0 0 8.313035133156095 16.784365384612144 18.69776923077815 10.282265902386632 18.753596153843198 20.66700000000843 0 0.0 8.308301405345444 18.69776923077815
20 260.0 strict_priority telemetry 7.692327447833066 208 208 0 0 10.909659023688215 19.493946153856623 20.66700000000843 13.863505177534366 22.44779230770204 23.620846153853847 0 0.0 8.930960798836377 18.69776923077815
21 260.0 strict_priority video 240.24038523274479 1078 1078 0 23 187.65972534610552 365.73226153856297 480.49230769247583 205.4597538889915 380.5630769230985 499.20000000016887 0 0.0 3.9965313971947523 18.25707692318801
22 260.0 latest_state emergency 0.02465489566613162 1 1 0 0 0.0 0.0 0.0 1.969230769230279 1.969230769230279 1.969230769230279 0 0.0 0.0 0.0
23 260.0 latest_state control 10.256436597110755 416 416 0 0 8.313035133156095 16.784365384612144 18.69776923077815 10.282265902386632 18.753596153843198 20.66700000000843 0 0.0 8.308301405345444 18.69776923077815
24 260.0 latest_state telemetry 7.692327447833066 208 208 0 0 10.909659023688215 19.493946153856623 20.66700000000843 13.863505177534366 22.44779230770204 23.620846153853847 0 0.0 8.930960798836377 18.69776923077815
25 260.0 latest_state video 240.24038523274479 1078 1078 0 23 187.65972534610552 365.73226153856297 480.49230769247583 205.4597538889915 380.5630769230985 499.20000000016887 0 0.0 3.9965313971947523 18.25707692318801
26 230.0 fifo emergency 0.02465489566613162 1 1 0 0 878.8869565216775 878.8869565216775 878.8869565216775 881.1130434781998 881.1130434781998 881.1130434781998 1 1.0 0.4173913042819777 0.4173913042819777
27 230.0 fifo control 10.256436597110755 416 416 0 42 1172.0617892976043 2238.3086956520533 2606.2956521737738 1174.2878762541263 2240.5347826085754 2608.5217391302963 414 0.9951923076923077 9.870735785906158 21.13043478255605
28 230.0 fifo telemetry 7.692327447833066 208 208 0 21 1161.6391304347285 2222.267826086846 2608.5217391302963 1164.978260869511 2225.6069565216276 2611.860869565078 177 0.8509615384615384 9.380434782561796 21.043478260802218
29 230.0 fifo video 240.24038523274479 1078 1078 0 118 1186.612664354224 2267.033969565095 2618.4808260868167 1206.7344357505297 2288.1817956520513 2639.628652173773 0 0.0 8.20556586266141 21.136565217338088
30 230.0 strict_priority emergency 0.02465489566613162 1 1 0 0 9.32173913036749 9.32173913036749 9.32173913036749 11.547826086889756 11.547826086889756 11.547826086889756 0 0.0 9.32173913036749 9.32173913036749
31 230.0 strict_priority control 10.256436597110755 416 416 0 1 10.02424749162144 19.62173913038057 21.113043478251825 12.250334448143445 21.84782608690261 23.33913043477409 0 0.0 10.018896321052877 21.113043478251825
32 230.0 strict_priority telemetry 7.692327447833066 208 208 0 0 12.175083612023139 21.772173913030322 23.33913043477409 15.51421404680579 25.111304347813142 26.678260869555714 0 0.0 9.938294314364006 21.113043478251825
33 230.0 strict_priority video 240.24038523274479 1078 1078 0 126 1273.2989271597767 2364.9321739130005 2626.272130434721 1293.4206985560818 2379.542608695629 2647.4199565216772 0 0.0 9.628813422585635 19.802565217331036
34 230.0 latest_state emergency 0.02465489566613162 1 1 0 0 9.32173913036749 9.32173913036749 9.32173913036749 11.547826086889756 11.547826086889756 11.547826086889756 0 0.0 9.32173913036749 9.32173913036749
35 230.0 latest_state control 10.256436597110755 416 416 0 1 10.02424749162144 19.62173913038057 21.113043478251825 12.250334448143445 21.84782608690261 23.33913043477409 0 0.0 10.018896321052877 21.113043478251825
36 230.0 latest_state telemetry 7.692327447833066 208 208 0 0 12.175083612023139 21.772173913030322 23.33913043477409 15.51421404680579 25.111304347813142 26.678260869555714 0 0.0 9.938294314364006 21.113043478251825
37 230.0 latest_state video 240.24038523274479 1078 1078 0 126 1273.2989271597767 2364.9321739130005 2626.272130434721 1293.4206985560818 2379.542608695629 2647.4199565216772 0 0.0 9.628813422585635 19.802565217331036

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,165 @@
Lab033. Общий пакет канала и приоритетное обслуживание
1. Git и конфигурация
- Git-корень: C:/Users/user/Desktop/projects/SDR_Rover.
- Ветка: main; HEAD: 3dbb7afa8332b49535d867f1eabca7bdd2b7cdde.
- Исходное видео: 20.766667 с, 63 составных кадров, 3 кадра/с.
- BASE 240x135 grayscale JPEG Q23; ROI 320x180 grayscale JPEG Q33.
- Lab028: payload 512 байт; Lab030: FEC 12+3; Lab031: D=1.
- Фактический внешний видеопоток до Lab033: 226.951 кбит/с; с общим заголовком: 240.240 кбит/с.
- Аварийная команда: 0.024655; обычные команды: 10.256; телеметрия: 7.692 кбит/с.
- Общая предложенная нагрузка: 258.214 кбит/с; суммарная служебная доля: 34.923%.
2. Формат общего заголовка
- struct: !4sBBBBHIQHII; размер: 32 байта; network byte order, неявного padding нет.
- Смещения: magic[4]@0, version:u8@4, traffic_class:u8@5, direction:u8@6, flags:u8@7, stream_id:u16@8, sequence_number:u32@10, generation_time_us:u64@14, deadline_ms:u16@22, payload_length:u32@24, packet_crc32:u32@28.
- traffic_class: 1=EMERGENCY, 2=CONTROL, 3=TELEMETRY, 4=VIDEO; direction: 1=GROUND_TO_ROVER, 2=ROVER_TO_GROUND; flags в версии 1 равен нулю.
- CRC32 защищает заголовок с нулевым packet_crc32 и всю полезную нагрузку.
3. Девять сочетаний скорости и планировщика
speed | scheduler | offered/capacity | control P95 ms | emergency ms | telemetry P95 ms | video P95 ms | queue@end packets | drain extra s | final video
300 | FIFO | 0.8607 | 238.634 | 1.707 | 233.680 | 304.579 | 18 | 0.231867 | 1.000000
300 | Строгий приоритет | 0.8607 | 17.466 | 1.707 | 20.107 | 322.072 | 15 | 0.231867 | 1.000000
300 | Приоритет + замена | 0.8607 | 17.466 | 1.707 | 20.107 | 322.072 | 15 | 0.231867 | 1.000000
260 | FIFO | 0.9931 | 333.059 | 1.969 | 331.782 | 420.025 | 32 | 0.399200 | 1.000000
260 | Строгий приоритет | 0.9931 | 18.754 | 1.969 | 22.448 | 445.846 | 23 | 0.399200 | 1.000000
260 | Приоритет + замена | 0.9931 | 18.754 | 1.969 | 22.448 | 445.846 | 23 | 0.399200 | 1.000000
230 | FIFO | 1.1227 | 2240.535 | 881.113 | 2225.607 | 2417.731 | 181 | 2.547420 | 1.000000
230 | Строгий приоритет | 1.1227 | 21.848 | 11.548 | 25.111 | 2505.995 | 127 | 2.547420 | 1.000000
230 | Приоритет + замена | 1.1227 | 21.848 | 11.548 | 25.111 | 2505.995 | 127 | 2.547420 | 1.000000
4. Подробные показатели
[300 кбит/с; FIFO]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 10.478/22 пакетов и 4452.4/12221 байт.
- На конце видео: 18 пакетов, 8695.0 байт; полное освобождение 20.998534 с, дополнительно 0.231867 с.
- Команды: mean/P95/max 106.072/238.634/296.186 мс; превышений 100 мс 203; промежутков >100 мс 63; max gap 344.480 мс; заменено 0.
- Аварийная команда: start/total 0.000/1.707 мс; deadline 50 мс соблюдён; блокировал none, 0 байт, seq=-1, ожидание 0.000 мс.
- Телеметрия: mean/P95/max 101.032/233.680/296.827 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.984127, после drain 1.000000, итог 1.000000; delay mean/P95/max 261.559/304.579/311.146 мс.
- Возраст изображения mean/P95/max 423.846/580.000/643.333 мс; доля >1 с 0.000000; поздних кадров 0, max серия 0.
[300 кбит/с; Строгий приоритет]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 8.050/22 пакетов и 4452.4/12221 байт.
- На конце видео: 15 пакетов, 8695.0 байт; полное освобождение 20.998534 с, дополнительно 0.231867 с.
- Команды: mean/P95/max 9.440/17.466/17.813 мс; превышений 100 мс 0; промежутков >100 мс 0; max gap 65.760 мс; заменено 0.
- Аварийная команда: start/total 0.000/1.707 мс; deadline 50 мс соблюдён; блокировал none, 0 байт, seq=-1, ожидание 0.000 мс.
- Телеметрия: mean/P95/max 12.424/20.107/20.373 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.984127, после drain 1.000000, итог 1.000000; delay mean/P95/max 276.337/322.072/330.773 мс.
- Возраст изображения mean/P95/max 437.649/593.333/660.000 мс; доля >1 с 0.000000; поздних кадров 0, max серия 0.
[300 кбит/с; Приоритет + замена]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 8.050/22 пакетов и 4452.4/12221 байт.
- На конце видео: 15 пакетов, 8695.0 байт; полное освобождение 20.998534 с, дополнительно 0.231867 с.
- Команды: mean/P95/max 9.440/17.466/17.813 мс; превышений 100 мс 0; промежутков >100 мс 0; max gap 65.760 мс; заменено 0.
- Аварийная команда: start/total 0.000/1.707 мс; deadline 50 мс соблюдён; блокировал none, 0 байт, seq=-1, ожидание 0.000 мс.
- Телеметрия: mean/P95/max 12.424/20.107/20.373 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.984127, после drain 1.000000, итог 1.000000; delay mean/P95/max 276.337/322.072/330.773 мс.
- Возраст изображения mean/P95/max 437.649/593.333/660.000 мс; доля >1 с 0.000000; поздних кадров 0, max серия 0.
[260 кбит/с; FIFO]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 14.800/37 пакетов и 6010.5/16512 байт.
- На конце видео: 32 пакетов, 12974.0 байт; полное освобождение 21.165867 с, дополнительно 0.399200 с.
- Команды: mean/P95/max 169.073/333.059/460.944 мс; превышений 100 мс 298; промежутков >100 мс 63; max gap 384.348 мс; заменено 0.
- Аварийная команда: start/total 0.000/1.969 мс; deadline 50 мс соблюдён; блокировал none, 0 байт, seq=-1, ожидание 0.000 мс.
- Телеметрия: mean/P95/max 163.317/331.782/463.898 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.984127, после drain 1.000000, итог 1.000000; delay mean/P95/max 329.072/420.025/436.185 мс.
- Возраст изображения mean/P95/max 489.627/666.667/766.667 мс; доля >1 с 0.000000; поздних кадров 0, max серия 0.
[260 кбит/с; Строгий приоритет]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 10.783/28 пакетов и 6010.2/16384 байт.
- На конце видео: 23 пакетов, 12974.0 байт; полное освобождение 21.165867 с, дополнительно 0.399200 с.
- Команды: mean/P95/max 10.282/18.754/20.667 мс; превышений 100 мс 0; промежутков >100 мс 0; max gap 63.477 мс; заменено 0.
- Аварийная команда: start/total 0.000/1.969 мс; deadline 50 мс соблюдён; блокировал none, 0 байт, seq=-1, ожидание 0.000 мс.
- Телеметрия: mean/P95/max 13.864/22.448/23.621 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.968254, после drain 1.000000, итог 1.000000; delay mean/P95/max 351.218/445.846/459.559 мс.
- Возраст изображения mean/P95/max 511.188/693.333/783.333 мс; доля >1 с 0.000000; поздних кадров 0, max серия 0.
[260 кбит/с; Приоритет + замена]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 10.783/28 пакетов и 6010.2/16384 байт.
- На конце видео: 23 пакетов, 12974.0 байт; полное освобождение 21.165867 с, дополнительно 0.399200 с.
- Команды: mean/P95/max 10.282/18.754/20.667 мс; превышений 100 мс 0; промежутков >100 мс 0; max gap 63.477 мс; заменено 0.
- Аварийная команда: start/total 0.000/1.969 мс; deadline 50 мс соблюдён; блокировал none, 0 байт, seq=-1, ожидание 0.000 мс.
- Телеметрия: mean/P95/max 13.864/22.448/23.621 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.968254, после drain 1.000000, итог 1.000000; delay mean/P95/max 351.218/445.846/459.559 мс.
- Возраст изображения mean/P95/max 511.188/693.333/783.333 мс; доля >1 с 0.000000; поздних кадров 0, max серия 0.
[230 кбит/с; FIFO]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 86.915/184 пакетов и 34309.1/76497 байт.
- На конце видео: 181 пакетов, 73238.3 байт; полное освобождение 23.314087 с, дополнительно 2.547420 с.
- Команды: mean/P95/max 1174.288/2240.535/2608.522 мс; превышений 100 мс 414; промежутков >100 мс 63; max gap 427.304 мс; заменено 0.
- Аварийная команда: start/total 878.887/881.113 мс; deadline 50 мс НАРУШЕН; блокировал video, 608 байт, seq=461, ожидание 0.417 мс.
- Телеметрия: mean/P95/max 1164.978/2225.607/2611.861 мс; превышений 500 мс 177 (0.850962); заменено 0.
- Видео: опубликовано к концу источника 0.888889, после drain 1.000000, итог 1.000000; delay mean/P95/max 1347.217/2417.731/2576.186 мс.
- Возраст изображения mean/P95/max 1506.487/2588.167/2906.667 мс; доля >1 с 0.771870; поздних кадров 42, max серия 42.
[230 кбит/с; Строгий приоритет]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 59.750/132 пакетов и 34307.1/75969 байт.
- На конце видео: 127 пакетов, 73238.3 байт; полное освобождение 23.314087 с, дополнительно 2.547420 с.
- Команды: mean/P95/max 12.250/21.848/23.339 мс; превышений 100 мс 0; промежутков >100 мс 0; max gap 69.009 мс; заменено 0.
- Аварийная команда: start/total 9.322/11.548 мс; deadline 50 мс соблюдён; блокировал video, 608 байт, seq=458, ожидание 9.322 мс.
- Телеметрия: mean/P95/max 15.514/25.111/26.678 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.888889, после drain 1.000000, итог 1.000000; delay mean/P95/max 1445.995/2505.995/2583.977 мс.
- Возраст изображения mean/P95/max 1594.537/2678.167/2916.667 мс; доля >1 с 0.804460; поздних кадров 47, max серия 47.
[230 кбит/с; Приоритет + замена]
- Передано 1703 пакетов, 670280 байт; средняя/максимальная очередь 59.750/132 пакетов и 34307.1/75969 байт.
- На конце видео: 127 пакетов, 73238.3 байт; полное освобождение 23.314087 с, дополнительно 2.547420 с.
- Команды: mean/P95/max 12.250/21.848/23.339 мс; превышений 100 мс 0; промежутков >100 мс 0; max gap 69.009 мс; заменено 0.
- Аварийная команда: start/total 9.322/11.548 мс; deadline 50 мс соблюдён; блокировал video, 608 байт, seq=458, ожидание 9.322 мс.
- Телеметрия: mean/P95/max 15.514/25.111/26.678 мс; превышений 500 мс 0 (0.000000); заменено 0.
- Видео: опубликовано к концу источника 0.888889, после drain 1.000000, итог 1.000000; delay mean/P95/max 1445.995/2505.995/2583.977 мс.
- Возраст изображения mean/P95/max 1594.537/2678.167/2916.667 мс; доля >1 с 0.804460; поздних кадров 47, max серия 47.
5. Интерпретация планировщиков
- FIFO задерживает команды, потому что все ранее поступившие внешние видеопакеты остаются перед ними независимо от срочности.
- Строгий приоритет после каждого окончания пакета выбирает управление раньше телеметрии и видео, поэтому сокращает очередь команд.
- Уже начатая передача не прерывается: модель сериализует целый пакет как атомарную единицу и не задаёт фрагментацию общего пакета или возобновление передачи.
- Поэтому нижняя граница задержки срочной команды включает остаток времени самого длинного пакета, уже занявшего ресурс.
- Замена старого состояния новым не тратит канал на запоздалую команду, которая к моменту приёма уже не отражает актуальное управление.
- Строгий приоритет перераспределяет порядок, но не уменьшает объём видео; при нагрузке выше пропускной способности видеопакеты продолжают накапливаться.
- Длинная очередь видео увеличивает возраст изображения даже без потерь: полные, но старые кадры публикуются позже времени формирования.
- Восстановление 100% кадров после drain подтверждает целостность, но не пригодность задержек канала для управления в реальном времени.
6. Допущения модели
- Один общий абстрактный ресурс и одна расчётная скорость для обоих направлений; переключение направления не задерживает передачу.
- Не выбран физический полудуплекс/дуплекс, TDD/FDD, модуляция, ретранслятор или физическое разделение восходящего и нисходящего каналов.
- Ошибки и помехи отсутствуют; полностью переданный пакет доставляется без повреждений; видеопакеты не отбрасываются по возрасту.
- После конца исходного видео новые данные не создаются, а очередь полностью освобождается.
- При совпадении времени FIFO использует traffic_class, stream_id и sequence_number как устойчивое правило разрешения совпадений.
- Средняя очередь рассчитана по времени на основном интервале и включает пакет, находящийся в передатчике; остаток байтов активного пакета учитывается пропорционально недопереданному времени.
7. Функциональные проверки
- PASS 01_header_roundtrip: PASS
- PASS 02_header_crc_detection: PASS
- PASS 03_payload_crc_detection: PASS
- PASS 04_video_wrapper_byte_exact: PASS
- PASS 05_lab030_crc: PASS
- PASS 06_lab028_crc: PASS
- PASS 07_fifo_global_order: PASS
- PASS 08_strict_priority: PASS
- PASS 09_non_preemptive: PASS
- PASS 10_intra_class_sequence: PASS
- PASS 11_latest_state_scope: PASS
- PASS 12_active_state_not_removed: PASS
- PASS 13_emergency_never_replaced: PASS
- PASS 14_300kbps_all_video: PASS
- PASS 15_overload_visible: PASS
- PASS 16_exact_transmission_duration: PASS
- PASS 17_total_bits_accounting: PASS
- PASS 18_reproducible_schedule: PASS
- PASS 19_no_video_control_isolated: PASS
- PASS 20_latest_state_reaches_receiver: PASS
8. Созданные файлы и итоговый Git status
- protocol/link_packet.py
- protocol/priority_scheduler.py
- tests/lab033_priority_channel_scheduler.py
- data/processed/lab033/lab033_summary.csv
- data/processed/lab033/lab033_class_metrics.csv
- data/processed/lab033/lab033_report.txt
- data/processed/lab033/lab033_control_delay.png
- data/processed/lab033/lab033_emergency_delay.png
- data/processed/lab033/lab033_video_delay.png
- data/processed/lab033/lab033_queue_length.png
- data/processed/lab033/lab033_deadline_misses.png
- data/processed/lab033/lab033_scheduler_comparison.png
- Lab028-Lab032 и их сохранённые результаты не изменены.
- Lab033 не добавлена в индекс и не закоммичена.
## main...origin/main
?? data/processed/lab033/
?? protocol/link_packet.py
?? protocol/priority_scheduler.py
?? tests/lab033_priority_channel_scheduler.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -0,0 +1,10 @@
channel_kbps,scheduler,offered_load_kbps,emergency_load_kbps,control_load_kbps,telemetry_load_kbps,video_load_kbps,service_share_percent,offered_to_capacity_ratio,transmitted_packets,transmitted_bytes,source_duration_seconds,drain_end_seconds,additional_drain_seconds,queue_at_source_end_packets,queue_at_source_end_bytes,mean_queue_packets,max_queue_packets,mean_queue_bytes,max_queue_bytes,control_mean_age_ms,control_p95_age_ms,control_max_age_ms,control_gaps_over_100ms,control_max_receive_gap_ms,control_replaced,emergency_start_delay_ms,emergency_total_delay_ms,emergency_deadline_met,emergency_blocker_class,emergency_blocker_size_bytes,emergency_blocker_sequence,emergency_blocking_delay_ms,telemetry_mean_age_ms,telemetry_p95_age_ms,telemetry_max_age_ms,telemetry_misses_500ms,telemetry_miss_fraction,telemetry_replaced,video_published_at_source_end_fraction,video_published_after_drain_fraction,video_final_recovery_fraction,video_mean_publication_delay_ms,video_p95_publication_delay_ms,video_max_publication_delay_ms,video_mean_display_age_ms,video_p95_display_age_ms,video_max_display_age_ms,video_age_over_1s_fraction,video_late_frame_count,video_max_late_run
300.0,fifo,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,0.8607126805778491,1703,670280,20.766666666666666,20.99853366666666,0.23186699999999405,18,8695.012499999913,10.477643691813581,22,4452.409985633955,12221,106.0716594551255,238.6335833333294,296.1863333333259,63,344.4796666666594,0,0.0,1.7066666666671892,True,none,0,-1,0.0,101.03181249999754,233.68033333332914,296.8269999999933,0,0.0,0,0.9841269841269841,1.0,1.0,261.5593703703662,304.57866666666183,311.1463333333262,423.8457877201335,580.0,643.3333333333333,0.0,0,0
300.0,strict_priority,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,0.8607126805778491,1703,670280,20.766666666666666,20.99853366666666,0.23186699999999405,15,8695.012499999915,8.05016637239154,22,4452.387224542477,12221,9.439928685896334,17.46633333333346,17.813000000000745,0,65.7596666666671,0,0.0,1.7066666666671892,True,none,0,-1,0.0,12.424120192306697,20.10688333333437,20.37300000000064,0,0.0,0,0.9841269841269841,1.0,1.0,276.3369365079338,322.0720000000001,330.77299999999,437.6487386958594,593.3333333333334,660.0000000000001,0.0,0,0
300.0,latest_state,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,0.8607126805778491,1703,670280,20.766666666666666,20.99853366666666,0.23186699999999405,15,8695.012499999915,8.05016637239154,22,4452.387224542477,12221,9.439928685896334,17.46633333333346,17.813000000000745,0,65.7596666666671,0,0.0,1.7066666666671892,True,none,0,-1,0.0,12.424120192306697,20.10688333333437,20.37300000000064,0,0.0,0,0.9841269841269841,1.0,1.0,276.3369365079338,322.0720000000001,330.77299999999,437.6487386958594,593.3333333333334,660.0000000000001,0.0,0,0
260.0,fifo,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,0.9931300160513643,1703,670280,20.766666666666666,21.165867000000176,0.39920033333351057,32,12974.010833338622,14.800483013954153,37,6010.5496338357525,16512,169.07309430475814,333.0593076923547,460.9439230770995,63,384.34838461538055,0,0.0,1.969230769230279,True,none,0,-1,0.0,163.31735133138503,331.7823846154539,463.8977692309467,0,0.0,0,0.9841269841269841,1.0,1.0,329.07164590967284,420.0249487180816,436.18494871812175,489.62700661000974,666.6666666666679,766.6666666666692,0.0,0,0
260.0,strict_priority,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,0.9931300160513643,1703,670280,20.766666666666666,21.16586700000017,0.39920033333350347,23,12974.010833338338,10.783079034450305,28,6010.224630522928,16384,10.282265902386632,18.753596153843198,20.66700000000843,0,63.476923076922546,0,0.0,1.969230769230279,True,none,0,-1,0.0,13.863505177534366,22.44779230770204,23.620846153853847,0,0.0,0,0.9682539682539683,1.0,1.0,351.2176776557015,445.84648717956554,459.55930769245157,511.18822788794483,693.3333333333316,783.3333333333314,0.0,0,0
260.0,latest_state,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,0.9931300160513643,1703,670280,20.766666666666666,21.16586700000017,0.39920033333350347,23,12974.010833338338,10.783079034450305,28,6010.224630522928,16384,10.282265902386632,18.753596153843198,20.66700000000843,0,63.476923076922546,0,0.0,1.969230769230279,True,none,0,-1,0.0,13.863505177534366,22.44779230770204,23.620846153853847,0,0.0,0,0.9682539682539683,1.0,1.0,351.2176776557015,445.84648717956554,459.55930769245157,511.18822788794483,693.3333333333316,783.3333333333314,0.0,0,0
230.0,fifo,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,1.1226687137971945,1703,670280,20.766666666666666,23.3140869565216,2.5474202898549336,181,73238.33333333043,86.91502882266383,184,34309.08168164904,76497,1174.2878762541263,2240.5347826085754,2608.5217391302963,63,427.3043478260803,0,878.8869565216775,881.1130434781998,False,video,608,461,0.4173913042819777,1164.978260869511,2225.6069565216276,2611.860869565078,177,0.8509615384615384,0,0.8888888888888888,1.0,1.0,1347.216563146941,2417.731014492629,2576.1855072462367,1506.486563750715,2588.1666666666674,2906.6666666666697,0.7718696397941681,42,42
230.0,strict_priority,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,1.1226687137971945,1703,670280,20.766666666666666,23.314086956521678,2.547420289855012,127,73238.33333333276,59.749842696627866,132,34307.118394888734,75969,12.250334448143445,21.84782608690261,23.33913043477409,0,69.00869565217427,0,9.32173913036749,11.547826086889756,True,video,608,458,9.32173913036749,15.51421404680579,25.111304347813142,26.678260869555714,0,0.0,0,0.8888888888888888,1.0,1.0,1445.9947550034299,2505.9953623187953,2583.976811594141,1594.536878216124,2678.1666666666674,2916.666666666668,0.8044596912521441,47,47
230.0,latest_state,258.21380417335473,0.02465489566613162,10.256436597110755,7.692327447833066,240.24038523274479,34.922718863758426,1.1226687137971945,1703,670280,20.766666666666666,23.314086956521678,2.547420289855012,127,73238.33333333276,59.749842696627866,132,34307.118394888734,75969,12.250334448143445,21.84782608690261,23.33913043477409,0,69.00869565217427,0,9.32173913036749,11.547826086889756,True,video,608,458,9.32173913036749,15.51421404680579,25.111304347813142,26.678260869555714,0,0.0,0,0.8888888888888888,1.0,1.0,1445.9947550034299,2505.9953623187953,2583.976811594141,1594.536878216124,2678.1666666666674,2916.666666666668,0.8044596912521441,47,47
1 channel_kbps scheduler offered_load_kbps emergency_load_kbps control_load_kbps telemetry_load_kbps video_load_kbps service_share_percent offered_to_capacity_ratio transmitted_packets transmitted_bytes source_duration_seconds drain_end_seconds additional_drain_seconds queue_at_source_end_packets queue_at_source_end_bytes mean_queue_packets max_queue_packets mean_queue_bytes max_queue_bytes control_mean_age_ms control_p95_age_ms control_max_age_ms control_gaps_over_100ms control_max_receive_gap_ms control_replaced emergency_start_delay_ms emergency_total_delay_ms emergency_deadline_met emergency_blocker_class emergency_blocker_size_bytes emergency_blocker_sequence emergency_blocking_delay_ms telemetry_mean_age_ms telemetry_p95_age_ms telemetry_max_age_ms telemetry_misses_500ms telemetry_miss_fraction telemetry_replaced video_published_at_source_end_fraction video_published_after_drain_fraction video_final_recovery_fraction video_mean_publication_delay_ms video_p95_publication_delay_ms video_max_publication_delay_ms video_mean_display_age_ms video_p95_display_age_ms video_max_display_age_ms video_age_over_1s_fraction video_late_frame_count video_max_late_run
2 300.0 fifo 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 0.8607126805778491 1703 670280 20.766666666666666 20.99853366666666 0.23186699999999405 18 8695.012499999913 10.477643691813581 22 4452.409985633955 12221 106.0716594551255 238.6335833333294 296.1863333333259 63 344.4796666666594 0 0.0 1.7066666666671892 True none 0 -1 0.0 101.03181249999754 233.68033333332914 296.8269999999933 0 0.0 0 0.9841269841269841 1.0 1.0 261.5593703703662 304.57866666666183 311.1463333333262 423.8457877201335 580.0 643.3333333333333 0.0 0 0
3 300.0 strict_priority 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 0.8607126805778491 1703 670280 20.766666666666666 20.99853366666666 0.23186699999999405 15 8695.012499999915 8.05016637239154 22 4452.387224542477 12221 9.439928685896334 17.46633333333346 17.813000000000745 0 65.7596666666671 0 0.0 1.7066666666671892 True none 0 -1 0.0 12.424120192306697 20.10688333333437 20.37300000000064 0 0.0 0 0.9841269841269841 1.0 1.0 276.3369365079338 322.0720000000001 330.77299999999 437.6487386958594 593.3333333333334 660.0000000000001 0.0 0 0
4 300.0 latest_state 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 0.8607126805778491 1703 670280 20.766666666666666 20.99853366666666 0.23186699999999405 15 8695.012499999915 8.05016637239154 22 4452.387224542477 12221 9.439928685896334 17.46633333333346 17.813000000000745 0 65.7596666666671 0 0.0 1.7066666666671892 True none 0 -1 0.0 12.424120192306697 20.10688333333437 20.37300000000064 0 0.0 0 0.9841269841269841 1.0 1.0 276.3369365079338 322.0720000000001 330.77299999999 437.6487386958594 593.3333333333334 660.0000000000001 0.0 0 0
5 260.0 fifo 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 0.9931300160513643 1703 670280 20.766666666666666 21.165867000000176 0.39920033333351057 32 12974.010833338622 14.800483013954153 37 6010.5496338357525 16512 169.07309430475814 333.0593076923547 460.9439230770995 63 384.34838461538055 0 0.0 1.969230769230279 True none 0 -1 0.0 163.31735133138503 331.7823846154539 463.8977692309467 0 0.0 0 0.9841269841269841 1.0 1.0 329.07164590967284 420.0249487180816 436.18494871812175 489.62700661000974 666.6666666666679 766.6666666666692 0.0 0 0
6 260.0 strict_priority 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 0.9931300160513643 1703 670280 20.766666666666666 21.16586700000017 0.39920033333350347 23 12974.010833338338 10.783079034450305 28 6010.224630522928 16384 10.282265902386632 18.753596153843198 20.66700000000843 0 63.476923076922546 0 0.0 1.969230769230279 True none 0 -1 0.0 13.863505177534366 22.44779230770204 23.620846153853847 0 0.0 0 0.9682539682539683 1.0 1.0 351.2176776557015 445.84648717956554 459.55930769245157 511.18822788794483 693.3333333333316 783.3333333333314 0.0 0 0
7 260.0 latest_state 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 0.9931300160513643 1703 670280 20.766666666666666 21.16586700000017 0.39920033333350347 23 12974.010833338338 10.783079034450305 28 6010.224630522928 16384 10.282265902386632 18.753596153843198 20.66700000000843 0 63.476923076922546 0 0.0 1.969230769230279 True none 0 -1 0.0 13.863505177534366 22.44779230770204 23.620846153853847 0 0.0 0 0.9682539682539683 1.0 1.0 351.2176776557015 445.84648717956554 459.55930769245157 511.18822788794483 693.3333333333316 783.3333333333314 0.0 0 0
8 230.0 fifo 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 1.1226687137971945 1703 670280 20.766666666666666 23.3140869565216 2.5474202898549336 181 73238.33333333043 86.91502882266383 184 34309.08168164904 76497 1174.2878762541263 2240.5347826085754 2608.5217391302963 63 427.3043478260803 0 878.8869565216775 881.1130434781998 False video 608 461 0.4173913042819777 1164.978260869511 2225.6069565216276 2611.860869565078 177 0.8509615384615384 0 0.8888888888888888 1.0 1.0 1347.216563146941 2417.731014492629 2576.1855072462367 1506.486563750715 2588.1666666666674 2906.6666666666697 0.7718696397941681 42 42
9 230.0 strict_priority 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 1.1226687137971945 1703 670280 20.766666666666666 23.314086956521678 2.547420289855012 127 73238.33333333276 59.749842696627866 132 34307.118394888734 75969 12.250334448143445 21.84782608690261 23.33913043477409 0 69.00869565217427 0 9.32173913036749 11.547826086889756 True video 608 458 9.32173913036749 15.51421404680579 25.111304347813142 26.678260869555714 0 0.0 0 0.8888888888888888 1.0 1.0 1445.9947550034299 2505.9953623187953 2583.976811594141 1594.536878216124 2678.1666666666674 2916.666666666668 0.8044596912521441 47 47
10 230.0 latest_state 258.21380417335473 0.02465489566613162 10.256436597110755 7.692327447833066 240.24038523274479 34.922718863758426 1.1226687137971945 1703 670280 20.766666666666666 23.314086956521678 2.547420289855012 127 73238.33333333276 59.749842696627866 132 34307.118394888734 75969 12.250334448143445 21.84782608690261 23.33913043477409 0 69.00869565217427 0 9.32173913036749 11.547826086889756 True video 608 458 9.32173913036749 15.51421404680579 25.111304347813142 26.678260869555714 0 0.0 0 0.8888888888888888 1.0 1.0 1445.9947550034299 2505.9953623187953 2583.976811594141 1594.536878216124 2678.1666666666674 2916.666666666668 0.8044596912521441 47 47

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,16 @@
channel_kbps,policy,control_mean_age_ms,control_p95_age_ms,control_max_age_ms,control_deadline_misses,control_max_receive_gap_ms,control_replaced,emergency_total_delay_ms,emergency_deadline_met,emergency_blocker_class,emergency_blocker_size_bytes,emergency_blocker_sequence,emergency_blocking_delay_ms,telemetry_mean_age_ms,telemetry_p95_age_ms,telemetry_max_age_ms,telemetry_deadline_misses,telemetry_replaced
300.0,continuous_no_drop,9.439928685896334,17.46633333333346,17.813000000000745,0,65.7596666666671,0,1.7066666666671892,True,none,0,-1,0.0,12.424120192306697,20.10688333333437,20.37300000000064,0,0
300.0,aligned_no_drop,10.09563301281934,17.46633333333314,17.78700000000022,0,65.7596666666671,0,1.7066666666671892,True,none,0,-1,0.0,12.858206730768112,19.573666666667933,20.160000000000622,0,0
300.0,aligned_1500ms,10.09563301281934,17.46633333333314,17.78700000000022,0,65.7596666666671,0,1.7066666666671892,True,none,0,-1,0.0,12.858206730768112,19.573666666667933,20.160000000000622,0,0
300.0,aligned_1000ms,10.09563301281934,17.46633333333314,17.78700000000022,0,65.7596666666671,0,1.7066666666671892,True,none,0,-1,0.0,12.858206730768112,19.573666666667933,20.160000000000622,0,0
300.0,aligned_500ms,10.09563301281934,17.46633333333314,17.78700000000022,0,65.7596666666671,0,1.7066666666671892,True,none,0,-1,0.0,12.858206730768112,19.573666666667933,20.160000000000622,0,0
260.0,continuous_no_drop,10.282265902386632,18.753596153843198,20.66700000000843,0,63.476923076922546,0,1.969230769230279,True,none,0,-1,0.0,13.863505177534366,22.44779230770204,23.620846153853847,0,0
260.0,aligned_no_drop,10.671617788489147,18.907692307691043,20.492307692309808,0,67.81538461538617,0,1.969230769230279,True,none,0,-1,0.0,13.490528846181197,21.89960384616611,23.323076923056796,0,0
260.0,aligned_1500ms,10.671617788489147,18.907692307691043,20.492307692309808,0,67.81538461538617,0,1.969230769230279,True,none,0,-1,0.0,13.490528846181197,21.89960384616611,23.323076923056796,0,0
260.0,aligned_1000ms,10.671617788489147,18.907692307691043,20.492307692309808,0,67.81538461538617,0,1.969230769230279,True,none,0,-1,0.0,13.490528846181197,21.89960384616611,23.323076923056796,0,0
260.0,aligned_500ms,10.71385151629976,18.907692307691043,20.492307692309808,0,67.81538461538617,0,1.969230769230279,True,none,0,-1,0.0,13.61641642014566,22.039999999996684,23.323076923056796,0,0
230.0,continuous_no_drop,12.250334448143445,21.84782608690261,23.33913043477409,0,69.00869565217427,0,11.547826086889756,True,video,608,458,9.32173913036749,15.51421404680579,25.111304347813142,26.678260869555714,0,0
230.0,aligned_no_drop,12.729598662164767,22.499999999942013,24.556521739064863,0,69.00869565217427,0,22.330434782542596,True,video,608,463,20.10434782602033,15.594983277549522,25.377391304327652,27.895652173848262,0,0
230.0,aligned_1500ms,12.613043478216982,22.460869565154475,24.556521739064863,0,69.00869565217427,0,22.330434782542596,True,video,608,463,20.10434782602033,15.613377926377664,25.426086956510073,27.895652173848262,0,0
230.0,aligned_1000ms,12.76596989961911,22.578260869525746,23.356521739078318,0,69.00869565217427,0,11.652173912974462,True,video,608,484,9.426086956452195,15.511371237411904,25.37739130434555,26.608695652086567,0,0
230.0,aligned_500ms,12.377759197289913,22.230434782573028,23.339130434790967,0,69.00869565217427,0,3.6869565216850475,True,video,608,511,1.4608695651627812,15.598996655484031,24.913043478221116,26.678260869573478,0,0
1 channel_kbps policy control_mean_age_ms control_p95_age_ms control_max_age_ms control_deadline_misses control_max_receive_gap_ms control_replaced emergency_total_delay_ms emergency_deadline_met emergency_blocker_class emergency_blocker_size_bytes emergency_blocker_sequence emergency_blocking_delay_ms telemetry_mean_age_ms telemetry_p95_age_ms telemetry_max_age_ms telemetry_deadline_misses telemetry_replaced
2 300.0 continuous_no_drop 9.439928685896334 17.46633333333346 17.813000000000745 0 65.7596666666671 0 1.7066666666671892 True none 0 -1 0.0 12.424120192306697 20.10688333333437 20.37300000000064 0 0
3 300.0 aligned_no_drop 10.09563301281934 17.46633333333314 17.78700000000022 0 65.7596666666671 0 1.7066666666671892 True none 0 -1 0.0 12.858206730768112 19.573666666667933 20.160000000000622 0 0
4 300.0 aligned_1500ms 10.09563301281934 17.46633333333314 17.78700000000022 0 65.7596666666671 0 1.7066666666671892 True none 0 -1 0.0 12.858206730768112 19.573666666667933 20.160000000000622 0 0
5 300.0 aligned_1000ms 10.09563301281934 17.46633333333314 17.78700000000022 0 65.7596666666671 0 1.7066666666671892 True none 0 -1 0.0 12.858206730768112 19.573666666667933 20.160000000000622 0 0
6 300.0 aligned_500ms 10.09563301281934 17.46633333333314 17.78700000000022 0 65.7596666666671 0 1.7066666666671892 True none 0 -1 0.0 12.858206730768112 19.573666666667933 20.160000000000622 0 0
7 260.0 continuous_no_drop 10.282265902386632 18.753596153843198 20.66700000000843 0 63.476923076922546 0 1.969230769230279 True none 0 -1 0.0 13.863505177534366 22.44779230770204 23.620846153853847 0 0
8 260.0 aligned_no_drop 10.671617788489147 18.907692307691043 20.492307692309808 0 67.81538461538617 0 1.969230769230279 True none 0 -1 0.0 13.490528846181197 21.89960384616611 23.323076923056796 0 0
9 260.0 aligned_1500ms 10.671617788489147 18.907692307691043 20.492307692309808 0 67.81538461538617 0 1.969230769230279 True none 0 -1 0.0 13.490528846181197 21.89960384616611 23.323076923056796 0 0
10 260.0 aligned_1000ms 10.671617788489147 18.907692307691043 20.492307692309808 0 67.81538461538617 0 1.969230769230279 True none 0 -1 0.0 13.490528846181197 21.89960384616611 23.323076923056796 0 0
11 260.0 aligned_500ms 10.71385151629976 18.907692307691043 20.492307692309808 0 67.81538461538617 0 1.969230769230279 True none 0 -1 0.0 13.61641642014566 22.039999999996684 23.323076923056796 0 0
12 230.0 continuous_no_drop 12.250334448143445 21.84782608690261 23.33913043477409 0 69.00869565217427 0 11.547826086889756 True video 608 458 9.32173913036749 15.51421404680579 25.111304347813142 26.678260869555714 0 0
13 230.0 aligned_no_drop 12.729598662164767 22.499999999942013 24.556521739064863 0 69.00869565217427 0 22.330434782542596 True video 608 463 20.10434782602033 15.594983277549522 25.377391304327652 27.895652173848262 0 0
14 230.0 aligned_1500ms 12.613043478216982 22.460869565154475 24.556521739064863 0 69.00869565217427 0 22.330434782542596 True video 608 463 20.10434782602033 15.613377926377664 25.426086956510073 27.895652173848262 0 0
15 230.0 aligned_1000ms 12.76596989961911 22.578260869525746 23.356521739078318 0 69.00869565217427 0 11.652173912974462 True video 608 484 9.426086956452195 15.511371237411904 25.37739130434555 26.608695652086567 0 0
16 230.0 aligned_500ms 12.377759197289913 22.230434782573028 23.339130434790967 0 69.00869565217427 0 3.6869565216850475 True video 608 511 1.4608695651627812 15.598996655484031 24.913043478221116 26.678260869573478 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -0,0 +1,98 @@
Lab034. Ограничение возраста видеоданных и отбрасывание устаревших кадров
1. Исходное состояние
- Commit Lab033: 21751f41dd0af022fc1f935c67e795e3ae4257c5.
- Перед Lab034 рабочее дерево было чистым; main опережала origin/main на один commit.
- Lab028-Lab033 и их результаты не изменяются.
2. Формирование FEC-блоков
layout | source | parity | blocks | partial | cross-frame | frames/block mean/max | before/after Lab033 kbps
Непрерывные | 862 | 216 | 72 | 1 | 58 | 1.806/2 | 226.951/240.240
По кадрам | 862 | 251 | 125 | 62 | 0 | 1.000/1 | 232.926/246.647
- Дополнительная избыточность выравнивания: 5.975 кбит/с (2.633%).
- Средняя эквивалентная группа кадра: непрерывные 17.111 пакета / 263.968 мс; выровненные 17.667 пакета / 271.007 мс при 300 кбит/с.
3. Пятнадцать сочетаний
speed | policy | offered/capacity | published/drop/partial | update fps | age P95 ms | control P95 ms | emergency ms | queue max/end | drain s | wasted bytes
300 | Непрерывные, без отбрасывания | 0.8607 | 63/0/0 | 2.986 | 593.333 | 17.466 | 1.707 | 22/15 | 0.231867 | 0
300 | По кадрам, без отбрасывания | 0.8821 | 63/0/0 | 2.986 | 590.000 | 17.466 | 1.707 | 21/13 | 0.199440 | 0
300 | По кадрам, 1500 мс | 0.8821 | 63/0/0 | 2.986 | 590.000 | 17.466 | 1.707 | 21/13 | 0.199440 | 0
300 | По кадрам, 1000 мс | 0.8821 | 63/0/0 | 2.986 | 590.000 | 17.466 | 1.707 | 21/13 | 0.199440 | 0
300 | По кадрам, 500 мс | 0.8821 | 63/0/0 | 2.986 | 590.000 | 17.466 | 1.707 | 21/13 | 0.199440 | 0
260 | Непрерывные, без отбрасывания | 0.9931 | 63/0/0 | 2.937 | 690.000 | 18.754 | 1.969 | 28/23 | 0.399200 | 0
260 | По кадрам, без отбрасывания | 1.0178 | 63/0/0 | 2.937 | 773.833 | 18.908 | 1.969 | 37/30 | 0.539293 | 0
260 | По кадрам, 1500 мс | 1.0178 | 63/0/0 | 2.937 | 773.833 | 18.908 | 1.969 | 37/30 | 0.539293 | 0
260 | По кадрам, 1000 мс | 1.0178 | 63/0/0 | 2.937 | 773.833 | 18.908 | 1.969 | 37/30 | 0.539293 | 0
260 | По кадрам, 500 мс | 1.0178 | 56/7/7 | 2.648 | 1053.833 | 18.908 | 1.969 | 31/24 | 0.403939 | 69597
230 | Непрерывные, без отбрасывания | 1.1227 | 63/0/0 | 2.697 | 2443.833 | 21.848 | 11.548 | 132/127 | 2.547420 | 0
230 | По кадрам, без отбрасывания | 1.1505 | 63/0/0 | 2.600 | 2913.333 | 22.500 | 22.330 | 160/155 | 3.125855 | 0
230 | По кадрам, 1500 мс | 1.1505 | 27/36/36 | 1.300 | 11064.833 | 22.461 | 22.330 | 87/81 | 1.418064 | 321929
230 | По кадрам, 1000 мс | 1.1505 | 14/49/49 | 0.674 | 11731.500 | 22.578 | 11.652 | 63/53 | 0.904881 | 434885
230 | По кадрам, 500 мс | 1.1505 | 3/60/60 | 0.144 | 11731.500 | 22.230 | 3.687 | 32/27 | 0.419072 | 532167
4. Практический смысл ограничений возраста
- При 300 кбит/с очередь обслуживается достаточно быстро: ограничения 500, 1000 и 1500 мс не срабатывают и не дают выигрыша.
- При 260 кбит/с пределы 1000 и 1500 мс также не срабатывают; предел 500 мс публикует 56 из 63 кадров и отбрасывает 7, поэтому он уже чрезмерно жёсткий для части кадров.
- При 230 кбит/с предел 1500 мс сохраняет 27 кадров, 1000 мс — 14, 500 мс — только 3; более строгий предел лучше ограничивает очередь, но резко уменьшает частоту обновления.
- В исследованной перегрузке 1500 мс является наиболее мягким из конечных ограничений; 1000 мс сильнее улучшает свежесть ценой обновлений, а 500 мс чрезмерно жёсток.
5. Интерпретация
- Произвольное удаление символа из непрерывного FEC-блока опасно: блок может содержать соседние кадры, и нехватка символов затрагивает их совместное декодирование.
- Выравнивание изолирует кадр в собственных блоках, поэтому все ожидающие символы устаревшего кадра можно удалить без повреждения соседей.
- Цена изоляции — 5.975 кбит/с дополнительного внешнего потока из-за большего числа неполных блоков.
- Полная доставка после освобождения очереди не является целью операторского видео: поздний полный кадр уже не описывает текущее состояние ровера.
- При перегрузке отбрасывание старого кадра освобождает ресурс для более свежего изображения и не затрагивает команды.
- При 230 кбит/с выравнивание меняет фазу активного видеопакета около 10,0 с; фактическая задержка аварийной команды может отличаться от Lab033, но отбрасывание её не увеличивает, а непереключаемая верхняя граница остаётся прежней, поскольку максимальный размер пакета не изменён.
- Слишком малый возраст может отменять кадры быстрее их полной передачи и снижать фактическую частоту обновления.
- Поэтому возраст изображения оценивается вместе с частотой обновления: хорошее значение одного показателя не гарантирует полезный видеопоток.
- Замена команд допустима только для текущего состояния (скорость, поворот, торможение, телеметрия). Дискретные события заменять нельзя; в Lab034 они не моделируются.
6. Допущения
- Один общий абстрактный ресурс, одна скорость для обоих направлений, без времени переключения и без выбора физического duplex/TDD/FDD.
- Ошибки и помехи отсутствуют; начатый пакет не прерывается; форматы Lab028, Lab030 и Lab033 неизменны.
- Возраст считается от формирования составного кадра по generation_time_us; служебные frame_id и состояние отмены не сериализуются.
- Видеокадр с удалёнными пакетами очищается в приёмнике и никогда не публикуется.
- Допустимые возраста 500, 1000 и 1500 мс заданы явно и автоматически не выбираются.
7. Функциональные проверки
- PASS 01_continuous_matches_lab033: PASS
- PASS 02_aligned_blocks_one_frame: PASS
- PASS 03_aligned_no_drop_recovers_all: PASS
- PASS 04_whole_frame_drop_isolated: PASS
- PASS 05_only_waiting_video_removed: PASS
- PASS 06_active_packet_not_preempted: PASS
- PASS 07_partial_cancel_not_published: PASS
- PASS 08_age_uses_frame_generation: PASS
- PASS 09_receiver_cleans_stale_state: PASS
- PASS 10_emergency_never_deleted: PASS
- PASS 11_high_priority_before_video: PASS
- PASS 12_230kbps_finite_age_bounds_queue: PASS
- PASS 13_packet_accounting: PASS
- PASS 14_byte_accounting: PASS
- PASS 15_all_crc_layers_pass: PASS
- PASS 16_incomplete_frame_never_published: PASS
- PASS 17_reproducible: PASS
- PASS 18_drop_does_not_worsen_emergency: PASS
8. Созданные файлы
- protocol/video_age_policy.py
- tests/lab034_stale_video_drop.py
- data/processed/lab034/lab034_summary.csv
- data/processed/lab034/lab034_video_metrics.csv
- data/processed/lab034/lab034_control_metrics.csv
- data/processed/lab034/lab034_report.txt
- data/processed/lab034/lab034_image_age.png
- data/processed/lab034/lab034_frame_outcomes.png
- data/processed/lab034/lab034_queue_size.png
- data/processed/lab034/lab034_control_delay.png
- data/processed/lab034/lab034_update_rate.png
- data/processed/lab034/lab034_fec_alignment_overhead.png
- data/processed/lab034/lab034_policy_comparison.png
9. Итоговый Git status
- Lab034 не добавлена в индекс и не закоммичена.
## main...origin/main [ahead 1]
?? data/processed/lab034/
?? protocol/video_age_policy.py
?? tests/lab034_stale_video_drop.py

View File

@@ -0,0 +1,16 @@
channel_kbps,policy,layout,max_video_age_ms,offered_load_kbps,offered_to_capacity_ratio,transmitted_packets,transmitted_bytes,dropped_packets,dropped_bytes,wasted_transmitted_bytes,mean_queue_packets,max_queue_packets,mean_queue_bytes,max_queue_bytes,queue_at_source_end_packets,additional_drain_seconds,channel_utilization_fraction
300.0,continuous_no_drop,continuous,-1,258.21380417335473,0.8607126805778491,1703,670280,0,0,0,8.05016637239154,22,4452.387224542477,12221,15,0.23186699999999405,0.8512086423304386
300.0,aligned_no_drop,aligned,-1,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,13,0.1994403333333281,0.8724635473515175
300.0,aligned_1500ms,aligned,1500,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,13,0.1994403333333281,0.8724635473515175
300.0,aligned_1000ms,aligned,1000,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,13,0.1994403333333281,0.8724635473515175
300.0,aligned_500ms,aligned,500,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,13,0.1994403333333281,0.8724635473515175
260.0,continuous_no_drop,continuous,-1,258.21380417335473,0.9931300160513643,1703,670280,0,0,0,10.783079034450305,28,6010.224630522928,16384,23,0.39920033333350347,0.9743990170589202
260.0,aligned_no_drop,aligned,-1,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,30,0.5392926410258561,0.991800947030499
260.0,aligned_1500ms,aligned,1500,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,30,0.5392926410258561,0.991800947030499
260.0,aligned_1000ms,aligned,1000,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,30,0.5392926410258561,0.991800947030499
260.0,aligned_500ms,aligned,500,264.62022471910115,1.017770095073466,1730,682511,8,4399,69597,13.261018996174682,31,7402.747049027978,16992,24,0.4039387948720048,0.991800947030499
230.0,continuous_no_drop,continuous,-1,258.21380417335473,1.1226687137971945,1703,670280,0,0,0,59.749842696627866,132,34307.118394888734,75969,127,2.547420289855012,1.0
230.0,aligned_no_drop,aligned,-1,264.62022471910115,1.1505227161700051,1738,686910,0,0,0,76.17493190801534,160,43586.68565075366,92568,155,3.1258550724636294,1.0
230.0,aligned_1500ms,aligned,1500,264.62022471910115,1.1505227161700051,1641,637811,97,49099,321929,59.44779896084508,87,33911.67327808277,50035,81,1.4180637681158217,1.0
230.0,aligned_1000ms,aligned,1000,264.62022471910115,1.1505227161700051,1609,623057,129,63853,434885,42.60156656500422,63,24223.970784957004,35495,53,0.9048811594201744,1.0
230.0,aligned_500ms,aligned,500,264.62022471910115,1.1505227161700051,1580,609090,158,77820,532167,20.03526158908224,32,11270.42233914812,18208,27,0.41907246376802476,0.9999999999999993
1 channel_kbps policy layout max_video_age_ms offered_load_kbps offered_to_capacity_ratio transmitted_packets transmitted_bytes dropped_packets dropped_bytes wasted_transmitted_bytes mean_queue_packets max_queue_packets mean_queue_bytes max_queue_bytes queue_at_source_end_packets additional_drain_seconds channel_utilization_fraction
2 300.0 continuous_no_drop continuous -1 258.21380417335473 0.8607126805778491 1703 670280 0 0 0 8.05016637239154 22 4452.387224542477 12221 15 0.23186699999999405 0.8512086423304386
3 300.0 aligned_no_drop aligned -1 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 13 0.1994403333333281 0.8724635473515175
4 300.0 aligned_1500ms aligned 1500 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 13 0.1994403333333281 0.8724635473515175
5 300.0 aligned_1000ms aligned 1000 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 13 0.1994403333333281 0.8724635473515175
6 300.0 aligned_500ms aligned 500 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 13 0.1994403333333281 0.8724635473515175
7 260.0 continuous_no_drop continuous -1 258.21380417335473 0.9931300160513643 1703 670280 0 0 0 10.783079034450305 28 6010.224630522928 16384 23 0.39920033333350347 0.9743990170589202
8 260.0 aligned_no_drop aligned -1 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 30 0.5392926410258561 0.991800947030499
9 260.0 aligned_1500ms aligned 1500 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 30 0.5392926410258561 0.991800947030499
10 260.0 aligned_1000ms aligned 1000 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 30 0.5392926410258561 0.991800947030499
11 260.0 aligned_500ms aligned 500 264.62022471910115 1.017770095073466 1730 682511 8 4399 69597 13.261018996174682 31 7402.747049027978 16992 24 0.4039387948720048 0.991800947030499
12 230.0 continuous_no_drop continuous -1 258.21380417335473 1.1226687137971945 1703 670280 0 0 0 59.749842696627866 132 34307.118394888734 75969 127 2.547420289855012 1.0
13 230.0 aligned_no_drop aligned -1 264.62022471910115 1.1505227161700051 1738 686910 0 0 0 76.17493190801534 160 43586.68565075366 92568 155 3.1258550724636294 1.0
14 230.0 aligned_1500ms aligned 1500 264.62022471910115 1.1505227161700051 1641 637811 97 49099 321929 59.44779896084508 87 33911.67327808277 50035 81 1.4180637681158217 1.0
15 230.0 aligned_1000ms aligned 1000 264.62022471910115 1.1505227161700051 1609 623057 129 63853 434885 42.60156656500422 63 24223.970784957004 35495 53 0.9048811594201744 1.0
16 230.0 aligned_500ms aligned 500 264.62022471910115 1.1505227161700051 1580 609090 158 77820 532167 20.03526158908224 32 11270.42233914812 18208 27 0.41907246376802476 0.9999999999999993

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,16 @@
channel_kbps,policy,created_frames,published_frames,intentionally_dropped_frames,partially_transmitted_cancelled_frames,published_after_deadline_frames,published_fraction,actual_update_fps,mean_publication_delay_ms,p95_publication_delay_ms,max_publication_delay_ms,mean_display_age_ms,p95_display_age_ms,max_display_age_ms,display_age_over_500ms_fraction,display_age_over_1000ms_fraction,mean_no_update_duration_ms,p95_no_update_duration_ms,max_no_update_duration_ms,mean_missing_run_frames,p95_missing_run_frames,max_missing_run_frames,delivered_useful_video_kbps,wasted_transmitted_video_bytes,receiver_cleared_frames
300.0,continuous_no_drop,63,63,0,0,0,1.0,2.985553772070626,276.3369365079338,322.0720000000001,330.77299999999,437.2938723131218,593.3333333333333,660.0000000000001,0.3142444658325313,0.0,329.6296296296296,388.9899000000025,397.1729999999969,0.0,0.0,0,157.76975922953451,0,0
300.0,aligned_no_drop,63,63,0,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,157.76975922953451,0,0
300.0,aligned_1500ms,63,63,0,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,157.76975922953451,0,0
300.0,aligned_1000ms,63,63,0,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,157.76975922953451,0,0
300.0,aligned_500ms,63,63,0,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,157.76975922953451,0,0
260.0,continuous_no_drop,63,63,0,0,0,1.0,2.937399678972713,351.2176776557015,445.84648717956554,459.55930769245157,509.63907603464884,690.0000000000005,783.3333333333314,0.5312800769971127,0.0,334.9462365591397,401.4781615384583,411.91761538461515,0.0,0.0,0,157.76975922953451,0,0
260.0,aligned_no_drop,63,63,0,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,157.76975922953451,0,0
260.0,aligned_1500ms,63,63,0,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,157.76975922953451,0,0
260.0,aligned_1000ms,63,63,0,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,157.76975922953451,0,0
260.0,aligned_500ms,63,56,7,7,0,0.8888888888888888,2.648475120385233,367.5917000915918,493.7670000001404,498.6567435899403,584.550850176452,1053.8333333333312,1769.9999999999995,0.6212704523580366,0.060153994225216556,370.8333333333333,664.0000000000219,1270.1227435895887,1.4,2.5999999999999996,3,139.5205136436597,69597,7
230.0,continuous_no_drop,63,63,0,0,0,1.0,2.696629213483146,1445.9947550034299,2505.9953623187953,2583.976811594141,1464.8845043310876,2443.8333333333317,2740.000000000002,0.9716073147256978,0.78055822906641,364.32748538011697,437.67652173912944,464.34782608695,0.0,0.0,0,157.76975922953451,0,0
230.0,aligned_no_drop,63,63,0,0,0,1.0,2.6003210272873196,1777.4951000689596,3067.511884057848,3204.707246376671,1723.7872954764196,2913.333333333334,3206.666666666667,0.9735322425409048,0.8267564966313763,377.57575757575756,412.5426086956473,424.00000000000125,0.0,0.0,0,157.76975922953451,0,0
230.0,aligned_1500ms,63,27,36,36,0,0.42857142857142855,1.3001605136436598,1010.3600644122084,1414.757101449207,1473.0202898550094,4028.4071222329167,11064.833333333332,12103.333333333334,0.9735322425409048,0.8267564966313763,741.6666666666666,416.659130434783,10626.97971014499,36.0,36.0,36,66.39987158908507,321929,36
230.0,aligned_1000ms,63,14,49,49,0,0.2222222222222222,0.6741573033707865,734.7155279503047,977.28927536231,995.617391304334,4667.483156881616,11731.499999999998,12770.0,0.9735322425409048,0.8233878729547642,1384.4444444444443,6308.219130434754,11828.788405797173,24.5,36.65,38,34.951781701444624,434885,49
230.0,aligned_500ms,63,3,60,60,0,0.047619047619047616,0.14446227929373998,429.5149758453909,457.5026086956464,460.9971014492756,5508.034969521976,11731.499999999998,12770.0,0.9696823869104908,0.9076034648700674,5191.666666666666,11634.280579710185,12340.614492753682,30.0,37.2,38,7.567126805778492,532167,60
1 channel_kbps policy created_frames published_frames intentionally_dropped_frames partially_transmitted_cancelled_frames published_after_deadline_frames published_fraction actual_update_fps mean_publication_delay_ms p95_publication_delay_ms max_publication_delay_ms mean_display_age_ms p95_display_age_ms max_display_age_ms display_age_over_500ms_fraction display_age_over_1000ms_fraction mean_no_update_duration_ms p95_no_update_duration_ms max_no_update_duration_ms mean_missing_run_frames p95_missing_run_frames max_missing_run_frames delivered_useful_video_kbps wasted_transmitted_video_bytes receiver_cleared_frames
2 300.0 continuous_no_drop 63 63 0 0 0 1.0 2.985553772070626 276.3369365079338 322.0720000000001 330.77299999999 437.2938723131218 593.3333333333333 660.0000000000001 0.3142444658325313 0.0 329.6296296296296 388.9899000000025 397.1729999999969 0.0 0.0 0 157.76975922953451 0 0
3 300.0 aligned_no_drop 63 63 0 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 157.76975922953451 0 0
4 300.0 aligned_1500ms 63 63 0 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 157.76975922953451 0 0
5 300.0 aligned_1000ms 63 63 0 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 157.76975922953451 0 0
6 300.0 aligned_500ms 63 63 0 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 157.76975922953451 0 0
7 260.0 continuous_no_drop 63 63 0 0 0 1.0 2.937399678972713 351.2176776557015 445.84648717956554 459.55930769245157 509.63907603464884 690.0000000000005 783.3333333333314 0.5312800769971127 0.0 334.9462365591397 401.4781615384583 411.91761538461515 0.0 0.0 0 157.76975922953451 0 0
8 260.0 aligned_no_drop 63 63 0 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 157.76975922953451 0 0
9 260.0 aligned_1500ms 63 63 0 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 157.76975922953451 0 0
10 260.0 aligned_1000ms 63 63 0 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 157.76975922953451 0 0
11 260.0 aligned_500ms 63 56 7 7 0 0.8888888888888888 2.648475120385233 367.5917000915918 493.7670000001404 498.6567435899403 584.550850176452 1053.8333333333312 1769.9999999999995 0.6212704523580366 0.060153994225216556 370.8333333333333 664.0000000000219 1270.1227435895887 1.4 2.5999999999999996 3 139.5205136436597 69597 7
12 230.0 continuous_no_drop 63 63 0 0 0 1.0 2.696629213483146 1445.9947550034299 2505.9953623187953 2583.976811594141 1464.8845043310876 2443.8333333333317 2740.000000000002 0.9716073147256978 0.78055822906641 364.32748538011697 437.67652173912944 464.34782608695 0.0 0.0 0 157.76975922953451 0 0
13 230.0 aligned_no_drop 63 63 0 0 0 1.0 2.6003210272873196 1777.4951000689596 3067.511884057848 3204.707246376671 1723.7872954764196 2913.333333333334 3206.666666666667 0.9735322425409048 0.8267564966313763 377.57575757575756 412.5426086956473 424.00000000000125 0.0 0.0 0 157.76975922953451 0 0
14 230.0 aligned_1500ms 63 27 36 36 0 0.42857142857142855 1.3001605136436598 1010.3600644122084 1414.757101449207 1473.0202898550094 4028.4071222329167 11064.833333333332 12103.333333333334 0.9735322425409048 0.8267564966313763 741.6666666666666 416.659130434783 10626.97971014499 36.0 36.0 36 66.39987158908507 321929 36
15 230.0 aligned_1000ms 63 14 49 49 0 0.2222222222222222 0.6741573033707865 734.7155279503047 977.28927536231 995.617391304334 4667.483156881616 11731.499999999998 12770.0 0.9735322425409048 0.8233878729547642 1384.4444444444443 6308.219130434754 11828.788405797173 24.5 36.65 38 34.951781701444624 434885 49
16 230.0 aligned_500ms 63 3 60 60 0 0.047619047619047616 0.14446227929373998 429.5149758453909 457.5026086956464 460.9971014492756 5508.034969521976 11731.499999999998 12770.0 0.9696823869104908 0.9076034648700674 5191.666666666666 11634.280579710185 12340.614492753682 30.0 37.2 38 7.567126805778492 532167 60

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,19 @@
channel_kbps,policy,control_p95_delay_ms,control_max_delay_ms,control_deadline_misses,control_max_receive_gap_ms,emergency_delay_ms,emergency_deadline_met,emergency_blocker_class,emergency_blocking_delay_ms,telemetry_deadline_misses
300.0,no_drop,17.46633333333314,17.78700000000022,0,65.7596666666671,1.7066666666671892,True,none,0.0,0
300.0,reactive_1500ms,17.46633333333314,17.78700000000022,0,65.7596666666671,1.7066666666671892,True,none,0.0,0
300.0,latest_only,17.46633333333314,17.78700000000022,0,65.7596666666671,1.7066666666671892,True,none,0.0,0
300.0,two_waiting,17.46633333333314,17.78700000000022,0,65.7596666666671,1.7066666666671892,True,none,0.0,0
300.0,predict_1000ms,17.46633333333314,17.78700000000022,0,65.7596666666671,1.7066666666671892,True,none,0.0,0
300.0,predict_500ms,17.46633333333314,17.78700000000022,0,65.7596666666671,1.7066666666671892,True,none,0.0,0
260.0,no_drop,18.907692307691043,20.492307692309808,0,67.81538461538617,1.969230769230279,True,none,0.0,0
260.0,reactive_1500ms,18.907692307691043,20.492307692309808,0,67.81538461538617,1.969230769230279,True,none,0.0,0
260.0,latest_only,18.907692307691043,20.492307692309808,0,67.81538461538617,1.969230769230279,True,none,0.0,0
260.0,two_waiting,18.907692307691043,20.492307692309808,0,67.81538461538617,1.969230769230279,True,none,0.0,0
260.0,predict_1000ms,18.907692307691043,20.492307692309808,0,67.81538461538617,1.969230769230279,True,none,0.0,0
260.0,predict_500ms,18.907692307691043,20.533000000078516,0,67.81538461538617,1.969230769230279,True,none,0.0,0
230.0,no_drop,22.499999999942013,24.556521739064863,0,69.00869565217427,22.330434782542596,True,video,20.10434782602033,0
230.0,reactive_1500ms,22.460869565154475,24.556521739064863,0,69.00869565217427,22.330434782542596,True,video,20.10434782602033,0
230.0,latest_only,22.043478260820315,23.339130434699484,0,69.00869565217427,6.2260869564561006,True,video,3.9999999999338343,0
230.0,two_waiting,22.53913043472089,23.33913043474567,0,69.00869565217427,6.991304347760519,True,video,4.765217391238252,0
230.0,predict_1000ms,22.317391304289558,23.35652173909253,0,69.00869565217427,2.365217391240293,True,video,0.13913043471802666,0
230.0,predict_500ms,22.211510869569295,23.304347826089256,0,69.00869565217427,5.739130434786688,True,video,3.5130434782644215,0
1 channel_kbps policy control_p95_delay_ms control_max_delay_ms control_deadline_misses control_max_receive_gap_ms emergency_delay_ms emergency_deadline_met emergency_blocker_class emergency_blocking_delay_ms telemetry_deadline_misses
2 300.0 no_drop 17.46633333333314 17.78700000000022 0 65.7596666666671 1.7066666666671892 True none 0.0 0
3 300.0 reactive_1500ms 17.46633333333314 17.78700000000022 0 65.7596666666671 1.7066666666671892 True none 0.0 0
4 300.0 latest_only 17.46633333333314 17.78700000000022 0 65.7596666666671 1.7066666666671892 True none 0.0 0
5 300.0 two_waiting 17.46633333333314 17.78700000000022 0 65.7596666666671 1.7066666666671892 True none 0.0 0
6 300.0 predict_1000ms 17.46633333333314 17.78700000000022 0 65.7596666666671 1.7066666666671892 True none 0.0 0
7 300.0 predict_500ms 17.46633333333314 17.78700000000022 0 65.7596666666671 1.7066666666671892 True none 0.0 0
8 260.0 no_drop 18.907692307691043 20.492307692309808 0 67.81538461538617 1.969230769230279 True none 0.0 0
9 260.0 reactive_1500ms 18.907692307691043 20.492307692309808 0 67.81538461538617 1.969230769230279 True none 0.0 0
10 260.0 latest_only 18.907692307691043 20.492307692309808 0 67.81538461538617 1.969230769230279 True none 0.0 0
11 260.0 two_waiting 18.907692307691043 20.492307692309808 0 67.81538461538617 1.969230769230279 True none 0.0 0
12 260.0 predict_1000ms 18.907692307691043 20.492307692309808 0 67.81538461538617 1.969230769230279 True none 0.0 0
13 260.0 predict_500ms 18.907692307691043 20.533000000078516 0 67.81538461538617 1.969230769230279 True none 0.0 0
14 230.0 no_drop 22.499999999942013 24.556521739064863 0 69.00869565217427 22.330434782542596 True video 20.10434782602033 0
15 230.0 reactive_1500ms 22.460869565154475 24.556521739064863 0 69.00869565217427 22.330434782542596 True video 20.10434782602033 0
16 230.0 latest_only 22.043478260820315 23.339130434699484 0 69.00869565217427 6.2260869564561006 True video 3.9999999999338343 0
17 230.0 two_waiting 22.53913043472089 23.33913043474567 0 69.00869565217427 6.991304347760519 True video 4.765217391238252 0
18 230.0 predict_1000ms 22.317391304289558 23.35652173909253 0 69.00869565217427 2.365217391240293 True video 0.13913043471802666 0
19 230.0 predict_500ms 22.211510869569295 23.304347826089256 0 69.00869565217427 5.739130434786688 True video 3.5130434782644215 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,19 @@
channel_kbps,policy,admitted_frames,prediction_rejected_frames,mean_absolute_error_ms,p95_absolute_error_ms,max_absolute_error_ms,published_after_deadline_frames,false_rejections
300.0,no_drop,0,0,0.0,0.0,0.0,0,0
300.0,reactive_1500ms,0,0,0.0,0.0,0.0,0,0
300.0,latest_only,0,0,0.0,0.0,0.0,0,0
300.0,two_waiting,0,0,0.0,0.0,0.0,0,0
300.0,predict_1000ms,63,0,0.0,0.0,0.0,0,0
300.0,predict_500ms,63,0,0.0,0.0,0.0,0,0
260.0,no_drop,0,0,0.0,0.0,0.0,0,0
260.0,reactive_1500ms,0,0,0.0,0.0,0.0,0,0
260.0,latest_only,0,0,0.0,0.0,0.0,0,0
260.0,two_waiting,0,0,0.0,0.0,0.0,0,0
260.0,predict_1000ms,63,0,0.0,0.0,0.0,0,0
260.0,predict_500ms,62,1,0.0,0.0,0.0,0,0
230.0,no_drop,0,0,0.0,0.0,0.0,0,0
230.0,reactive_1500ms,0,0,0.0,0.0,0.0,0,0
230.0,latest_only,0,0,0.0,0.0,0.0,0,0
230.0,two_waiting,0,0,0.0,0.0,0.0,0,0
230.0,predict_1000ms,56,7,0.0,0.0,0.0,0,0
230.0,predict_500ms,47,16,0.0,0.0,0.0,0,0
1 channel_kbps policy admitted_frames prediction_rejected_frames mean_absolute_error_ms p95_absolute_error_ms max_absolute_error_ms published_after_deadline_frames false_rejections
2 300.0 no_drop 0 0 0.0 0.0 0.0 0 0
3 300.0 reactive_1500ms 0 0 0.0 0.0 0.0 0 0
4 300.0 latest_only 0 0 0.0 0.0 0.0 0 0
5 300.0 two_waiting 0 0 0.0 0.0 0.0 0 0
6 300.0 predict_1000ms 63 0 0.0 0.0 0.0 0 0
7 300.0 predict_500ms 63 0 0.0 0.0 0.0 0 0
8 260.0 no_drop 0 0 0.0 0.0 0.0 0 0
9 260.0 reactive_1500ms 0 0 0.0 0.0 0.0 0 0
10 260.0 latest_only 0 0 0.0 0.0 0.0 0 0
11 260.0 two_waiting 0 0 0.0 0.0 0.0 0 0
12 260.0 predict_1000ms 63 0 0.0 0.0 0.0 0 0
13 260.0 predict_500ms 62 1 0.0 0.0 0.0 0 0
14 230.0 no_drop 0 0 0.0 0.0 0.0 0 0
15 230.0 reactive_1500ms 0 0 0.0 0.0 0.0 0 0
16 230.0 latest_only 0 0 0.0 0.0 0.0 0 0
17 230.0 two_waiting 0 0 0.0 0.0 0.0 0 0
18 230.0 predict_1000ms 56 7 0.0 0.0 0.0 0 0
19 230.0 predict_500ms 47 16 0.0 0.0 0.0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,99 @@
Lab035. Упреждающий допуск видеокадров и обслуживание видео целыми кадрами
1. Исходное состояние
- Commit Lab034: b63e36abdb7031d642de8b8138b43cc29e94b759.
- Перед Lab035 рабочее дерево было чистым; main опережала origin/main на два commit.
2. Правило обслуживания
- После первого видеопакета кадр становится активным и не удаляется.
- При повторном выборе видео передаётся следующий пакет активного кадра; команды и телеметрия могут передаваться между пакетами.
- Новый видеокадр начинается только после полного завершения активного; видеопакеты разных кадров не чередуются; отдельный пакет не прерывается.
- Только неактивные кадры могут быть удалены до передачи первого пакета.
3. Теоретическая пропускная способность
speed | nonvideo kbps | remaining video kbps | remaining/aligned | minimum skip | maximum fps
300 | 17.973 | 282.027 | 1.143443 | 0.000000 | 3.000
260 | 17.973 | 242.027 | 0.981268 | 0.018732 | 2.944
230 | 17.973 | 212.027 | 0.859636 | 0.140364 | 2.579
4. Восемнадцать сочетаний
speed | policy | published/drop/partial | fps | age P95 ms | no-update max ms | queue max/waiting frames | waste bytes | prediction MAE/P95/max ms | control P95/max ms | emergency ms
300 | Без удаления | 63/0/0 | 2.986 | 590.000 | 406.506 | 21/1 | 0 | 0.000000/0.000000/0.000000 | 17.466/17.787 | 1.707
300 | Реактивная 1500 мс | 63/0/0 | 2.986 | 590.000 | 406.506 | 21/1 | 0 | 0.000000/0.000000/0.000000 | 17.466/17.787 | 1.707
300 | Самый свежий | 63/0/0 | 2.986 | 590.000 | 406.506 | 21/1 | 0 | 0.000000/0.000000/0.000000 | 17.466/17.787 | 1.707
300 | Два ожидающих | 63/0/0 | 2.986 | 590.000 | 406.506 | 21/1 | 0 | 0.000000/0.000000/0.000000 | 17.466/17.787 | 1.707
300 | Прогноз 1000 мс | 63/0/0 | 2.986 | 590.000 | 406.506 | 21/1 | 0 | 0.000000/0.000000/0.000000 | 17.466/17.787 | 1.707
300 | Прогноз 500 мс | 63/0/0 | 2.986 | 590.000 | 406.506 | 21/1 | 0 | 0.000000/0.000000/0.000000 | 17.466/17.787 | 1.707
260 | Без удаления | 63/0/0 | 2.937 | 773.833 | 417.764 | 37/1 | 0 | 0.000000/0.000000/0.000000 | 18.908/20.492 | 1.969
260 | Реактивная 1500 мс | 63/0/0 | 2.937 | 773.833 | 417.764 | 37/1 | 0 | 0.000000/0.000000/0.000000 | 18.908/20.492 | 1.969
260 | Самый свежий | 63/0/0 | 2.937 | 773.833 | 417.764 | 37/1 | 0 | 0.000000/0.000000/0.000000 | 18.908/20.492 | 1.969
260 | Два ожидающих | 63/0/0 | 2.937 | 773.833 | 417.764 | 37/1 | 0 | 0.000000/0.000000/0.000000 | 18.908/20.492 | 1.969
260 | Прогноз 1000 мс | 63/0/0 | 2.937 | 773.833 | 417.764 | 37/1 | 0 | 0.000000/0.000000/0.000000 | 18.908/20.492 | 1.969
260 | Прогноз 500 мс | 62/1/0 | 2.889 | 703.333 | 524.266 | 29/1 | 0 | 0.000000/0.000000/0.000000 | 18.908/20.533 | 1.969
230 | Без удаления | 63/0/0 | 2.600 | 2913.333 | 424.000 | 160/8 | 0 | 0.000000/0.000000/0.000000 | 22.500/24.557 | 22.330
230 | Реактивная 1500 мс | 27/0/36 | 1.300 | 11064.833 | 10626.980 | 87/4 | 321929 | 0.000000/0.000000/0.000000 | 22.461/24.557 | 22.330
230 | Самый свежий | 56/7/0 | 2.600 | 960.000 | 426.226 | 37/1 | 0 | 0.000000/0.000000/0.000000 | 22.043/23.339 | 6.226
230 | Два ожидающих | 57/6/0 | 2.600 | 1286.667 | 421.183 | 56/2 | 0 | 0.000000/0.000000/0.000000 | 22.539/23.339 | 6.991
230 | Прогноз 1000 мс | 56/7/0 | 2.600 | 1226.667 | 426.226 | 53/2 | 0 | 0.000000/0.000000/0.000000 | 22.317/23.357 | 2.365
230 | Прогноз 500 мс | 47/16/0 | 2.215 | 966.667 | 622.817 | 28/1 | 0 | 0.000000/0.000000/0.000000 | 22.212/23.304 | 5.739
5. Интерпретация
- Реактивная Lab034 начинает кадр без гарантии завершения, затем удаляет остаток: уже переданные байты становятся бесполезными, а обновление не публикуется.
- Удаление до первого пакета исключает бесполезную передачу; обслуживание целыми кадрами гарантирует, что начатый кадр будет опубликован.
- Политика самого свежего уменьшает задержку ожидающих данных, но удаляет больше промежуточных кадров; очередь из двух кадров сохраняет больше последовательных обновлений ценой возраста.
- Прогноз полного завершения учитывает весь размер кадра и будущую периодическую высокоприоритетную нагрузку, поэтому полезнее проверки только текущего возраста.
- В модели точно известны команды 20 Гц, телеметрия 10 Гц и аварийная команда 10,0 с; неизвестные будущие дискретные события не моделируются и в реальной системе потребовали бы запаса.
- При устойчивой перегрузке невозможно одновременно сохранить все кадры, исходное JPEG-качество и малую задержку; требуется уменьшить частоту, качество или заранее пропускать кадры.
- Частота обновления, возраст изображения и длительность отсутствия нового изображения оцениваются одновременно: оптимизация одного показателя может ухудшить остальные.
6. Допущения
- Ошибки и помехи отсутствуют; один общий абстрактный ресурс, форматы Lab028-Lab034 неизменны, активный пакет не прерывается.
- Прогноз не изменяет настоящую очередь; для допущенных кадров сохраняются только агрегированные ошибки, без подробного журнала.
- Политика автоматически не выбирается.
7. Функциональные проверки
- PASS 01_video_frames_do_not_interleave: PASS
- PASS 02_started_frame_never_dropped: PASS
- PASS 03_high_priority_between_frame_packets: PASS
- PASS 04_latest_drops_only_unstarted: PASS
- PASS 05_two_waiting_limit: PASS
- PASS 06_prediction_is_pure: PASS
- PASS 07_prediction_uses_actual_sizes: PASS
- PASS 08_prestart_drop_has_no_waste: PASS
- PASS 09_partial_only_reactive: PASS
- PASS 10_incomplete_not_published: PASS
- PASS 11_crc_layers_pass: PASS
- PASS 12_emergency_never_deleted: PASS
- PASS 13_priority_above_video: PASS
- PASS 14_300kbps_no_unnecessary_loss: PASS
- PASS 15_230kbps_bounded_queue: PASS
- PASS 16_frame_accounting: PASS
- PASS 17_byte_accounting: PASS
- PASS 18_reproducible: PASS
- PASS 19_predict_1000_never_known_late: PASS
- PASS 20_command_delay_bound: PASS
8. Созданные файлы
- protocol/video_frame_scheduler.py
- tests/lab035_video_frame_admission.py
- data/processed/lab035/lab035_summary.csv
- data/processed/lab035/lab035_video_metrics.csv
- data/processed/lab035/lab035_control_metrics.csv
- data/processed/lab035/lab035_prediction_metrics.csv
- data/processed/lab035/lab035_report.txt
- data/processed/lab035/lab035_update_rate.png
- data/processed/lab035/lab035_image_age.png
- data/processed/lab035/lab035_publication_delay.png
- data/processed/lab035/lab035_frame_outcomes.png
- data/processed/lab035/lab035_queue_size.png
- data/processed/lab035/lab035_prediction_accuracy.png
- data/processed/lab035/lab035_control_delay.png
- data/processed/lab035/lab035_policy_comparison.png
9. Итоговый Git status
- Lab035 не добавлена в индекс и не закоммичена.
## main...origin/main [ahead 2]
?? data/processed/lab035/
?? protocol/video_frame_scheduler.py
?? tests/lab035_video_frame_admission.py

View File

@@ -0,0 +1,19 @@
channel_kbps,policy,offered_load_kbps,offered_to_capacity_ratio,transmitted_packets,transmitted_bytes,dropped_before_start_packets,dropped_before_start_bytes,wasted_transmitted_bytes,mean_queue_packets,max_queue_packets,mean_queue_bytes,max_queue_bytes,mean_waiting_video_frames,max_waiting_video_frames,queue_at_source_end_packets,additional_drain_seconds,remaining_video_capacity_kbps,theoretical_minimum_skip_fraction,theoretical_maximum_update_fps
300.0,no_drop,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,0.0043967897271271275,1,13,0.1994403333333281,282.02658105939,0.0,3.0
300.0,reactive_1500ms,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,0.004397126805778977,1,13,0.1994403333333281,282.02658105939,0.0,3.0
300.0,latest_only,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,0.0043967897271271275,1,13,0.1994403333333281,282.02658105939,0.0,3.0
300.0,two_waiting,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,0.0043967897271271275,1,13,0.1994403333333281,282.02658105939,0.0,3.0
300.0,predict_1000ms,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,0.0043967897271271275,1,13,0.1994403333333281,282.02658105939,0.0,3.0
300.0,predict_500ms,264.62022471910115,0.8820674157303372,1738,686910,0,0,0,8.518393226324118,21,4685.69717834665,11415,0.0043967897271271275,1,13,0.1994403333333281,282.02658105939,0.0,3.0
260.0,no_drop,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,0.22409427213244032,1,30,0.5392926410258561,242.02658105939005,0.0187321490116944,2.943803552964917
260.0,reactive_1500ms,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,0.22409428818380472,1,30,0.5392926410258561,242.02658105939005,0.0187321490116944,2.943803552964917
260.0,latest_only,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,0.22409427213244032,1,30,0.5392926410258561,242.02658105939005,0.0187321490116944,2.943803552964917
260.0,two_waiting,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,0.22409427213244032,1,30,0.5392926410258561,242.02658105939005,0.0187321490116944,2.943803552964917
260.0,predict_1000ms,264.62022471910115,1.017770095073466,1738,686910,0,0,0,13.695290291396285,37,7655.6639836816,21131,0.22409427213244032,1,30,0.5392926410258561,242.02658105939005,0.0187321490116944,2.943803552964917
260.0,predict_500ms,264.62022471910115,1.017770095073466,1719,675994,19,10916,0,12.011204749970242,29,6682.897892161229,15804,0.13726051487841454,1,21,0.37186633333345753,242.02658105939005,0.0187321490116944,2.943803552964917
230.0,no_drop,264.62022471910115,1.1505227161700051,1738,686910,0,0,0,76.17493190801534,160,43586.68565075366,92568,3.7476585944586778,8,155,3.1258550724636294,212.02658105939005,0.14036356404385353,2.5789093078684395
230.0,reactive_1500ms,264.62022471910115,1.1505227161700051,1641,637811,0,0,321929,59.44779896084508,87,33911.67327808277,50035,2.785503524321179,4,81,1.4180637681158217,212.02658105939005,0.14036356404385353,2.5789093078684395
230.0,latest_only,264.62022471910115,1.1505227161700051,1614,615407,124,71503,0,19.887121174537647,37,11167.829372157905,21067,0.5720064205456188,1,33,0.6387942028984455,212.02658105939005,0.14036356404385353,2.5789093078684395
230.0,two_waiting,264.62022471910115,1.1505227161700051,1633,626345,105,60565,0,35.493155370922615,56,20128.047561843177,32209,1.456761532556227,2,52,1.0192463768114841,212.02658105939005,0.14036356404385353,2.5789093078684395
230.0,predict_1000ms,264.62022471910115,1.1505227161700051,1612,613851,126,73059,0,32.84338218367999,53,18617.01722113549,30172,1.3064353409168972,2,49,0.5846724637680119,212.02658105939005,0.14036356404385353,2.5789093078684395
230.0,predict_500ms,264.62022471910115,1.1505227161700051,1452,521373,286,165537,0,12.440944832856212,28,6904.035936733742,15168,0.22863162956241578,1,22,0.04693333333332461,212.02658105939005,0.14036356404385353,2.5789093078684395
1 channel_kbps policy offered_load_kbps offered_to_capacity_ratio transmitted_packets transmitted_bytes dropped_before_start_packets dropped_before_start_bytes wasted_transmitted_bytes mean_queue_packets max_queue_packets mean_queue_bytes max_queue_bytes mean_waiting_video_frames max_waiting_video_frames queue_at_source_end_packets additional_drain_seconds remaining_video_capacity_kbps theoretical_minimum_skip_fraction theoretical_maximum_update_fps
2 300.0 no_drop 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 0.0043967897271271275 1 13 0.1994403333333281 282.02658105939 0.0 3.0
3 300.0 reactive_1500ms 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 0.004397126805778977 1 13 0.1994403333333281 282.02658105939 0.0 3.0
4 300.0 latest_only 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 0.0043967897271271275 1 13 0.1994403333333281 282.02658105939 0.0 3.0
5 300.0 two_waiting 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 0.0043967897271271275 1 13 0.1994403333333281 282.02658105939 0.0 3.0
6 300.0 predict_1000ms 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 0.0043967897271271275 1 13 0.1994403333333281 282.02658105939 0.0 3.0
7 300.0 predict_500ms 264.62022471910115 0.8820674157303372 1738 686910 0 0 0 8.518393226324118 21 4685.69717834665 11415 0.0043967897271271275 1 13 0.1994403333333281 282.02658105939 0.0 3.0
8 260.0 no_drop 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 0.22409427213244032 1 30 0.5392926410258561 242.02658105939005 0.0187321490116944 2.943803552964917
9 260.0 reactive_1500ms 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 0.22409428818380472 1 30 0.5392926410258561 242.02658105939005 0.0187321490116944 2.943803552964917
10 260.0 latest_only 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 0.22409427213244032 1 30 0.5392926410258561 242.02658105939005 0.0187321490116944 2.943803552964917
11 260.0 two_waiting 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 0.22409427213244032 1 30 0.5392926410258561 242.02658105939005 0.0187321490116944 2.943803552964917
12 260.0 predict_1000ms 264.62022471910115 1.017770095073466 1738 686910 0 0 0 13.695290291396285 37 7655.6639836816 21131 0.22409427213244032 1 30 0.5392926410258561 242.02658105939005 0.0187321490116944 2.943803552964917
13 260.0 predict_500ms 264.62022471910115 1.017770095073466 1719 675994 19 10916 0 12.011204749970242 29 6682.897892161229 15804 0.13726051487841454 1 21 0.37186633333345753 242.02658105939005 0.0187321490116944 2.943803552964917
14 230.0 no_drop 264.62022471910115 1.1505227161700051 1738 686910 0 0 0 76.17493190801534 160 43586.68565075366 92568 3.7476585944586778 8 155 3.1258550724636294 212.02658105939005 0.14036356404385353 2.5789093078684395
15 230.0 reactive_1500ms 264.62022471910115 1.1505227161700051 1641 637811 0 0 321929 59.44779896084508 87 33911.67327808277 50035 2.785503524321179 4 81 1.4180637681158217 212.02658105939005 0.14036356404385353 2.5789093078684395
16 230.0 latest_only 264.62022471910115 1.1505227161700051 1614 615407 124 71503 0 19.887121174537647 37 11167.829372157905 21067 0.5720064205456188 1 33 0.6387942028984455 212.02658105939005 0.14036356404385353 2.5789093078684395
17 230.0 two_waiting 264.62022471910115 1.1505227161700051 1633 626345 105 60565 0 35.493155370922615 56 20128.047561843177 32209 1.456761532556227 2 52 1.0192463768114841 212.02658105939005 0.14036356404385353 2.5789093078684395
18 230.0 predict_1000ms 264.62022471910115 1.1505227161700051 1612 613851 126 73059 0 32.84338218367999 53 18617.01722113549 30172 1.3064353409168972 2 49 0.5846724637680119 212.02658105939005 0.14036356404385353 2.5789093078684395
19 230.0 predict_500ms 264.62022471910115 1.1505227161700051 1452 521373 286 165537 0 12.440944832856212 28 6904.035936733742 15168 0.22863162956241578 1 22 0.04693333333332461 212.02658105939005 0.14036356404385353 2.5789093078684395

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,19 @@
channel_kbps,policy,created_frames,started_frames,published_frames,dropped_before_start_frames,partially_transmitted_cancelled_frames,published_fraction,actual_update_fps,mean_publication_delay_ms,p95_publication_delay_ms,max_publication_delay_ms,mean_display_age_ms,p95_display_age_ms,max_display_age_ms,display_age_over_500ms_fraction,display_age_over_1000ms_fraction,mean_no_update_duration_ms,p95_no_update_duration_ms,max_no_update_duration_ms,mean_missing_run_frames,p95_missing_run_frames,max_missing_run_frames,mean_publication_gap_ms,max_publication_gap_ms,transmitted_video_bytes,dropped_before_start_video_bytes,wasted_transmitted_video_bytes,delivered_useful_video_kbps
300.0,no_drop,63,63,63,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,333.07759016393436,406.506333333331,640254,0,0,157.76975922953451
300.0,reactive_1500ms,63,63,63,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,333.03312365591387,406.506333333331,640254,0,0,157.76975922953451
300.0,latest_only,63,63,63,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,333.07759016393436,406.506333333331,640254,0,0,157.76975922953451
300.0,two_waiting,63,63,63,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,333.07759016393436,406.506333333331,640254,0,0,157.76975922953451
300.0,predict_1000ms,63,63,63,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,333.07759016393436,406.506333333331,640254,0,0,157.76975922953451
300.0,predict_500ms,63,63,63,0,0,1.0,2.985553772070626,272.2167195767169,293.13066666666225,301.84000000000003,433.2836060314408,589.9999999999999,623.3333333333348,0.30221366698748797,0.0,329.6296296296296,355.05666666666684,406.506333333331,0.0,0.0,0,333.07759016393436,406.506333333331,640254,0,0,157.76975922953451
260.0,no_drop,63,63,63,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,337.6377833333365,417.7637692307723,640254,0,0,157.76975922953451
260.0,reactive_1500ms,63,63,63,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,337.64599379652947,417.7637692307723,640254,0,0,157.76975922953451
260.0,latest_only,63,63,63,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,337.6377833333365,417.7637692307723,640254,0,0,157.76975922953451
260.0,two_waiting,63,63,63,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,337.6377833333365,417.7637692307723,640254,0,0,157.76975922953451
260.0,predict_1000ms,63,63,63,0,0,1.0,2.937399678972713,393.259570207603,579.4598205129968,620.584948718161,546.5335258261151,773.833333333333,943.3333333333316,0.62223291626564,0.0,334.9462365591397,363.58000000001095,417.7637692307723,0.0,0.0,0,337.6377833333365,417.7637692307723,640254,0,0,157.76975922953451
260.0,predict_500ms,63,62,62,1,0,0.9841269841269841,2.8892455858747996,360.2011848635393,447.1807256411333,465.7234102564871,523.434392043632,703.3333333333336,983.3333333333343,0.5654475457170356,0.0,340.4371584699453,365.69230769231353,524.2659999999332,1.0,1.0,1,340.28908604954535,524.2659999999332,629338,10916,0,155.0561797752809
230.0,no_drop,63,63,63,0,0,1.0,2.6003210272873196,1777.4951000689596,3067.511884057848,3204.707246376671,1723.7872954764196,2913.333333333334,3206.666666666667,0.9735322425409048,0.8267564966313763,377.57575757575756,412.5426086956473,424.00000000000125,0.0,0.0,0,380.14569319113855,424.00000000000125,640254,0,0,157.76975922953451
230.0,reactive_1500ms,63,63,27,0,36,0.42857142857142855,1.3001605136436598,1010.3600644122084,1414.757101449207,1473.0202898550094,4028.4071222329167,11064.833333333332,12103.333333333334,0.9735322425409048,0.8267564966313763,741.6666666666666,416.659130434783,10626.97971014499,36.0,36.0,36,374.5458193979908,424.00000000000125,591155,0,321929,66.39987158908507
230.0,latest_only,63,56,56,7,0,0.8888888888888888,2.6003210272873196,536.8126293995401,685.881159420235,717.6463768114871,709.5107475136352,960.0000000000001,1076.6666666666679,0.9100096246390761,0.02165543792107796,377.5757575757575,416.6017391304294,426.22608695652355,1.0,1.0,1,381.4884331419178,426.22608695652355,568751,71503,0,140.1904333868379
230.0,two_waiting,63,57,57,6,0,0.9047619047619048,2.6003210272873196,838.9013984235485,1026.1286956521203,1098.0985507245257,998.5707410972092,1286.6666666666665,1440.0000000000014,0.9735322425409048,0.546679499518768,377.5757575757576,406.0417391304327,421.1826086956538,1.0,1.0,1,381.49433962263976,421.1826086956538,579689,60565,0,142.8577849117175
230.0,predict_1000ms,63,56,56,7,0,0.8888888888888888,2.6003210272873196,771.422153209065,952.4115942028234,971.5478260868871,943.7102983638115,1226.6666666666665,1359.9999999999995,0.9735322425409048,0.41193455245428295,377.57575757575756,405.373913043476,426.22608695652355,1.0,1.0,1,380.15028712058887,426.22608695652355,567195,73059,0,139.75743178170146
230.0,predict_500ms,63,47,47,16,0,0.746031746031746,2.215088282504013,412.55253931544405,466.07430289855006,482.2141594203,641.0153994225219,966.6666666666686,1056.6666666666683,0.7776708373435997,0.026467757459095284,441.84397163120565,614.827726086956,622.8173913043555,1.0,1.0,1,444.19246376811583,622.8173913043555,474717,165537,0,117.06375601926163
1 channel_kbps policy created_frames started_frames published_frames dropped_before_start_frames partially_transmitted_cancelled_frames published_fraction actual_update_fps mean_publication_delay_ms p95_publication_delay_ms max_publication_delay_ms mean_display_age_ms p95_display_age_ms max_display_age_ms display_age_over_500ms_fraction display_age_over_1000ms_fraction mean_no_update_duration_ms p95_no_update_duration_ms max_no_update_duration_ms mean_missing_run_frames p95_missing_run_frames max_missing_run_frames mean_publication_gap_ms max_publication_gap_ms transmitted_video_bytes dropped_before_start_video_bytes wasted_transmitted_video_bytes delivered_useful_video_kbps
2 300.0 no_drop 63 63 63 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 333.07759016393436 406.506333333331 640254 0 0 157.76975922953451
3 300.0 reactive_1500ms 63 63 63 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 333.03312365591387 406.506333333331 640254 0 0 157.76975922953451
4 300.0 latest_only 63 63 63 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 333.07759016393436 406.506333333331 640254 0 0 157.76975922953451
5 300.0 two_waiting 63 63 63 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 333.07759016393436 406.506333333331 640254 0 0 157.76975922953451
6 300.0 predict_1000ms 63 63 63 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 333.07759016393436 406.506333333331 640254 0 0 157.76975922953451
7 300.0 predict_500ms 63 63 63 0 0 1.0 2.985553772070626 272.2167195767169 293.13066666666225 301.84000000000003 433.2836060314408 589.9999999999999 623.3333333333348 0.30221366698748797 0.0 329.6296296296296 355.05666666666684 406.506333333331 0.0 0.0 0 333.07759016393436 406.506333333331 640254 0 0 157.76975922953451
8 260.0 no_drop 63 63 63 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 337.6377833333365 417.7637692307723 640254 0 0 157.76975922953451
9 260.0 reactive_1500ms 63 63 63 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 337.64599379652947 417.7637692307723 640254 0 0 157.76975922953451
10 260.0 latest_only 63 63 63 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 337.6377833333365 417.7637692307723 640254 0 0 157.76975922953451
11 260.0 two_waiting 63 63 63 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 337.6377833333365 417.7637692307723 640254 0 0 157.76975922953451
12 260.0 predict_1000ms 63 63 63 0 0 1.0 2.937399678972713 393.259570207603 579.4598205129968 620.584948718161 546.5335258261151 773.833333333333 943.3333333333316 0.62223291626564 0.0 334.9462365591397 363.58000000001095 417.7637692307723 0.0 0.0 0 337.6377833333365 417.7637692307723 640254 0 0 157.76975922953451
13 260.0 predict_500ms 63 62 62 1 0 0.9841269841269841 2.8892455858747996 360.2011848635393 447.1807256411333 465.7234102564871 523.434392043632 703.3333333333336 983.3333333333343 0.5654475457170356 0.0 340.4371584699453 365.69230769231353 524.2659999999332 1.0 1.0 1 340.28908604954535 524.2659999999332 629338 10916 0 155.0561797752809
14 230.0 no_drop 63 63 63 0 0 1.0 2.6003210272873196 1777.4951000689596 3067.511884057848 3204.707246376671 1723.7872954764196 2913.333333333334 3206.666666666667 0.9735322425409048 0.8267564966313763 377.57575757575756 412.5426086956473 424.00000000000125 0.0 0.0 0 380.14569319113855 424.00000000000125 640254 0 0 157.76975922953451
15 230.0 reactive_1500ms 63 63 27 0 36 0.42857142857142855 1.3001605136436598 1010.3600644122084 1414.757101449207 1473.0202898550094 4028.4071222329167 11064.833333333332 12103.333333333334 0.9735322425409048 0.8267564966313763 741.6666666666666 416.659130434783 10626.97971014499 36.0 36.0 36 374.5458193979908 424.00000000000125 591155 0 321929 66.39987158908507
16 230.0 latest_only 63 56 56 7 0 0.8888888888888888 2.6003210272873196 536.8126293995401 685.881159420235 717.6463768114871 709.5107475136352 960.0000000000001 1076.6666666666679 0.9100096246390761 0.02165543792107796 377.5757575757575 416.6017391304294 426.22608695652355 1.0 1.0 1 381.4884331419178 426.22608695652355 568751 71503 0 140.1904333868379
17 230.0 two_waiting 63 57 57 6 0 0.9047619047619048 2.6003210272873196 838.9013984235485 1026.1286956521203 1098.0985507245257 998.5707410972092 1286.6666666666665 1440.0000000000014 0.9735322425409048 0.546679499518768 377.5757575757576 406.0417391304327 421.1826086956538 1.0 1.0 1 381.49433962263976 421.1826086956538 579689 60565 0 142.8577849117175
18 230.0 predict_1000ms 63 56 56 7 0 0.8888888888888888 2.6003210272873196 771.422153209065 952.4115942028234 971.5478260868871 943.7102983638115 1226.6666666666665 1359.9999999999995 0.9735322425409048 0.41193455245428295 377.57575757575756 405.373913043476 426.22608695652355 1.0 1.0 1 380.15028712058887 426.22608695652355 567195 73059 0 139.75743178170146
19 230.0 predict_500ms 63 47 47 16 0 0.746031746031746 2.215088282504013 412.55253931544405 466.07430289855006 482.2141594203 641.0153994225219 966.6666666666686 1056.6666666666683 0.7776708373435997 0.026467757459095284 441.84397163120565 614.827726086956 622.8173913043555 1.0 1.0 1 444.19246376811583 622.8173913043555 474717 165537 0 117.06375601926163

195
protocol/link_packet.py Normal file
View File

@@ -0,0 +1,195 @@
"""Common Lab033 link packet shared by commands, telemetry, and video.
The fixed 32-byte header uses network byte order and no implicit padding::
!4sBBBBHIQHII
Offset Size Field
0 4 magic (b"SLP1")
4 1 version
5 1 traffic_class
6 1 direction
7 1 flags
8 2 stream_id
10 4 sequence_number
14 8 generation_time_us
22 2 deadline_ms
24 4 payload_length
28 4 packet_crc32
``packet_crc32`` is IEEE CRC32 over the complete header with that field set
to zero, followed by the complete payload.
"""
from __future__ import annotations
from dataclasses import dataclass, replace
from enum import IntEnum
import struct
import zlib
MAGIC = b"SLP1"
VERSION = 1
HEADER_FORMAT = "!4sBBBBHIQHII"
HEADER_SIZE = struct.calcsize(HEADER_FORMAT)
SUPPORTED_FLAGS_MASK = 0
class TrafficClass(IntEnum):
"""Traffic classes in descending scheduling priority."""
EMERGENCY = 1
CONTROL = 2
TELEMETRY = 3
VIDEO = 4
class Direction(IntEnum):
"""Logical direction of a packet on the shared modelled resource."""
GROUND_TO_ROVER = 1
ROVER_TO_GROUND = 2
class LinkPacketError(ValueError):
"""Base class for malformed Lab033 link packets."""
class LinkPacketCRCError(LinkPacketError):
"""The Lab033 packet header or payload failed CRC32 validation."""
@dataclass(frozen=True)
class LinkPacket:
"""Decoded common packet, including its CRC-validated payload."""
traffic_class: TrafficClass
direction: Direction
stream_id: int
sequence_number: int
generation_time_us: int
deadline_ms: int
payload: bytes
flags: int = 0
version: int = VERSION
packet_crc32: int = 0
def crc32(data: bytes) -> int:
"""Return unsigned IEEE CRC32."""
return zlib.crc32(data) & 0xFFFFFFFF
def _validated(packet: LinkPacket) -> LinkPacket:
if not isinstance(packet, LinkPacket):
raise TypeError("packet must be LinkPacket")
try:
traffic_class = TrafficClass(packet.traffic_class)
direction = Direction(packet.direction)
except ValueError as error:
raise LinkPacketError("unsupported enumeration value") from error
if packet.version != VERSION:
raise LinkPacketError("unsupported link packet version")
if packet.flags & ~SUPPORTED_FLAGS_MASK:
raise LinkPacketError("unsupported link packet flags")
if not 0 <= packet.stream_id <= 0xFFFF:
raise LinkPacketError("stream_id is outside uint16")
if not 0 <= packet.sequence_number <= 0xFFFFFFFF:
raise LinkPacketError("sequence_number is outside uint32")
if not 0 <= packet.generation_time_us <= 0xFFFFFFFFFFFFFFFF:
raise LinkPacketError("generation_time_us is outside uint64")
if not 0 <= packet.deadline_ms <= 0xFFFF:
raise LinkPacketError("deadline_ms is outside uint16")
if not isinstance(packet.payload, (bytes, bytearray)):
raise TypeError("payload must be bytes or bytearray")
payload = bytes(packet.payload)
if len(payload) > 0xFFFFFFFF:
raise LinkPacketError("payload is outside uint32 length")
return replace(
packet,
traffic_class=traffic_class,
direction=direction,
payload=payload,
)
def _pack_header(packet: LinkPacket, crc_value: int) -> bytes:
return struct.pack(
HEADER_FORMAT,
MAGIC,
packet.version,
int(packet.traffic_class),
int(packet.direction),
packet.flags,
packet.stream_id,
packet.sequence_number,
packet.generation_time_us,
packet.deadline_ms,
len(packet.payload),
crc_value,
)
def encode_link_packet(packet: LinkPacket) -> bytes:
"""Serialize a common packet and calculate its CRC32."""
packet = _validated(packet)
header_without_crc = _pack_header(packet, 0)
packet_crc32 = crc32(header_without_crc + packet.payload)
return _pack_header(packet, packet_crc32) + packet.payload
def decode_link_packet(wire_packet: bytes) -> LinkPacket:
"""Deserialize a common packet and validate its format and CRC32."""
if not isinstance(wire_packet, (bytes, bytearray)):
raise TypeError("wire_packet must be bytes or bytearray")
wire_packet = bytes(wire_packet)
if len(wire_packet) < HEADER_SIZE:
raise LinkPacketError("link packet is shorter than its header")
(
magic,
version,
traffic_class,
direction,
flags,
stream_id,
sequence_number,
generation_time_us,
deadline_ms,
payload_length,
received_crc32,
) = struct.unpack(HEADER_FORMAT, wire_packet[:HEADER_SIZE])
if magic != MAGIC:
raise LinkPacketError("invalid link packet magic")
if len(wire_packet) != HEADER_SIZE + payload_length:
raise LinkPacketError("payload_length does not match packet length")
try:
packet = LinkPacket(
traffic_class=TrafficClass(traffic_class),
direction=Direction(direction),
stream_id=stream_id,
sequence_number=sequence_number,
generation_time_us=generation_time_us,
deadline_ms=deadline_ms,
payload=wire_packet[HEADER_SIZE:],
flags=flags,
version=version,
packet_crc32=received_crc32,
)
except ValueError as error:
raise LinkPacketError("unsupported enumeration value") from error
packet = _validated(packet)
calculated_crc32 = crc32(_pack_header(packet, 0) + packet.payload)
if calculated_crc32 != received_crc32:
raise LinkPacketCRCError(
"link packet CRC mismatch: "
f"received 0x{received_crc32:08X}, "
f"calculated 0x{calculated_crc32:08X}"
)
return packet
assert HEADER_SIZE == 32

View File

@@ -0,0 +1,222 @@
"""Deterministic non-preemptive queue schedulers for Lab033."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Iterable
from protocol.link_packet import (
HEADER_SIZE,
LinkPacket,
TrafficClass,
encode_link_packet,
)
TIME_EPSILON_SECONDS = 1e-12
class SchedulerMode(str, Enum):
FIFO = "fifo"
STRICT_PRIORITY = "strict_priority"
LATEST_STATE = "latest_state"
@dataclass(frozen=True)
class QueuedPacket:
"""A packet plus a stable global arrival-order tie breaker."""
packet: LinkPacket
arrival_order: int
@property
def generation_time_seconds(self) -> float:
return self.packet.generation_time_us / 1_000_000.0
@property
def wire_size_bytes(self) -> int:
return HEADER_SIZE + len(self.packet.payload)
@dataclass(frozen=True)
class Replacement:
removed: QueuedPacket
replacement: QueuedPacket
@dataclass(frozen=True)
class ScheduledPacket:
queued: QueuedPacket
start_seconds: float
end_seconds: float
blocked_by: QueuedPacket | None
blocking_delay_seconds: float
wire_packet: bytes
@dataclass(frozen=True)
class QueueSample:
time_seconds: float
packets: int
bytes: int
@dataclass(frozen=True)
class ScheduleResult:
mode: SchedulerMode
channel_bitrate_bps: float
transmitted: tuple[ScheduledPacket, ...]
replacements: tuple[Replacement, ...]
queue_samples: tuple[QueueSample, ...]
def transmission_duration_seconds(
packet_size_bytes: int,
channel_bitrate_bps: float,
) -> float:
"""Return exact serialization duration for a complete packet."""
if packet_size_bytes < 0:
raise ValueError("packet size must not be negative")
if channel_bitrate_bps <= 0.0:
raise ValueError("channel bitrate must be positive")
return packet_size_bytes * 8.0 / channel_bitrate_bps
def _selection_key(item: QueuedPacket, mode: SchedulerMode) -> tuple[int, int]:
if mode is SchedulerMode.FIFO:
return (0, item.arrival_order)
return (int(item.packet.traffic_class), item.arrival_order)
def _enqueue(
ready: list[QueuedPacket],
item: QueuedPacket,
mode: SchedulerMode,
replacements: list[Replacement],
) -> None:
if (
mode is SchedulerMode.LATEST_STATE
and item.packet.traffic_class
in (TrafficClass.CONTROL, TrafficClass.TELEMETRY)
):
retained = []
for old in ready:
if (
old.packet.traffic_class == item.packet.traffic_class
and old.packet.stream_id == item.packet.stream_id
):
replacements.append(Replacement(old, item))
else:
retained.append(old)
ready[:] = retained
ready.append(item)
def schedule_packets(
packets: Iterable[QueuedPacket],
mode: SchedulerMode,
channel_bitrate_bps: float,
) -> ScheduleResult:
"""Serialize all packets through one non-preemptive shared resource.
The caller supplies a unique ``arrival_order``. Packets are admitted by
``(generation_time, arrival_order)``. FIFO selects the smallest arrival
order; priority modes select the smallest ``TrafficClass`` and preserve
arrival order within a class. Latest-state replacement only touches items
still waiting in ``ready`` and therefore can never remove an active packet.
"""
try:
mode = SchedulerMode(mode)
except ValueError as error:
raise ValueError("unsupported scheduler mode") from error
if channel_bitrate_bps <= 0.0:
raise ValueError("channel bitrate must be positive")
arrivals = sorted(
tuple(packets),
key=lambda item: (
item.generation_time_seconds,
item.arrival_order,
),
)
if len({item.arrival_order for item in arrivals}) != len(arrivals):
raise ValueError("arrival_order values must be unique")
ready: list[QueuedPacket] = []
transmitted: list[ScheduledPacket] = []
replacements: list[Replacement] = []
samples: list[QueueSample] = []
blocker_by_order: dict[int, tuple[QueuedPacket, float]] = {}
cursor = 0.0
arrival_index = 0
def sample(time_seconds: float) -> None:
samples.append(
QueueSample(
time_seconds=time_seconds,
packets=len(ready),
bytes=sum(item.wire_size_bytes for item in ready),
)
)
while arrival_index < len(arrivals) or ready:
if not ready:
cursor = max(cursor, arrivals[arrival_index].generation_time_seconds)
while (
arrival_index < len(arrivals)
and arrivals[arrival_index].generation_time_seconds
<= cursor + TIME_EPSILON_SECONDS
):
_enqueue(ready, arrivals[arrival_index], mode, replacements)
arrival_index += 1
sample(cursor)
selected_index = min(
range(len(ready)),
key=lambda index: _selection_key(ready[index], mode),
)
selected = ready.pop(selected_index)
start = max(cursor, selected.generation_time_seconds)
wire_packet = encode_link_packet(selected.packet)
end = start + transmission_duration_seconds(
len(wire_packet), channel_bitrate_bps
)
while (
arrival_index < len(arrivals)
and arrivals[arrival_index].generation_time_seconds
<= end + TIME_EPSILON_SECONDS
):
arriving = arrivals[arrival_index]
if arriving.generation_time_seconds > start + TIME_EPSILON_SECONDS:
blocker_by_order[arriving.arrival_order] = (
selected,
max(0.0, end - arriving.generation_time_seconds),
)
_enqueue(ready, arriving, mode, replacements)
arrival_index += 1
blocker, blocking_delay = blocker_by_order.get(
selected.arrival_order, (None, 0.0)
)
transmitted.append(
ScheduledPacket(
queued=selected,
start_seconds=start,
end_seconds=end,
blocked_by=blocker,
blocking_delay_seconds=blocking_delay,
wire_packet=wire_packet,
)
)
cursor = end
sample(cursor)
return ScheduleResult(
mode=mode,
channel_bitrate_bps=channel_bitrate_bps,
transmitted=tuple(transmitted),
replacements=tuple(replacements),
queue_samples=tuple(samples),
)

View File

@@ -0,0 +1,224 @@
"""Frame-aware stale-video policy for the Lab034 shared link queue.
The scheduler remains strict-priority, state-replacing, and non-preemptive.
Unlike Lab033, packet availability is separate from the timestamp stored in
the common header. Every aligned video packet therefore carries the source
composite-frame generation time while FEC construction metadata remains local
to the scheduler.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Iterable
from protocol.link_packet import HEADER_SIZE, LinkPacket, TrafficClass, encode_link_packet
from protocol.priority_scheduler import transmission_duration_seconds
TIME_EPSILON_SECONDS = 1e-12
@dataclass(frozen=True)
class AgePolicyPacket:
"""A common packet plus non-serialized availability/frame metadata."""
packet: LinkPacket
arrival_order: int
available_time_us: int
composite_frame_id: int | None = None
@property
def available_time_seconds(self) -> float:
return self.available_time_us / 1_000_000.0
@property
def frame_generation_seconds(self) -> float:
return self.packet.generation_time_us / 1_000_000.0
@property
def wire_size_bytes(self) -> int:
return HEADER_SIZE + len(self.packet.payload)
@dataclass(frozen=True)
class StateReplacement:
removed: AgePolicyPacket
replacement: AgePolicyPacket
time_seconds: float
@dataclass(frozen=True)
class DroppedVideoPacket:
item: AgePolicyPacket
drop_time_seconds: float
@dataclass(frozen=True)
class AgeScheduledPacket:
item: AgePolicyPacket
start_seconds: float
end_seconds: float
blocked_by: AgePolicyPacket | None
blocking_delay_seconds: float
wire_packet: bytes
@dataclass(frozen=True)
class AgeScheduleResult:
channel_bitrate_bps: float
max_video_age_seconds: float | None
transmitted: tuple[AgeScheduledPacket, ...]
dropped_video: tuple[DroppedVideoPacket, ...]
replacements: tuple[StateReplacement, ...]
def _enqueue(
ready: list[AgePolicyPacket],
item: AgePolicyPacket,
replacements: list[StateReplacement],
) -> None:
if item.packet.traffic_class in (TrafficClass.CONTROL, TrafficClass.TELEMETRY):
retained = []
for old in ready:
if (
old.packet.traffic_class == item.packet.traffic_class
and old.packet.stream_id == item.packet.stream_id
):
replacements.append(
StateReplacement(old, item, item.available_time_seconds)
)
else:
retained.append(old)
ready[:] = retained
ready.append(item)
def schedule_with_video_age(
packets: Iterable[AgePolicyPacket],
channel_bitrate_bps: float,
max_video_age_seconds: float | None,
) -> AgeScheduleResult:
"""Run a strict-priority non-preemptive scheduler with frame drops.
At every packet-selection instant all queued packets belonging to a video
frame older than ``max_video_age_seconds`` are removed together. An active
packet has already left the queue and is never interrupted.
"""
if channel_bitrate_bps <= 0.0:
raise ValueError("channel bitrate must be positive")
if max_video_age_seconds is not None and max_video_age_seconds <= 0.0:
raise ValueError("maximum video age must be positive")
arrivals = sorted(
tuple(packets),
key=lambda item: (item.available_time_seconds, item.arrival_order),
)
if len({item.arrival_order for item in arrivals}) != len(arrivals):
raise ValueError("arrival_order values must be unique")
for item in arrivals:
if item.packet.traffic_class is TrafficClass.VIDEO:
if item.composite_frame_id is None:
raise ValueError("video packet lacks composite_frame_id")
elif item.composite_frame_id is not None:
raise ValueError("non-video packet has composite_frame_id")
ready: list[AgePolicyPacket] = []
transmitted: list[AgeScheduledPacket] = []
dropped: list[DroppedVideoPacket] = []
replacements: list[StateReplacement] = []
blocker_by_order: dict[int, tuple[AgePolicyPacket, float]] = {}
dropped_frames: set[int] = set()
arrival_index = 0
cursor = 0.0
def admit_until(limit: float, active: AgePolicyPacket | None = None, active_end: float = 0.0) -> None:
nonlocal arrival_index
while (
arrival_index < len(arrivals)
and arrivals[arrival_index].available_time_seconds
<= limit + TIME_EPSILON_SECONDS
):
item = arrivals[arrival_index]
arrival_index += 1
if (
active is not None
and item.available_time_seconds > cursor + TIME_EPSILON_SECONDS
):
blocker_by_order[item.arrival_order] = (
active,
max(0.0, active_end - item.available_time_seconds),
)
if item.composite_frame_id in dropped_frames:
dropped.append(DroppedVideoPacket(item, limit))
else:
_enqueue(ready, item, replacements)
def expire_video(now: float) -> None:
if max_video_age_seconds is None:
return
expired_frames = {
item.composite_frame_id
for item in ready
if (
item.packet.traffic_class is TrafficClass.VIDEO
and now - item.frame_generation_seconds
> max_video_age_seconds + TIME_EPSILON_SECONDS
)
}
if not expired_frames:
return
retained = []
for item in ready:
if item.composite_frame_id in expired_frames:
dropped.append(DroppedVideoPacket(item, now))
assert item.composite_frame_id is not None
dropped_frames.add(item.composite_frame_id)
else:
retained.append(item)
ready[:] = retained
while arrival_index < len(arrivals) or ready:
if not ready:
cursor = max(cursor, arrivals[arrival_index].available_time_seconds)
admit_until(cursor)
expire_video(cursor)
if not ready:
continue
selected_index = min(
range(len(ready)),
key=lambda index: (
int(ready[index].packet.traffic_class),
ready[index].arrival_order,
),
)
selected = ready.pop(selected_index)
start = max(cursor, selected.available_time_seconds)
wire_packet = encode_link_packet(selected.packet)
end = start + transmission_duration_seconds(
len(wire_packet), channel_bitrate_bps
)
cursor = start
admit_until(end, selected, end)
blocker, blocking_delay = blocker_by_order.get(
selected.arrival_order, (None, 0.0)
)
transmitted.append(
AgeScheduledPacket(
item=selected,
start_seconds=start,
end_seconds=end,
blocked_by=blocker,
blocking_delay_seconds=blocking_delay,
wire_packet=wire_packet,
)
)
cursor = end
return AgeScheduleResult(
channel_bitrate_bps=channel_bitrate_bps,
max_video_age_seconds=max_video_age_seconds,
transmitted=tuple(transmitted),
dropped_video=tuple(dropped),
replacements=tuple(replacements),
)

View File

@@ -0,0 +1,330 @@
"""Whole-frame video scheduling and predictive admission for Lab035."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Iterable
from protocol.link_packet import TrafficClass, encode_link_packet
from protocol.priority_scheduler import transmission_duration_seconds
from protocol.video_age_policy import AgePolicyPacket
TIME_EPSILON_SECONDS = 1e-12
class FramePolicy(str, Enum):
NO_DROP = "no_drop"
LATEST_ONLY = "latest_only"
TWO_WAITING = "two_waiting"
PREDICT_1000MS = "predict_1000ms"
PREDICT_500MS = "predict_500ms"
@property
def deadline_seconds(self) -> float | None:
if self is FramePolicy.PREDICT_1000MS:
return 1.0
if self is FramePolicy.PREDICT_500MS:
return 0.5
return None
@dataclass(frozen=True)
class VideoFrameGroup:
composite_frame_id: int
generation_time_us: int
packets: tuple[AgePolicyPacket, ...]
@property
def generation_time_seconds(self) -> float:
return self.generation_time_us / 1_000_000.0
@property
def wire_size_bytes(self) -> int:
return sum(packet.wire_size_bytes for packet in self.packets)
@dataclass(frozen=True)
class FrameDrop:
frame: VideoFrameGroup
drop_time_seconds: float
reason: str
@dataclass(frozen=True)
class StateReplacement:
removed: AgePolicyPacket
replacement: AgePolicyPacket
time_seconds: float
@dataclass(frozen=True)
class FrameScheduledPacket:
item: AgePolicyPacket
start_seconds: float
end_seconds: float
blocked_by: AgePolicyPacket | None
blocking_delay_seconds: float
wire_packet: bytes
@dataclass(frozen=True)
class FrameAdmission:
composite_frame_id: int
predicted_completion_seconds: float
actual_completion_seconds: float
@property
def prediction_error_seconds(self) -> float:
return self.actual_completion_seconds - self.predicted_completion_seconds
@dataclass(frozen=True)
class FrameScheduleResult:
policy: FramePolicy
channel_bitrate_bps: float
transmitted: tuple[FrameScheduledPacket, ...]
dropped_frames: tuple[FrameDrop, ...]
replacements: tuple[StateReplacement, ...]
started_frame_ids: tuple[int, ...]
completed_frame_ids: tuple[int, ...]
admissions: tuple[FrameAdmission, ...]
def _replace_state(
ready: list[AgePolicyPacket],
item: AgePolicyPacket,
replacements: list[StateReplacement],
) -> None:
if item.packet.traffic_class in (TrafficClass.CONTROL, TrafficClass.TELEMETRY):
retained = []
for old in ready:
if (
old.packet.traffic_class == item.packet.traffic_class
and old.packet.stream_id == item.packet.stream_id
):
replacements.append(StateReplacement(old, item, item.available_time_seconds))
else:
retained.append(old)
ready[:] = retained
ready.append(item)
def _serve_high_priority(
cursor: float,
ready: list[AgePolicyPacket],
future: tuple[AgePolicyPacket, ...],
future_index: int,
channel_bitrate_bps: float,
) -> tuple[float, int, list[AgePolicyPacket]]:
"""Pure local helper used only by the completion predictor."""
copied_ready = list(ready)
index = future_index
while index < len(future) and future[index].available_time_seconds <= cursor + TIME_EPSILON_SECONDS:
item = future[index]
index += 1
copied_ready = [
old for old in copied_ready
if not (
old.packet.traffic_class == item.packet.traffic_class
and old.packet.stream_id == item.packet.stream_id
and item.packet.traffic_class in (TrafficClass.CONTROL, TrafficClass.TELEMETRY)
)
]
copied_ready.append(item)
while copied_ready:
selected = min(
copied_ready,
key=lambda item: (int(item.packet.traffic_class), item.arrival_order),
)
copied_ready.remove(selected)
cursor += transmission_duration_seconds(selected.wire_size_bytes, channel_bitrate_bps)
while index < len(future) and future[index].available_time_seconds <= cursor + TIME_EPSILON_SECONDS:
item = future[index]
index += 1
copied_ready = [
old for old in copied_ready
if not (
old.packet.traffic_class == item.packet.traffic_class
and old.packet.stream_id == item.packet.stream_id
and item.packet.traffic_class in (TrafficClass.CONTROL, TrafficClass.TELEMETRY)
)
]
copied_ready.append(item)
return cursor, index, copied_ready
def predict_frame_completion(
current_time_seconds: float,
frame: VideoFrameGroup,
channel_bitrate_bps: float,
ready_high_priority: Iterable[AgePolicyPacket],
future_high_priority: Iterable[AgePolicyPacket],
active_packet_remaining_seconds: float = 0.0,
) -> float:
"""Predict full-frame completion without mutating any caller collection.
Known periodic commands, telemetry, and the scheduled emergency event are
simulated exactly at packet boundaries. Unknown future discrete events are
outside the Lab035 traffic model and therefore cannot be included.
"""
if channel_bitrate_bps <= 0.0:
raise ValueError("channel bitrate must be positive")
if active_packet_remaining_seconds < 0.0:
raise ValueError("active packet remainder must not be negative")
cursor = current_time_seconds + active_packet_remaining_seconds
ready = list(ready_high_priority)
future = tuple(sorted(future_high_priority, key=lambda item: (item.available_time_seconds, item.arrival_order)))
future_index = 0
for video_packet in frame.packets:
cursor, future_index, ready = _serve_high_priority(
cursor, ready, future, future_index, channel_bitrate_bps
)
cursor += transmission_duration_seconds(video_packet.wire_size_bytes, channel_bitrate_bps)
while future_index < len(future) and future[future_index].available_time_seconds <= cursor + TIME_EPSILON_SECONDS:
ready.append(future[future_index])
future_index += 1
return cursor
def schedule_video_frames(
frames: Iterable[VideoFrameGroup],
high_priority_packets: Iterable[AgePolicyPacket],
policy: FramePolicy,
channel_bitrate_bps: float,
) -> FrameScheduleResult:
"""Run strict-priority service while keeping video frames contiguous."""
policy = FramePolicy(policy)
if channel_bitrate_bps <= 0.0:
raise ValueError("channel bitrate must be positive")
frame_arrivals = tuple(sorted(frames, key=lambda frame: (frame.generation_time_seconds, frame.composite_frame_id)))
high_arrivals = tuple(sorted(high_priority_packets, key=lambda item: (item.available_time_seconds, item.arrival_order)))
ready_high: list[AgePolicyPacket] = []
pending_frames: list[VideoFrameGroup] = []
transmitted: list[FrameScheduledPacket] = []
dropped: list[FrameDrop] = []
replacements: list[StateReplacement] = []
started: list[int] = []
completed: list[int] = []
predicted_by_frame: dict[int, float] = {}
actual_by_frame: dict[int, float] = {}
active: VideoFrameGroup | None = None
active_index = 0
cursor = 0.0
frame_index = high_index = 0
blocker_by_order: dict[int, tuple[AgePolicyPacket, float]] = {}
def drop_frame(frame: VideoFrameGroup, reason: str) -> None:
dropped.append(FrameDrop(frame, cursor, reason))
def admit(now: float, active_packet: AgePolicyPacket | None = None, active_end: float = 0.0) -> None:
nonlocal frame_index, high_index
while high_index < len(high_arrivals) and high_arrivals[high_index].available_time_seconds <= now + TIME_EPSILON_SECONDS:
item = high_arrivals[high_index]
high_index += 1
if active_packet is not None and item.available_time_seconds > cursor + TIME_EPSILON_SECONDS:
blocker_by_order[item.arrival_order] = (
active_packet,
max(0.0, active_end - item.available_time_seconds),
)
_replace_state(ready_high, item, replacements)
while frame_index < len(frame_arrivals) and frame_arrivals[frame_index].generation_time_seconds <= now + TIME_EPSILON_SECONDS:
frame = frame_arrivals[frame_index]
frame_index += 1
if policy is FramePolicy.LATEST_ONLY:
for old in pending_frames:
drop_frame(old, "replaced_by_newest")
pending_frames[:] = [frame]
elif policy is FramePolicy.TWO_WAITING:
pending_frames.append(frame)
while len(pending_frames) > 2:
drop_frame(pending_frames.pop(0), "waiting_limit")
else:
pending_frames.append(frame)
while (
frame_index < len(frame_arrivals)
or high_index < len(high_arrivals)
or ready_high
or pending_frames
or active is not None
):
if not ready_high and not pending_frames and active is None:
next_times = []
if frame_index < len(frame_arrivals):
next_times.append(frame_arrivals[frame_index].generation_time_seconds)
if high_index < len(high_arrivals):
next_times.append(high_arrivals[high_index].available_time_seconds)
cursor = max(cursor, min(next_times))
admit(cursor)
selected: AgePolicyPacket | None = None
if ready_high:
selected = min(
ready_high,
key=lambda item: (int(item.packet.traffic_class), item.arrival_order),
)
ready_high.remove(selected)
else:
if active is None:
while pending_frames and active is None:
candidate = pending_frames.pop(0)
deadline = policy.deadline_seconds
if deadline is not None:
future_high = high_arrivals[high_index:]
predicted = predict_frame_completion(
cursor,
candidate,
channel_bitrate_bps,
tuple(ready_high),
future_high,
)
if predicted - candidate.generation_time_seconds > deadline + TIME_EPSILON_SECONDS:
drop_frame(candidate, "prediction_reject")
continue
predicted_by_frame[candidate.composite_frame_id] = predicted
active = candidate
active_index = 0
started.append(active.composite_frame_id)
if active is not None:
selected = active.packets[active_index]
if selected is None:
continue
start = max(cursor, selected.available_time_seconds)
wire_packet = encode_link_packet(selected.packet)
end = start + transmission_duration_seconds(len(wire_packet), channel_bitrate_bps)
cursor = start
admit(end, selected, end)
blocker, blocking_delay = blocker_by_order.get(selected.arrival_order, (None, 0.0))
transmitted.append(
FrameScheduledPacket(selected, start, end, blocker, blocking_delay, wire_packet)
)
cursor = end
if selected.packet.traffic_class is TrafficClass.VIDEO:
assert active is not None
active_index += 1
if active_index == len(active.packets):
completed.append(active.composite_frame_id)
actual_by_frame[active.composite_frame_id] = end
active = None
active_index = 0
admissions = tuple(
FrameAdmission(frame_id, predicted, actual_by_frame[frame_id])
for frame_id, predicted in sorted(predicted_by_frame.items())
)
return FrameScheduleResult(
policy=policy,
channel_bitrate_bps=channel_bitrate_bps,
transmitted=tuple(transmitted),
dropped_frames=tuple(dropped),
replacements=tuple(replacements),
started_frame_ids=tuple(started),
completed_frame_ids=tuple(completed),
admissions=admissions,
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,748 @@
"""Lab035: predictive admission and whole-frame video service."""
from __future__ import annotations
import csv
from dataclasses import asdict, dataclass
from pathlib import Path
import subprocess
from typing import Iterable
import cv2
import matplotlib
import numpy as np
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from protocol.link_packet import HEADER_SIZE, TrafficClass, decode_link_packet
from protocol.packet_erasure_fec import decode_fec_block, decode_outer_symbol
from protocol.video_frame_scheduler import (
FramePolicy,
FrameScheduleResult,
VideoFrameGroup,
predict_frame_completion,
schedule_video_frames,
)
from protocol.video_packet import CompositeReassembler, decode_packet as decode_inner_packet
from tests.lab028_video_packetization import COMPOSITE_FPS
from tests.lab034_stale_video_drop import (
PolicyDefinition as Lab034Policy,
build_aligned_workload,
build_lab033_workload,
percentile,
simulate_aligned,
)
OUTPUT_DIRECTORY = Path("data/processed/lab035")
SUMMARY_CSV_PATH = OUTPUT_DIRECTORY / "lab035_summary.csv"
VIDEO_CSV_PATH = OUTPUT_DIRECTORY / "lab035_video_metrics.csv"
CONTROL_CSV_PATH = OUTPUT_DIRECTORY / "lab035_control_metrics.csv"
PREDICTION_CSV_PATH = OUTPUT_DIRECTORY / "lab035_prediction_metrics.csv"
REPORT_PATH = OUTPUT_DIRECTORY / "lab035_report.txt"
UPDATE_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_update_rate.png"
AGE_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_image_age.png"
PUBLICATION_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_publication_delay.png"
OUTCOME_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_frame_outcomes.png"
QUEUE_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_queue_size.png"
PREDICTION_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_prediction_accuracy.png"
CONTROL_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_control_delay.png"
COMPARISON_PLOT_PATH = OUTPUT_DIRECTORY / "lab035_policy_comparison.png"
PLOT_PATHS = (
UPDATE_PLOT_PATH,
AGE_PLOT_PATH,
PUBLICATION_PLOT_PATH,
OUTCOME_PLOT_PATH,
QUEUE_PLOT_PATH,
PREDICTION_PLOT_PATH,
CONTROL_PLOT_PATH,
COMPARISON_PLOT_PATH,
)
LAB034_COMMIT = "b63e36abdb7031d642de8b8138b43cc29e94b759"
CHANNEL_RATES_KBPS = (300.0, 260.0, 230.0)
TIME_EPSILON_SECONDS = 1e-9
@dataclass(frozen=True)
class PolicyDefinition:
name: str
label: str
scheduler_policy: FramePolicy | None
reactive: bool = False
POLICIES = (
PolicyDefinition("no_drop", "Без удаления", FramePolicy.NO_DROP),
PolicyDefinition("reactive_1500ms", "Реактивная 1500 мс", None, True),
PolicyDefinition("latest_only", "Самый свежий", FramePolicy.LATEST_ONLY),
PolicyDefinition("two_waiting", "Два ожидающих", FramePolicy.TWO_WAITING),
PolicyDefinition("predict_1000ms", "Прогноз 1000 мс", FramePolicy.PREDICT_1000MS),
PolicyDefinition("predict_500ms", "Прогноз 500 мс", FramePolicy.PREDICT_500MS),
)
POLICY_BY_NAME = {policy.name: policy for policy in POLICIES}
@dataclass(frozen=True)
class TheoreticalCapacity:
channel_kbps: float
nonvideo_load_kbps: float
remaining_video_kbps: float
video_capacity_ratio: float
minimum_skip_fraction: float
maximum_update_fps: float
@dataclass(frozen=True)
class SummaryMetrics:
channel_kbps: float
policy: str
offered_load_kbps: float
offered_to_capacity_ratio: float
transmitted_packets: int
transmitted_bytes: int
dropped_before_start_packets: int
dropped_before_start_bytes: int
wasted_transmitted_bytes: int
mean_queue_packets: float
max_queue_packets: int
mean_queue_bytes: float
max_queue_bytes: int
mean_waiting_video_frames: float
max_waiting_video_frames: int
queue_at_source_end_packets: int
additional_drain_seconds: float
remaining_video_capacity_kbps: float
theoretical_minimum_skip_fraction: float
theoretical_maximum_update_fps: float
@dataclass(frozen=True)
class VideoMetrics:
channel_kbps: float
policy: str
created_frames: int
started_frames: int
published_frames: int
dropped_before_start_frames: int
partially_transmitted_cancelled_frames: int
published_fraction: float
actual_update_fps: float
mean_publication_delay_ms: float
p95_publication_delay_ms: float
max_publication_delay_ms: float
mean_display_age_ms: float
p95_display_age_ms: float
max_display_age_ms: float
display_age_over_500ms_fraction: float
display_age_over_1000ms_fraction: float
mean_no_update_duration_ms: float
p95_no_update_duration_ms: float
max_no_update_duration_ms: float
mean_missing_run_frames: float
p95_missing_run_frames: float
max_missing_run_frames: int
mean_publication_gap_ms: float
max_publication_gap_ms: float
transmitted_video_bytes: int
dropped_before_start_video_bytes: int
wasted_transmitted_video_bytes: int
delivered_useful_video_kbps: float
@dataclass(frozen=True)
class ControlMetrics:
channel_kbps: float
policy: str
control_p95_delay_ms: float
control_max_delay_ms: float
control_deadline_misses: int
control_max_receive_gap_ms: float
emergency_delay_ms: float
emergency_deadline_met: bool
emergency_blocker_class: str
emergency_blocking_delay_ms: float
telemetry_deadline_misses: int
@dataclass(frozen=True)
class PredictionMetrics:
channel_kbps: float
policy: str
admitted_frames: int
prediction_rejected_frames: int
mean_absolute_error_ms: float
p95_absolute_error_ms: float
max_absolute_error_ms: float
published_after_deadline_frames: int
false_rejections: int
@dataclass(frozen=True)
class ScenarioResult:
summary: SummaryMetrics
video: VideoMetrics
control: ControlMetrics
prediction: PredictionMetrics
publication_times: dict[int, float]
schedule: FrameScheduleResult | None
@dataclass(frozen=True)
class FunctionalTestResult:
name: str
passed: bool
detail: str
def build_frame_groups(aligned) -> tuple[VideoFrameGroup, ...]:
grouped: dict[int, list] = {}
for item in aligned.packets:
if item.composite_frame_id is not None:
grouped.setdefault(item.composite_frame_id, []).append(item)
return tuple(
VideoFrameGroup(
composite_frame_id=frame_id,
generation_time_us=packets[0].packet.generation_time_us,
packets=tuple(sorted(packets, key=lambda item: item.packet.sequence_number)),
)
for frame_id, packets in sorted(grouped.items())
)
def high_priority_packets(aligned) -> tuple:
return tuple(
item for item in aligned.packets
if item.packet.traffic_class is not TrafficClass.VIDEO
)
def theoretical_capacity(aligned, rate: float) -> TheoreticalCapacity:
duration = aligned.lab033.metadata.duration_seconds
nonvideo_bytes = sum(
item.wire_size_bytes for item in aligned.packets
if item.packet.traffic_class is not TrafficClass.VIDEO
)
nonvideo_kbps = nonvideo_bytes * 8.0 / duration / 1000.0
video_kbps = aligned.layout_metrics.after_link_kbps
remaining = max(0.0, rate - nonvideo_kbps)
ratio = remaining / video_kbps
skip = max(0.0, 1.0 - ratio)
return TheoreticalCapacity(
rate,
nonvideo_kbps,
remaining,
ratio,
skip,
COMPOSITE_FPS * min(1.0, ratio),
)
def receive_whole_frames(schedule: FrameScheduleResult) -> dict[int, float]:
dropped = {item.frame.composite_frame_id for item in schedule.dropped_frames}
receiver = CompositeReassembler()
publication_times: dict[int, float] = {}
symbols_by_block: dict[int, list[bytes]] = {}
decoded_blocks: set[int] = set()
for sent in schedule.transmitted:
link = decode_link_packet(sent.wire_packet)
if link.traffic_class is not TrafficClass.VIDEO:
continue
frame_id = sent.item.composite_frame_id
assert frame_id is not None and frame_id not in dropped
outer = decode_outer_symbol(link.payload)
symbols = symbols_by_block.setdefault(outer.block_id, [])
symbols.append(link.payload)
if not outer.is_parity:
completed = receiver.ingest(outer.data)
if completed is not None:
publication_times[completed.composite_frame_id] = sent.end_seconds
if outer.block_id not in decoded_blocks and len(symbols) >= outer.source_count:
decoded = decode_fec_block(tuple(symbols))
for inner in decoded.source_packets:
decode_inner_packet(inner)
decoded_blocks.add(outer.block_id)
if set(publication_times) != set(schedule.completed_frame_ids):
raise AssertionError("published frames differ from completed whole frames")
return publication_times
def display_and_gap_metrics(publications: dict[int, float], source_end: float):
events = sorted((time, frame) for frame, time in publications.items() if time <= source_end + TIME_EPSILON_SECONDS)
samples = np.arange(0.0, source_end + 0.005, 0.01)
ages = []
index = 0
last_frame = None
for time in samples:
while index < len(events) and events[index][0] <= time:
last_frame = events[index][1]; index += 1
generation = 0.0 if last_frame is None else last_frame / COMPOSITE_FPS
ages.append(max(0.0, time - generation))
update_times = [0.0] + [time for time, _ in events] + [source_end]
no_update = tuple(max(0.0, right - left) for left, right in zip(update_times, update_times[1:]))
publication_gaps = tuple(right[0] - left[0] for left, right in zip(events, events[1:]))
return ages, no_update, publication_gaps
def missing_runs(frame_count: int, published: set[int]) -> tuple[int, ...]:
runs = []
current = 0
for frame_id in range(frame_count):
if frame_id not in published:
current += 1
elif current:
runs.append(current); current = 0
if current:
runs.append(current)
return tuple(runs)
def queue_metrics(schedule: FrameScheduleResult, frames, source_end: float):
intervals = []
first_start: dict[int, float] = {}
for sent in schedule.transmitted:
intervals.append((sent.item.available_time_seconds, sent.end_seconds, sent.item.wire_size_bytes))
if sent.item.composite_frame_id is not None:
first_start.setdefault(sent.item.composite_frame_id, sent.start_seconds)
drop_time = {item.frame.composite_frame_id: item.drop_time_seconds for item in schedule.dropped_frames}
for item in schedule.dropped_frames:
for packet in item.frame.packets:
intervals.append((packet.available_time_seconds, item.drop_time_seconds, packet.wire_size_bytes))
for item in schedule.replacements:
intervals.append((item.removed.available_time_seconds, item.time_seconds, item.removed.wire_size_bytes))
packet_area = byte_area = 0.0
events: dict[float, list[int]] = {}
remaining = 0
for start, end, size in intervals:
if start <= source_end + TIME_EPSILON_SECONDS < end - TIME_EPSILON_SECONDS:
remaining += 1
left, right = max(0.0, start), min(source_end, end)
if right <= left + TIME_EPSILON_SECONDS:
continue
packet_area += right - left; byte_area += (right - left) * size
events.setdefault(left, [0, 0])[0] += 1; events[left][1] += size
events.setdefault(right, [0, 0])[0] -= 1; events[right][1] -= size
count = size_now = max_count = max_size = 0
for time in sorted(events):
count += events[time][0]; size_now += events[time][1]
max_count = max(max_count, count); max_size = max(max_size, size_now)
frame_intervals = []
for frame in frames:
end = first_start.get(frame.composite_frame_id, drop_time.get(frame.composite_frame_id, source_end))
frame_intervals.append((frame.generation_time_seconds, end))
frame_area = 0.0; frame_events: dict[float, int] = {}
for start, end in frame_intervals:
left, right = max(0.0, start), min(source_end, end)
if right <= left + TIME_EPSILON_SECONDS: continue
frame_area += right - left
frame_events[left] = frame_events.get(left, 0) + 1
frame_events[right] = frame_events.get(right, 0) - 1
waiting = max_waiting = 0
for time in sorted(frame_events):
waiting += frame_events[time]; max_waiting = max(max_waiting, waiting)
finish = max([source_end] + [item.end_seconds for item in schedule.transmitted] + [item.drop_time_seconds for item in schedule.dropped_frames])
return (
packet_area / source_end, max_count, byte_area / source_end, max_size,
frame_area / source_end, max_waiting, remaining, max(0.0, finish - source_end),
)
def control_metrics(schedule: FrameScheduleResult, rate: float, policy: str) -> ControlMetrics:
by_class = {
traffic: [item for item in schedule.transmitted if item.item.packet.traffic_class is traffic]
for traffic in (TrafficClass.CONTROL, TrafficClass.EMERGENCY, TrafficClass.TELEMETRY)
}
control = by_class[TrafficClass.CONTROL]
control_delays = [item.end_seconds - item.item.packet.generation_time_us / 1_000_000.0 for item in control]
gaps = [right.end_seconds - left.end_seconds for left, right in zip(control, control[1:])]
telemetry_delays = [item.end_seconds - item.item.packet.generation_time_us / 1_000_000.0 for item in by_class[TrafficClass.TELEMETRY]]
emergency = by_class[TrafficClass.EMERGENCY]
if len(emergency) != 1: raise AssertionError("exactly one emergency command is required")
urgent = emergency[0]
urgent_delay = urgent.end_seconds - urgent.item.packet.generation_time_us / 1_000_000.0
return ControlMetrics(
rate, policy,
percentile(control_delays, 95) * 1000.0,
max(control_delays) * 1000.0,
sum(delay > 0.1 + TIME_EPSILON_SECONDS for delay in control_delays),
max(gaps, default=0.0) * 1000.0,
urgent_delay * 1000.0,
urgent_delay <= 0.05 + TIME_EPSILON_SECONDS,
urgent.blocked_by.packet.traffic_class.name.lower() if urgent.blocked_by else "none",
urgent.blocking_delay_seconds * 1000.0,
sum(delay > 0.5 + TIME_EPSILON_SECONDS for delay in telemetry_delays),
)
def video_metrics(aligned, schedule, publications, rate, policy):
source_end = aligned.lab033.metadata.duration_seconds
published = set(publications)
dropped = {item.frame.composite_frame_id for item in schedule.dropped_frames}
delays = [publications[frame] - frame / COMPOSITE_FPS for frame in sorted(published)]
ages, no_update, publication_gaps = display_and_gap_metrics(publications, source_end)
runs = missing_runs(len(aligned.lab033.composites), published)
transmitted_video_bytes = sum(
len(item.wire_packet) for item in schedule.transmitted
if item.item.packet.traffic_class is TrafficClass.VIDEO
)
dropped_bytes = sum(item.frame.wire_size_bytes for item in schedule.dropped_frames)
useful = sum(
len(aligned.lab033.composites[frame].base_jpeg) + len(aligned.lab033.composites[frame].roi_jpeg)
for frame in published
)
return VideoMetrics(
rate, policy, len(aligned.lab033.composites), len(schedule.started_frame_ids),
len(published), len(dropped), 0, len(published) / len(aligned.lab033.composites),
sum(time <= source_end + TIME_EPSILON_SECONDS for time in publications.values()) / source_end,
float(np.mean(delays)) * 1000.0 if delays else 0.0,
percentile(delays, 95) * 1000.0, max(delays, default=0.0) * 1000.0,
float(np.mean(ages)) * 1000.0, percentile(ages, 95) * 1000.0,
max(ages, default=0.0) * 1000.0,
sum(age > 0.5 for age in ages) / len(ages),
sum(age > 1.0 for age in ages) / len(ages),
float(np.mean(no_update)) * 1000.0, percentile(no_update, 95) * 1000.0,
max(no_update, default=0.0) * 1000.0,
float(np.mean(runs)) if runs else 0.0, percentile(runs, 95), max(runs, default=0),
float(np.mean(publication_gaps)) * 1000.0 if publication_gaps else 0.0,
max(publication_gaps, default=0.0) * 1000.0,
transmitted_video_bytes, dropped_bytes, 0,
useful * 8.0 / source_end / 1000.0,
)
def prediction_metrics(schedule, rate, policy):
errors = [abs(item.prediction_error_seconds) for item in schedule.admissions]
deadline = schedule.policy.deadline_seconds
rejected = [item for item in schedule.dropped_frames if item.reason == "prediction_reject"]
late = sum(
item.actual_completion_seconds - item.composite_frame_id / COMPOSITE_FPS
> deadline + TIME_EPSILON_SECONDS
for item in schedule.admissions
) if deadline is not None else 0
return PredictionMetrics(
rate, policy, len(schedule.admissions), len(rejected),
float(np.mean(errors)) * 1000.0 if errors else 0.0,
percentile(errors, 95) * 1000.0, max(errors, default=0.0) * 1000.0,
late, 0,
)
def whole_frame_result(aligned, frames, high, rate, policy_def):
schedule = schedule_video_frames(frames, high, policy_def.scheduler_policy, rate * 1000.0)
publications = receive_whole_frames(schedule)
video = video_metrics(aligned, schedule, publications, rate, policy_def.name)
control = control_metrics(schedule, rate, policy_def.name)
prediction = prediction_metrics(schedule, rate, policy_def.name)
source_end = aligned.lab033.metadata.duration_seconds
qp, qmax, qb, qbmax, fq, fqmax, remaining, drain = queue_metrics(schedule, frames, source_end)
capacity = theoretical_capacity(aligned, rate)
offered_bytes = sum(item.wire_size_bytes for item in aligned.packets)
summary = SummaryMetrics(
rate, policy_def.name,
offered_bytes * 8.0 / source_end / 1000.0,
offered_bytes * 8.0 / source_end / (rate * 1000.0),
len(schedule.transmitted), sum(len(item.wire_packet) for item in schedule.transmitted),
sum(len(item.frame.packets) for item in schedule.dropped_frames),
sum(item.frame.wire_size_bytes for item in schedule.dropped_frames), 0,
qp, qmax, qb, qbmax, fq, fqmax, remaining, drain,
capacity.remaining_video_kbps, capacity.minimum_skip_fraction,
capacity.maximum_update_fps,
)
return ScenarioResult(summary, video, control, prediction, publications, schedule)
def reactive_result(aligned, rate):
old_policy = Lab034Policy("aligned_1500ms", "По кадрам, 1500 мс", "aligned", 1500)
old = simulate_aligned(aligned, rate, old_policy)
capacity = theoretical_capacity(aligned, rate)
s, v, c = old.summary, old.video, old.control
source_end = aligned.lab033.metadata.duration_seconds
dropped_frames = set(old.dropped_frames)
partial_frames = {
frame_id for frame_id in dropped_frames
if old.transmitted_video_by_frame.get(frame_id, 0) > 0
}
before_start_frames = dropped_frames - partial_frames
dropped_before_packets = [
item for item in old.schedule.dropped_video
if item.item.composite_frame_id in before_start_frames
]
first_start = {}
for item in old.schedule.transmitted:
if item.item.composite_frame_id is not None:
first_start.setdefault(item.item.composite_frame_id, item.start_seconds)
frame_drop_time = {}
for item in old.schedule.dropped_video:
assert item.item.composite_frame_id is not None
frame_drop_time.setdefault(item.item.composite_frame_id, item.drop_time_seconds)
frame_events = {}
frame_area = 0.0
for frame_id in range(len(aligned.lab033.composites)):
start = frame_id / COMPOSITE_FPS
end = first_start.get(frame_id, frame_drop_time.get(frame_id, start))
left, right = max(0.0, start), min(source_end, end)
if right <= left + TIME_EPSILON_SECONDS:
continue
frame_area += right - left
frame_events[left] = frame_events.get(left, 0) + 1
frame_events[right] = frame_events.get(right, 0) - 1
waiting = max_waiting = 0
for time in sorted(frame_events):
waiting += frame_events[time]
max_waiting = max(max_waiting, waiting)
publication_events = sorted(old.publication_times.values())
publication_gaps = [
right - left for left, right in zip(publication_events, publication_events[1:])
]
summary = SummaryMetrics(
rate, "reactive_1500ms", s.offered_load_kbps, s.offered_to_capacity_ratio,
s.transmitted_packets, s.transmitted_bytes,
len(dropped_before_packets),
sum(item.item.wire_size_bytes for item in dropped_before_packets),
s.wasted_transmitted_bytes, s.mean_queue_packets, s.max_queue_packets,
s.mean_queue_bytes, s.max_queue_bytes,
frame_area / source_end, max_waiting,
s.queue_at_source_end_packets, s.additional_drain_seconds,
capacity.remaining_video_kbps, capacity.minimum_skip_fraction,
capacity.maximum_update_fps,
)
video = VideoMetrics(
rate, "reactive_1500ms", v.created_frames,
v.published_frames + v.partially_transmitted_cancelled_frames,
v.published_frames,
v.intentionally_dropped_frames - v.partially_transmitted_cancelled_frames,
v.partially_transmitted_cancelled_frames,
v.published_fraction, v.actual_update_fps,
v.mean_publication_delay_ms, v.p95_publication_delay_ms, v.max_publication_delay_ms,
v.mean_display_age_ms, v.p95_display_age_ms, v.max_display_age_ms,
v.display_age_over_500ms_fraction, v.display_age_over_1000ms_fraction,
v.mean_no_update_duration_ms, v.p95_no_update_duration_ms, v.max_no_update_duration_ms,
v.mean_missing_run_frames, v.p95_missing_run_frames, v.max_missing_run_frames,
float(np.mean(publication_gaps)) * 1000.0 if publication_gaps else 0.0,
max(publication_gaps, default=0.0) * 1000.0,
sum(len(item.wire_packet) for item in old.schedule.transmitted if item.item.packet.traffic_class is TrafficClass.VIDEO),
0, v.wasted_transmitted_video_bytes, v.delivered_useful_video_kbps,
)
control = ControlMetrics(
rate, "reactive_1500ms", c.control_p95_age_ms, c.control_max_age_ms,
c.control_deadline_misses, c.control_max_receive_gap_ms,
c.emergency_total_delay_ms, c.emergency_deadline_met,
c.emergency_blocker_class, c.emergency_blocking_delay_ms,
c.telemetry_deadline_misses,
)
prediction = PredictionMetrics(rate, "reactive_1500ms", 0, 0, 0.0, 0.0, 0.0, 0, 0)
return ScenarioResult(summary, video, control, prediction, old.publication_times, None)
def run_experiment(aligned, frames, high):
results = []
for rate in CHANNEL_RATES_KBPS:
for policy in POLICIES:
results.append(
reactive_result(aligned, rate)
if policy.reactive
else whole_frame_result(aligned, frames, high, rate, policy)
)
return tuple(results)
def run_functional_tests(aligned, frames, high, results):
lookup = {(r.summary.channel_kbps, r.summary.policy): r for r in results}
checks = []
def check(name):
def decorator(function): checks.append((name, function)); return function
return decorator
whole = [result for result in results if result.schedule is not None]
@check("01_video_frames_do_not_interleave")
def _():
for result in whole:
sequence = [item.item.composite_frame_id for item in result.schedule.transmitted if item.item.composite_frame_id is not None]
compressed = [frame for index, frame in enumerate(sequence) if index == 0 or frame != sequence[index - 1]]
assert len(compressed) == len(set(compressed))
@check("02_started_frame_never_dropped")
def _():
for result in whole:
assert not (set(result.schedule.started_frame_ids) & {item.frame.composite_frame_id for item in result.schedule.dropped_frames})
@check("03_high_priority_between_frame_packets")
def _():
assert any(
any(item.item.packet.traffic_class is not TrafficClass.VIDEO for item in result.schedule.transmitted[left + 1:right])
for result in whole
for left, right in zip(
[i for i, item in enumerate(result.schedule.transmitted) if item.item.composite_frame_id is not None][:-1],
[i for i, item in enumerate(result.schedule.transmitted) if item.item.composite_frame_id is not None][1:],
)
if result.schedule.transmitted[left].item.composite_frame_id == result.schedule.transmitted[right].item.composite_frame_id
)
@check("04_latest_drops_only_unstarted")
def _():
for rate in CHANNEL_RATES_KBPS:
result = lookup[(rate, "latest_only")]
assert not (set(result.schedule.started_frame_ids) & {item.frame.composite_frame_id for item in result.schedule.dropped_frames})
@check("05_two_waiting_limit")
def _(): assert all(lookup[(rate, "two_waiting")].summary.max_waiting_video_frames <= 2 for rate in CHANNEL_RATES_KBPS)
@check("06_prediction_is_pure")
def _():
ready = list(high[:2]); future = list(high[2:20]); ready_before=list(ready); future_before=list(future)
predict_frame_completion(0.0, frames[0], 230_000.0, ready, future)
assert ready == ready_before and future == future_before
@check("07_prediction_uses_actual_sizes")
def _():
small = VideoFrameGroup(999, 0, (frames[0].packets[0],))
full = predict_frame_completion(0.0, frames[0], 300_000.0, (), ())
one = predict_frame_completion(0.0, small, 300_000.0, (), ())
assert full > one and abs(one - small.wire_size_bytes * 8.0 / 300_000.0) < 1e-12
@check("08_prestart_drop_has_no_waste")
def _(): assert all(result.video.wasted_transmitted_video_bytes == 0 for result in whole)
@check("09_partial_only_reactive")
def _():
assert all(result.video.partially_transmitted_cancelled_frames == 0 for result in whole)
assert lookup[(230.0, "reactive_1500ms")].video.partially_transmitted_cancelled_frames > 0
@check("10_incomplete_not_published")
def _():
for result in whole:
dropped = {item.frame.composite_frame_id for item in result.schedule.dropped_frames}
assert not (dropped & set(result.publication_times))
@check("11_crc_layers_pass")
def _(): assert all(result.video.published_frames == len(result.publication_times) for result in results)
@check("12_emergency_never_deleted")
def _(): assert all(result.control.emergency_deadline_met for result in results)
@check("13_priority_above_video")
def _(): assert all(result.control.control_deadline_misses == 0 and result.control.telemetry_deadline_misses == 0 for result in results)
@check("14_300kbps_no_unnecessary_loss")
def _(): assert all(lookup[(300.0, policy.name)].video.published_frames == 63 for policy in POLICIES)
@check("15_230kbps_bounded_queue")
def _():
baseline = lookup[(230.0, "no_drop")].summary.max_queue_packets
assert all(lookup[(230.0, name)].summary.max_queue_packets < baseline for name in ("latest_only", "two_waiting", "predict_1000ms", "predict_500ms"))
@check("16_frame_accounting")
def _():
for result in results:
assert result.video.published_frames + result.video.dropped_before_start_frames + result.video.partially_transmitted_cancelled_frames == 63
@check("17_byte_accounting")
def _():
for result in whole:
assert result.summary.transmitted_bytes == sum(len(item.wire_packet) for item in result.schedule.transmitted)
assert result.summary.dropped_before_start_bytes == sum(item.frame.wire_size_bytes for item in result.schedule.dropped_frames)
@check("18_reproducible")
def _():
original = lookup[(230.0, "predict_1000ms")].schedule
repeated = schedule_video_frames(frames, high, FramePolicy.PREDICT_1000MS, 230_000.0)
assert [(x.item.arrival_order,x.start_seconds,x.end_seconds) for x in original.transmitted] == [(x.item.arrival_order,x.start_seconds,x.end_seconds) for x in repeated.transmitted]
@check("19_predict_1000_never_known_late")
def _(): assert all(lookup[(rate, "predict_1000ms")].prediction.published_after_deadline_frames == 0 for rate in CHANNEL_RATES_KBPS)
@check("20_command_delay_bound")
def _():
lab033 = {}
with Path("data/processed/lab033/lab033_summary.csv").open(encoding="utf-8") as file:
for row in csv.DictReader(file):
if row["scheduler"] == "latest_state": lab033[float(row["channel_kbps"])] = float(row["control_max_age_ms"])
max_video_bytes = max(packet.wire_size_bytes for frame in frames for packet in frame.packets)
for result in results:
bound = lab033[result.summary.channel_kbps] + max_video_bytes * 8.0 / (result.summary.channel_kbps * 1000.0) * 1000.0
assert result.control.control_max_delay_ms <= bound + 1e-9
output=[]
for name,function in checks:
try: function(); output.append(FunctionalTestResult(name,True,"PASS"))
except Exception as error: output.append(FunctionalTestResult(name,False,f"{type(error).__name__}: {error}"))
if not all(item.passed for item in output): raise AssertionError("functional checks failed: "+", ".join(item.name for item in output if not item.passed))
return tuple(output)
def save_csv(results):
OUTPUT_DIRECTORY.mkdir(parents=True, exist_ok=True)
for path, cls, rows in (
(SUMMARY_CSV_PATH, SummaryMetrics, (r.summary for r in results)),
(VIDEO_CSV_PATH, VideoMetrics, (r.video for r in results)),
(CONTROL_CSV_PATH, ControlMetrics, (r.control for r in results)),
(PREDICTION_CSV_PATH, PredictionMetrics, (r.prediction for r in results)),
):
with path.open("w",encoding="utf-8",newline="") as file:
writer=csv.DictWriter(file,fieldnames=list(cls.__dataclass_fields__)); writer.writeheader(); writer.writerows(asdict(row) for row in rows)
def grouped_plot(results,value,ylabel,title,path):
x=np.arange(len(CHANNEL_RATES_KBPS)); width=.13
fig,axis=plt.subplots(figsize=(12,5.5))
for index,policy in enumerate(POLICIES):
rows=[r for r in results if r.summary.policy==policy.name]
axis.bar(x+(index-2.5)*width,[value(r) for r in rows],width,label=policy.label)
axis.set_xticks(x,[f"{rate:.0f}" for rate in CHANNEL_RATES_KBPS]); axis.set_xlabel("Скорость, кбит/с"); axis.set_ylabel(ylabel); axis.set_title(title); axis.grid(axis="y",alpha=.3); axis.legend(fontsize=8); fig.tight_layout(); fig.savefig(path,dpi=150); plt.close(fig)
def save_plots(results):
grouped_plot(results,lambda r:r.video.actual_update_fps,"Обновлений/с","Фактическая частота обновления",UPDATE_PLOT_PATH)
grouped_plot(results,lambda r:r.video.p95_display_age_ms,"P95 возраста, мс","Возраст отображаемого изображения",AGE_PLOT_PATH)
grouped_plot(results,lambda r:r.video.p95_publication_delay_ms,"P95 задержки, мс","Задержка публикации",PUBLICATION_PLOT_PATH)
grouped_plot(results,lambda r:r.video.published_frames,"Кадров","Опубликованные кадры",OUTCOME_PLOT_PATH)
grouped_plot(results,lambda r:r.summary.max_queue_packets,"Пакетов","Максимальный размер очереди",QUEUE_PLOT_PATH)
grouped_plot(results,lambda r:r.prediction.p95_absolute_error_ms,"P95 ошибки, мс","Точность прогноза",PREDICTION_PLOT_PATH)
grouped_plot(results,lambda r:r.control.control_p95_delay_ms,"P95, мс","Задержка команд",CONTROL_PLOT_PATH)
grouped_plot(results,lambda r:r.video.dropped_before_start_frames,"Кадров","Сравнение политик упреждающего удаления",COMPARISON_PLOT_PATH)
def write_report(aligned,results,tests):
git_status=subprocess.run(("git","status","--short","--branch"),check=True,capture_output=True,text=True,encoding="utf-8").stdout.rstrip()
capacities={rate:theoretical_capacity(aligned,rate) for rate in CHANNEL_RATES_KBPS}
lines=[
"Lab035. Упреждающий допуск видеокадров и обслуживание видео целыми кадрами","",
"1. Исходное состояние",f"- Commit Lab034: {LAB034_COMMIT}.","- Перед Lab035 рабочее дерево было чистым; main опережала origin/main на два commit.","",
"2. Правило обслуживания","- После первого видеопакета кадр становится активным и не удаляется.","- При повторном выборе видео передаётся следующий пакет активного кадра; команды и телеметрия могут передаваться между пакетами.","- Новый видеокадр начинается только после полного завершения активного; видеопакеты разных кадров не чередуются; отдельный пакет не прерывается.","- Только неактивные кадры могут быть удалены до передачи первого пакета.","",
"3. Теоретическая пропускная способность","speed | nonvideo kbps | remaining video kbps | remaining/aligned | minimum skip | maximum fps",
]
for rate in CHANNEL_RATES_KBPS:
c=capacities[rate]; lines.append(f"{rate:.0f} | {c.nonvideo_load_kbps:.3f} | {c.remaining_video_kbps:.3f} | {c.video_capacity_ratio:.6f} | {c.minimum_skip_fraction:.6f} | {c.maximum_update_fps:.3f}")
lines.extend(["","4. Восемнадцать сочетаний","speed | policy | published/drop/partial | fps | age P95 ms | no-update max ms | queue max/waiting frames | waste bytes | prediction MAE/P95/max ms | control P95/max ms | emergency ms"])
for r in results:
s,v,c,p=r.summary,r.video,r.control,r.prediction
lines.append(f"{s.channel_kbps:.0f} | {POLICY_BY_NAME[s.policy].label} | {v.published_frames}/{v.dropped_before_start_frames}/{v.partially_transmitted_cancelled_frames} | {v.actual_update_fps:.3f} | {v.p95_display_age_ms:.3f} | {v.max_no_update_duration_ms:.3f} | {s.max_queue_packets}/{s.max_waiting_video_frames} | {v.wasted_transmitted_video_bytes} | {p.mean_absolute_error_ms:.6f}/{p.p95_absolute_error_ms:.6f}/{p.max_absolute_error_ms:.6f} | {c.control_p95_delay_ms:.3f}/{c.control_max_delay_ms:.3f} | {c.emergency_delay_ms:.3f}")
lines.extend(["","5. Интерпретация","- Реактивная Lab034 начинает кадр без гарантии завершения, затем удаляет остаток: уже переданные байты становятся бесполезными, а обновление не публикуется.","- Удаление до первого пакета исключает бесполезную передачу; обслуживание целыми кадрами гарантирует, что начатый кадр будет опубликован.","- Политика самого свежего уменьшает задержку ожидающих данных, но удаляет больше промежуточных кадров; очередь из двух кадров сохраняет больше последовательных обновлений ценой возраста.","- Прогноз полного завершения учитывает весь размер кадра и будущую периодическую высокоприоритетную нагрузку, поэтому полезнее проверки только текущего возраста.","- В модели точно известны команды 20 Гц, телеметрия 10 Гц и аварийная команда 10,0 с; неизвестные будущие дискретные события не моделируются и в реальной системе потребовали бы запаса.","- При устойчивой перегрузке невозможно одновременно сохранить все кадры, исходное JPEG-качество и малую задержку; требуется уменьшить частоту, качество или заранее пропускать кадры.","- Частота обновления, возраст изображения и длительность отсутствия нового изображения оцениваются одновременно: оптимизация одного показателя может ухудшить остальные.","","6. Допущения","- Ошибки и помехи отсутствуют; один общий абстрактный ресурс, форматы Lab028-Lab034 неизменны, активный пакет не прерывается.","- Прогноз не изменяет настоящую очередь; для допущенных кадров сохраняются только агрегированные ошибки, без подробного журнала.","- Политика автоматически не выбирается.","","7. Функциональные проверки"])
lines.extend(f"- {'PASS' if item.passed else 'FAIL'} {item.name}: {item.detail}" for item in tests)
lines.extend(["","8. Созданные файлы"])
lines.extend(f"- {path.as_posix()}" for path in (Path("protocol/video_frame_scheduler.py"),Path("tests/lab035_video_frame_admission.py"),SUMMARY_CSV_PATH,VIDEO_CSV_PATH,CONTROL_CSV_PATH,PREDICTION_CSV_PATH,REPORT_PATH,*PLOT_PATHS))
lines.extend(["","9. Итоговый Git status","- Lab035 не добавлена в индекс и не закоммичена.","",git_status])
REPORT_PATH.write_text("\n".join(lines)+"\n",encoding="utf-8")
def validate_outputs():
for path in (SUMMARY_CSV_PATH,VIDEO_CSV_PATH,CONTROL_CSV_PATH,PREDICTION_CSV_PATH):
with path.open(encoding="utf-8",newline="") as file: rows=list(csv.DictReader(file))
if len(rows)!=18: raise AssertionError(f"{path} must contain 18 rows")
if "Lab035" not in REPORT_PATH.read_text(encoding="utf-8"): raise AssertionError("invalid report")
for path in PLOT_PATHS:
image=cv2.imread(str(path),cv2.IMREAD_UNCHANGED)
if image is None or image.size==0: raise AssertionError(f"OpenCV could not read {path}")
def main():
lab033=build_lab033_workload(); aligned=build_aligned_workload(lab033)
frames=build_frame_groups(aligned); high=high_priority_packets(aligned)
results=run_experiment(aligned,frames,high)
tests=run_functional_tests(aligned,frames,high,results)
save_csv(results); save_plots(results); write_report(aligned,results,tests); validate_outputs()
print(f"Lab035 complete: {len(results)} scenarios, {len(tests)} checks")
if __name__=="__main__": main()