Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/); versions fol
which is usually what you meant to test. Set 0, the default, to spread it evenly as before.
Profiles remember it, and the log says how often to expect a run.

- **The wireless presets now lose in runs.** Weak WiFi, Cafe, Train/metro, In-flight Wi-Fi, 3G,
Roaming and Satellite pick a run length to match the way a radio link really fails, so picking
one of them now stalls a transfer the way the real thing does instead of sprinkling single
losses. The wired ones - DSL and 56k modem - keep their loss evenly spread, because there it
is a full queue rather than a radio going away. Loss rates are unchanged, and the Wi-Fi
figures come from a published measurement.

- **A "Loss runs" counter** on the Statistics tab, in the stats CSV and in the reproduction
report, says how many runs of lost packets a session actually produced. Zero there, with a
run length set, means the session was too short to see one rather than the setting doing
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,14 @@ Terrible network - plus your own (saved under a name). The program **always star
network"** (nothing is impaired until you set something). Built-in presets cannot be deleted - the
"Delete" button is disabled for them.

The wireless ones also lose **in runs** rather than one packet at a time, because that is what a
radio link does: fading, interference and handovers take the channel away for a stretch. The
wired ones (DSL, 56k modem) keep their loss evenly spread, because there the loss is a full queue
rather than a radio going away.

Their numbers come from published measurements wherever measurements exist (Ookla® Speedtest®
medians for satellite and mobile, peer-reviewed studies for Starlink's 15-second reconfiguration
and for in-flight Wi-Fi). Every figure is attributed in the comment block at the top of
medians for satellite and mobile, peer-reviewed studies for Starlink's 15-second reconfiguration,
for in-flight Wi-Fi and for how many Wi-Fi packets are lost in a row). Every figure is attributed in the comment block at the top of
`beantester/presets.py`, with the authors, the venue and a DOI, so you can check it rather than
trust it. Where no such figure exists - "weak Wi-Fi" is not a measurable quantity - the comment
next to the value says so instead of inventing a source. A few of them are worth a sentence:
Expand Down Expand Up @@ -1039,6 +1044,7 @@ All of them loop except `upload-drop-midway.json`, so you can start one and leav
| `congested-vpn.json` | A VPN whose **upload** collapses while download stays fine (512 to 160 KB/s), with latency spikes, an MTU of 1400 and occasional resets. |
| `failing-dns.json` | Aimed at **UDP port 53 only**: name resolution degrades to 60% loss and 1.5 s of ping, goes **100% dead for 13 s**, then comes back. Everything else on the machine keeps working, which is what makes it a DNS test rather than an outage test. |
| `overloaded-game-server.json` | A server sagging under load: ping, jitter, loss and duplication all climb together, with latency spikes up to 800 ms at 45% of packets. |
| `same-loss-in-runs.json` | The same 5% loss for the whole run, arriving four different ways: evenly spread, then in runs of 5, 15 and 40 packets. Nothing else changes, so whatever breaks is the SHAPE of the loss and not how much of it there is. The one for finding out whether your reconnect path works. |
| `upload-drop-midway.json` | **Does not loop** - a one-shot: an upload that starts healthy, degrades, is **cut to zero mid-transfer** with a TCP reset, then partially recovers. For testing resumable uploads and progress bars that lie. |
| `blocked-endpoint.json` | One backend (`203.0.113.0/24`) is **blocked** at 20 s while everything else keeps working, then unblocked. For testing timeouts, retries and fallbacks against a single dependency. |

Expand Down
5 changes: 4 additions & 1 deletion README.pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,9 @@ w środku normalnego ruchu.

**Profile** - gotowe presety **posortowane od najlepszego (góra) do najgorszego (dół)**: Idealna sieć, Dobre WiFi, Sieć 5G, DSL domowy (VDSL), Sieć LTE/4G, Satelita niskoorbitalny, Odległy serwer (inny kontynent), Słabe WiFi, Kawiarnia (zatłoczone WiFi), Zapchane łącze domowe (bufferbloat), Pociąg / metro (tunele), Sieć 3G, Roaming zagraniczny, Satelita geostacjonarny, Wi-Fi w samolocie, Modem 56k, Fatalna sieć - oraz Twoje własne (zapis pod nazwą). Program **startuje zawsze na „Idealnej sieci”** (nic nie jest psute, dopóki sam czegoś nie ustawisz). Presetów wbudowanych nie da się usunąć - przycisk „Usuń” jest wtedy nieaktywny.

Ich liczby pochodzą z opublikowanych pomiarów wszędzie tam, gdzie pomiary istnieją (mediany Ookla® Speedtest® dla satelity i sieci komórkowych, recenzowane prace naukowe o 15-sekundowym przełączaniu Starlinka i o Wi-Fi w samolocie). Każda liczba jest przypisana do źródła w komentarzu na górze `beantester/presets.py` - z autorami, miejscem publikacji i numerem DOI - żebyś mógł ją sprawdzić, a nie tylko nam uwierzyć. Tam, gdzie takiej liczby nie ma - „słabe Wi-Fi" nie jest wielkością mierzalną - komentarz przy wartości mówi to wprost, zamiast wymyślać źródło.
Te bezprzewodowe gubią też pakiety **seriami**, a nie po jednym, bo tak zachowuje się łącze radiowe: zaniki, zakłócenia i przełączenia zabierają kanał na moment. Te przewodowe (DSL, modem 56k) mają stratę rozłożoną równomiernie, bo tam bierze się ona z zapchanej kolejki, a nie ze znikającego radia.

