A PyQt6 desktop application for analyzing, benchmarking, and diagnosing USB storage devices on Linux. Provides a graphical interface to run common USB diagnostic commands and interpret their results in plain language.
| Feature | Description |
|---|---|
| Dynamic Device Detection | Lists USB storage devices with vendor, model, size, mount point, and negotiated bus speed |
| Real-time Hotplug Monitoring | Uses pyudev to detect device insertion/removal without UI freezing |
| Bus Analysis | Runs lsusb -t to show USB topology and negotiated speed (480M vs 5000M) |
| Read Benchmark | Executes hdparm -tT (via pkexec) for cached and physical read speeds |
| Write Benchmark | Writes 1 GB test file with dd (with space check via psutil) |
| Advanced fio Benchmark | Sequential + 4K random read/write with IOPS, latency, and throughput |
| SMART Health | Runs smartctl -a (via pkexec) for device health diagnostics |
| Dual Output Panels | Technical Log (raw output) + User Analysis (plain-language interpretation) |
| Explanations Tab | A Markdown tutorial with diagrams that teaches how to spot a real SuperSpeed port |
| Collapsible Test List | The five test checkboxes stay folded into one line and show how many are enabled |
| About Box | Author, contact, licence and technologies, with the large application icon and clickable links |
| Progress Tracking | Real-time progress bar for long-running tests |
| Internationalization | Full Spanish translation included; the UI, the tutorial and the About box all follow the system locale |
(Add screenshots here when available)
# Debian/Ubuntu
sudo apt install python3-pyqt6 python3-pyudev python3-psutil \
util-linux smartmontools fio policykit-1
# Arch Linux
sudo pacman -S python-pyqt6 python-pyudev python-psutil \
util-linux smartmontools fio polkit
# Fedora
sudo dnf install python3-pyqt6 python3-pyudev python3-psutil \
util-linux smartmontools fio polkitpip install PyQt6 pyudev psutil# Debian/Ubuntu
sudo apt install qt6-translations-l10n qt6-tools-dev-tools
# Arch Linux
sudo pacman -S qt6-translations qt6-toolsqt6-translations-l10n provides Qt's own Spanish strings for the standard
dialogs. The Linguist tools are needed only when regenerating the .qm.
git clone https://github.com/wachin/usb-speed-tester.git
cd usb-speed-tester
# Install Python deps (if not using system packages)
pip install -r requirements.txtpython3 main.pyThe interface follows your desktop language (Spanish and English ship today). To start it in a specific language, see Testing another language from the terminal.
- Select a USB device from the dropdown
- Open Select tests and untick anything you do not want to run (all enabled by default)
- Click Run Selected Tests
- View results in the Technical Log tab
- Read plain-language analysis in the User Analysis tab
- Open the Explanations tab for the illustrated tutorial on
SSports, or the About menu for author, licence and technology credits
- Bus Analysis - No privileges needed. Shows if device negotiated USB 2.0 (480M) or USB 3.x (5000M)
- Read Benchmark - Requires root via
pkexec. Shows cached vs physical read speeds - Write Benchmark - Writes 1 GB file. Checks for 1.5 GB free space first
- fio Benchmark - Runs 4 jobs (seq read/write, rand 4K read/write) on 256 MB each
- SMART Health - Requires root. May show "not supported" for USB flash drives
usb-speed-tester/
├── main.py # Complete application (single-file for simplicity)
├── Makefile # translation/install/uninstall/clean/check targets
├── assets/
│ ├── svg/ # Editable vector sources of the tutorial diagrams
│ │ ├── usb3-ss-logo.svg # SS + USB trident SuperSpeed emblem
│ │ ├── usb3-ss-ports.svg # Two SS ports vs. a USB 2.0 port
│ │ └── usb3-ss-laptop.svg # SS marking engraved next to a laptop port
│ └── icon/ # Application icon: vector source + generated sizes
│ ├── usb-speed-tester.svg
│ └── usb-speed-tester-<size>.png # 16, 24, 32, 48, 64, 128, 256
├── tutorial/ # One folder per language for the Explanations tab
│ ├── EN/
│ │ ├── tutorial.md # The tutorial itself
│ │ └── usb3-ss-*.png # Diagrams, linked relatively
│ └── ES/
│ ├── tutorial.md # Spanish translation
│ └── usb3-ss-*.png
├── data/ # Files installed outside the program directory
│ ├── io.github.wachin.USBSpeedTester.desktop
│ ├── io.github.wachin.USBSpeedTester.metainfo.xml
│ └── usb-speed-tester.1 # Manual page
├── debian/ # Debian source package (see "Debian packaging")
├── tools/
│ └── svg_to_png.py # Regenerates every PNG above from the SVG sources
├── .gitignore
├── LICENSE
├── README.md
└── translations/ # Qt Linguist catalogues
├── usbtester_es.ts # Spanish source (editable)
└── usbtester_es.qm # Spanish compiled (loaded at runtime)
At runtime the program looks for assets/, tutorial/ and translations/
next to itself, then in ../share/usb-speed-tester, then in
/usr/local/share/usb-speed-tester and /usr/share/usb-speed-tester. Set
USB_SPEED_TESTER_DATA to override the search. This is what lets the same
source tree work as a checkout, as a make install and as a Debian package.
The Explanations tab renders tutorial/<LANG>/tutorial.md as Markdown,
together with the diagrams stored in the same folder. The folder is picked from
the system locale: Spanish systems open tutorial/ES, anything else falls back
to tutorial/EN.
To add another language, copy the tutorial/EN folder, translate
tutorial.md, and run the converter — it refreshes every
tutorial/<LANG> folder it finds:
cp -r tutorial/EN tutorial/FR
# translate tutorial/FR/tutorial.md
python3 tools/svg_to_png.pyThe diagrams are drawn as SVG and shipped as PNG, because a PNG carries its own glyphs: the labels can never shift or disappear because a font is missing on the machine running the program. The same applies to the application icon.
Edit the SVG sources, then regenerate every PNG:
python3 tools/svg_to_png.py # diagrams (1x + 2x) and the app icon
python3 tools/svg_to_png.py --list # preview without writing anything
python3 tools/svg_to_png.py --scales 1,2,3 # add even sharper variants
python3 tools/svg_to_png.py usb3-ss-logo.svg # only one diagram
python3 tools/svg_to_png.py --no-icon # diagrams only
python3 tools/svg_to_png.py --clean # drop stale PNGs firstDiagram files are written as name.png at scale 1 and name@2x.png for larger
scales. The tutorial links the plain name; at runtime the application picks the
smallest variant that still covers the screen's device pixel ratio, and falls
back to the SVG source if no PNG has been generated yet. Keep scale 1 in
--scales, since that is the file the Markdown points at.
Tip: QtSvg (used by the converter and by that fallback) does not implement every SVG feature. In particular it ignores
<tspan>elements that carry their ownycoordinate, so a multi-line text block written by Inkscape collapses into a single line. Give each line its own<text>element.
Every UI string goes through self.tr(), so the interface follows the system
locale. Spanish is fully translated (81 strings) and ships ready to use:
| File | Role |
|---|---|
translations/usbtester_es.ts |
Editable source, open it with Qt Linguist |
translations/usbtester_es.qm |
Compiled translation loaded at runtime |
TranslationManager looks for usbtester_<locale>.qm using the full locale
(es_EC) first and the bare language (es) second, so one file covers every
Spanish-speaking region. It also loads Qt's own qtbase_<language>.qm, which
translates the standard buttons such as Close → Cerrar. The tutorial and the
About box follow the same locale.
To add another language:
# 1. Extract the strings into a new catalogue
pylupdate6 main.py -ts translations/usbtester_fr.ts
# 2. Translate it (Qt Linguist is the friendliest option)
linguist-qt6 translations/usbtester_fr.ts
# 3. Compile it — the application picks it up on the next start
lrelease translations/usbtester_fr.ts
# 4. Copy tutorial/EN to tutorial/FR and translate tutorial.md
cp -r tutorial/EN tutorial/FR
python3 tools/svg_to_png.py # gives tutorial/FR its diagramsRe-running pylupdate6 on an existing .ts merges the new strings and keeps
the translations you already made, so it is safe to repeat after editing
main.py.
The interface, the tutorial and the About box all follow the system locale, so you can try any language without touching the code:
# Run in English, whatever your desktop language is
LANGUAGE=en python3 main.py
# Back to your normal language
python3 main.pyImportant: use
LANGUAGE, notLANG. Qt givesLANGUAGEpriority overLANGandLC_ALL, and many desktops export aLANGUAGEvalue (for exampleLANGUAGE=es_EC:es), so overriding onlyLANGhas no effect.
| Command | Locale seen by the app |
|---|---|
python3 main.py |
your desktop locale, e.g. es_EC |
LANGUAGE=en python3 main.py |
en_US |
LANGUAGE=en_US.UTF-8 python3 main.py |
en_US |
LC_ALL=C LANG=C python3 main.py |
C (also English) |
English is the language the strings are written in, so there is no
usbtester_en.qm: with an English locale no translator is installed at all and
the Qt standard buttons (Close, Cancel) also stay in English.
MainWindow
├── DeviceMonitor (QThread) # pyudev hotplug monitoring
├── BusAnalysisWorker (QThread) # lsusb -t
├── ReadBenchmarkWorker (QThread) # hdparm -tT via pkexec
├── WriteBenchmarkWorker (QThread) # dd with progress
├── FioBenchmarkWorker (QThread) # fio JSON output
└── SmartHealthWorker (QThread) # smartctl -a via pkexec
All workers inherit from BaseTestWorker which provides:
progresssignal (percent, message)finishedsignal (device_id, output)errorsignal (message)- Cancellation support
The debian/ directory holds a complete Debian source package following
Policy 4.7.4, debhelper compat 13 and the DEP-5 machine-readable copyright
format. The binary package installs:
| Path | Contents |
|---|---|
/usr/bin/usb-speed-tester |
The program |
/usr/share/usb-speed-tester/ |
assets/, tutorial/ and translations/ |
/usr/share/applications/io.github.wachin.USBSpeedTester.desktop |
Menu entry |
/usr/share/metainfo/io.github.wachin.USBSpeedTester.metainfo.xml |
AppStream metadata |
/usr/share/icons/hicolor/<size>/apps/io.github.wachin.USBSpeedTester.png |
Icons (7 sizes) |
/usr/share/man/man1/usb-speed-tester.1.gz |
Manual page |
Build it with:
sudo apt install debhelper dh-python qt6-l10n-tools lintian devscripts
dpkg-buildpackage -us -uc # source + binary, output lands in ..
lintian ../usb-speed-tester_1.0.0-1_amd64.changesdebian/rules delegates to the upstream Makefile, so the package and a plain
sudo make install produce exactly the same layout. The compiled .qm
translations are regenerated from the .ts catalogues during the build.
- File an ITP (Intent To Package) bug against the
wnpppseudo-package, for example withreportbug --kinds=wnpp, and add its number todebian/changelogas* Initial release. (Closes: #1234567). Lintian currently warnsinitial-upload-closes-no-bugsuntil that is done. - Tag the upstream release so
debian/watchcan find it, for examplegit tag -s v1.0.0 -m "usb-speed-tester 1.0.0", and push the tag. - Push an
debian/latestbranch (seedebian/gbp.conf) if you want to build withgbp buildpackage. - Find a sponsor: publish the package on
mentors.debian.net and request sponsorship on
the
debian-mentorsmailing list.
Lintian is clean apart from those two upload-time items:
W: initial-upload-closes-no-bugs # fix by filing the ITP
W: newer-standards-version 4.7.4 # trixie's lintian is older than Policy 4.7.4.1
| Issue | Solution |
|---|---|
| "pkexec not found" | Install policykit-1 or polkit |
| "hdparm: NOT_IOCTLABLE" | Unmount device first or check permissions |
| "fio timed out" | Reduce test size or increase timeout in code |
| "SMART not supported" | Normal for USB flash drives |
| Device not detected | Check lsblk and lsusb -t manually |
| Bus speed shows Unknown | Requires udev access to USB device ancestors |
GNU General Public License v3.0 - see LICENSE for details.
Copyright © 2026 Washington Indacochea Delgado · linuxfrontier@proton.me
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
- PyQt6 - Qt6 Python bindings
- pyudev - Linux device monitoring
- psutil - System and process utilities
- fio - Flexible I/O tester
- smartmontools - SMART monitoring tools
