* test: add bounded awaitUntil helper for timing-sensitive tests (#109) One small class per module (core and app-desktop share no test sources): polls a condition every 10 ms and fails with a message when the timeout elapses, so tests no longer guess with fixed Thread.sleep pauses. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(map): let TileFetcher take its nano clock as a test seam (#109) A package-private constructor accepts a LongSupplier for the monotonic clock the retry pause is measured against; the public constructors delegate with System::nanoTime, so production behaviour is unchanged. TileFetcherTest advances a fake clock instead of sleeping 20 ms three times and hoping the 1 ms pause has elapsed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(view): put CoalescingTrigger on a DelayTimer seam (#109) DelayTimer is a package-private one-shot timer (restart/stop) with a Factory; DelayTimer.SWING wraps javax.swing.Timer and is what the public constructor uses, so production behaviour is unchanged. A package-private constructor takes the factory. CoalescingTriggerTest fires a FakeDelayTimer itself: the negative checks (no second run, cancel prevents the run) are now deterministic and the four Thread.sleep calls are gone. One test keeps the real Swing timer end to end, waiting with awaitUntil. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(view): put LayoutAutosave on the DelayTimer seam (#109) The public constructor still builds a javax.swing.Timer via DelayTimer.SWING with the same 750 ms delay; a package-private constructor takes the factory. LayoutAutosaveTest fires a FakeDelayTimer on the event thread itself, so "cancelled means not written" no longer rests on a 1.2 s sleep and the burst test no longer sleeps 300 ms to prove the absence of a second write. The foreign-thread test keeps the real Swing timer path with awaitUntil. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(controller): inject scheduler and clock into On4KstConnectionManager (#109) A package-private constructor takes the ScheduledExecutorService and a LongSupplier for wall-clock milliseconds; the existing constructor delegates with the same single-thread supervisor and System::currentTimeMillis, so production behaviour is unchanged. Session's idle clocks start from the injected clock instead of a field initializer. The static parseMessageTimestamp keeps the wall clock: it sanity-checks server timestamps and is not part of the supervisor's timing. On4KstConnectionProbeTest drives unansweredProbeUsesExistingReconnectFlow through a ManualScheduler (immediate work on a real thread, delays and the five-second tick released on demand) and a fake clock: 90 s of silence sends the CK probe, which the test reads off the socket, 210 s fails the session, and the released backoff opens the second connection. The reflection-based state setup is gone because the state is now reached the production way; the test takes well under a second instead of seven. The two ignored await(2 s) results in the server stubs are asserted now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test: close the profile picker as soon as it shows instead of after 1.5 s (#109) The two display-bound picker tests disposed the modal dialog from a Swing timer after a fixed 1.5 s, which was long enough on a fast machine only and cost 3 s per run. The closer now waits with awaitUntil for the dialog to appear and disposes it on the event thread; the suite drops from 3.0 s to 0.26 s. Remaining fixed sleep: TopPriorityCandidatesWindowTest waits 400 ms for Compose's double-tap timeout; whether ImageComposeScene's time can advance it deterministically is still to be checked. Fixes #109 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
KST4Contest
KST4Contest is a Java-based client for the ON4KST chat, developed for coordinated VHF, UHF and microwave contest operation.
The application is developed by Marc Fröhlich (DO5AMF) and since 2026 Philipp Wagner (DN9APW).
Start here
- Project website
- Download Stable, Beta and Nightly builds
- Online manual
- GitHub wiki
- Issues and bug reports
- Development roadmap
What KST4Contest does
KST4Contest combines the ON4KST chat with information that is useful when coordinating contacts during a contest.
Among other things, it can:
- display and filter stations from the supported ON4KST chat categories;
- derive band and frequency information from chat messages and station names;
- maintain Worked and NOT QRV information for the available bands;
- calculate station priorities from distance, activity and other available information;
- manage internal skeds and received Win-Test skeds;
- use AirScout information when evaluating possible aircraft-scatter contacts;
- display stations, paths and additional propagation information on maps;
- exchange information with supported logging programs, Win-Test, PSTRotator and a local DX Cluster interface;
- provide configurable automatic replies for recurring chat requests.
Calculated scores, aircraft-scatter information and path assessments are operating aids. They depend on the available data and should not be treated as guarantees that a contact is possible.
Installation
Ready-to-use packages are available for Windows, Linux and macOS. These packages include the required Java runtime, so a separate Java installation is normally not necessary.
Use the central download page to select the appropriate build:
- Stable is intended for normal contest operation.
- Beta contains changes that are being prepared for a stable release.
- Nightly contains the latest automated development build and is mainly intended for testing.
Documentation
The documentation is available in German and English:
The Markdown sources used for the wiki and the generated PDF manuals are stored in github_docs.
Changes to operating behaviour should be documented together with their purpose and limitations. This is especially important for functions whose result depends on external data, heuristics or information derived from chat messages.
Building from source
Building KST4Contest requires JDK 21. The Gradle Wrapper included in the repository should be used, so a separate Gradle installation is not required.
Linux and macOS:
./gradlew clean build
Windows:
.\gradlew.bat clean build
Repository structure
core/– domain, controller and network code, free of user-interface technologyapp-desktop/– the Compose Multiplatform user interface and the packaging tasksgithub_docs/– German and English manual sourceswebsite/– project website sourcespackaging/– platform-specific packaging files
CI status
Documentation
Builds
License
KST4Contest is distributed under the GNU General Public License v3.0.