Ich liczby pochodzą z opublikowanych pomiarów wszędzie tam, gdzie pomiary istnieją (mediany Ookla® Speedtest® dla satelity i sieci komórkowych, recenzowane prace naukowe o 15-sekundowym przełączaniu Starlinka, o Wi-Fi w samolocie i o tym, ile pakietów Wi-Fi ginie pod rząd). Każda liczba jest przypisana do źródła w komentarzu na górze `beantester/presets.py` - z autorami, miejscem publikacji i numerem DOI - żebyś mógł ją sprawdzić, a nie tylko nam uwierzyć. Tam, gdzie takiej liczby nie ma - „słabe Wi-Fi" nie jest wielkością mierzalną - komentarz przy wartości mówi to wprost, zamiast wymyślać źródło.

- **Satelita niskoorbitalny** - wzorowany na Starlinku. W stanie ustalonym jest dobry (około 40 ms pingu, 100 Mb/s). Wyróżnia go przełączanie satelity co 15 sekund, które na chwilę wstrzymuje transmisję i objawia się okazjonalnym skokiem pingu, a nie stratą pakietów.
- **Odległy serwer (inny kontynent)** - szybkie łącze, w którym nic nie jest zepsute, tylko jest daleko (~120 ms pingu). To ten preset obnaża gadatliwe protokoły i kod pisany przy założeniu, że serwer stoi obok.
Expand Down Expand Up @@ -888,6 +890,7 @@ Wszystkie chodzą w pętli poza `upload-drop-midway.json`, więc można je włą
| `congested-vpn.json` | VPN, w którym pada **upload**, a pobieranie trzyma się dobrze (512 → 160 KB/s), ze skokami latencji, MTU 1400 i sporadycznymi resetami. |
| `failing-dns.json` | Wycelowany **wyłącznie w UDP port 53**: rozwiązywanie nazw degraduje się do 60% strat i 1,5 s pingu, na 13 s **pada w 100%**, po czym wraca. Reszta ruchu działa normalnie - i to właśnie czyni z tego test DNS-u, a nie test awarii. |
| `overloaded-game-server.json` | Serwer uginający się pod obciążeniem: ping, jitter, straty i duplikacja rosną razem, ze skokami latencji do 800 ms na 45% pakietów. |
| `same-loss-in-runs.json` | Te same 5% strat przez cały przebieg, przychodzące na cztery sposoby: równomiernie, a potem seriami po 5, 15 i 40 pakietów. Nic innego się nie zmienia, więc to, co pęknie, jest winą KSZTAŁTU straty, a nie jej wielkości. Ten do sprawdzenia, czy ścieżka ponownego łączenia działa. |
| `upload-drop-midway.json` | **Bez pętli** - jednorazowy: upload zaczyna zdrowo, degraduje się, zostaje **ucięty do zera w połowie transferu** z resetem TCP, potem częściowo wraca. Do testowania wznawialnych wysyłek i pasków postępu, które kłamią. |
| `blocked-endpoint.json` | Jeden backend (`203.0.113.0/24`) zostaje **zablokowany** w 20 s, reszta działa dalej, po czym blokada znika. Do testowania timeoutów, ponowień i fallbacków wobec pojedynczej zależności. |

Expand Down
15 changes: 15 additions & 0 deletions beantester/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ def burst_loss_params(loss, mean_burst):
the delivered loss and the delivered mean run length both land on the request
inside the sampling noise, from 0.5% upward.

On the licensing of all that, checked rather than assumed (2026-09-01), because
two of the three names above are the kind an audit stops on:

* the MMB 2008 paper is NOT open access. What is used from it is an IDEA - that
the average run length is the intuitive way in - together with an observation
about its printed formula. Ideas and facts carry no copyright, the formula in
this function was worked out here, and no wording of theirs is reproduced.
* ``tc netem`` is named, not used. It ships under GPL-2.0 as part of iproute2,
which convention 35 forbids as a DEPENDENCY - and nothing here depends on it.
No line of it was read into this file. It is named because a reader deserves
to know the model has a widely deployed implementation to compare against.
* Gilbert 1960 and Elliott 1963 are named as the model's origin, which is
attribution rather than reproduction. "Burst-noise channel" is the model's
own term, not a quotation.

🔴 Not every request is possible, and that is what ``achievable`` is for.
``p <= 1`` needs ``mean_burst >= loss / (1 - loss)``, so 90% loss cannot
arrive in runs of 5 - runs that short leave too little room between them.
Expand Down
Loading
Loading