← Project notes

software / csharp / radio

AutoLog: .NET cross platform console logger

Some projects sit neatly at the intersection of two interests. AutoLog is one of mine: a C# application for logging amateur radio contacts and forwarding them to online logbooks.

The starting idea was straightforward. Watch a log file, spot a new contact, and submit it to a logbook service. That sounds like a small utility. Then come the interesting questions: what happens when the service is unavailable? What if the application closes halfway through a submission? And how do you recognise the same contact arriving twice?

Those questions are shaping AutoLog into something more substantial.

From a log file to a local logbook

AutoLog began around the ADIF files produced by software such as WSJT-X. ADIF is a format used to exchange amateur radio contact records; a contact is usually called a QSO.

The application can watch an append-only ADIF log and pick up newly added contacts. A terminal dashboard shows the contact history and delivery status, with optional distance estimates calculated from Maidenhead grid locators—short codes that radio amateurs use to describe geographic locations.

It now also supports entering contacts directly. That moves it beyond being only a bridge between an existing logger and an online service: it has its own persistent local history.

The interface uses Spectre.Console. For this stage of the project, a terminal gives me a practical place to develop the workflow while keeping the application relatively small.

“Saved” and “uploaded” mean different things

The most important design decision so far is separating local storage from remote delivery.

When AutoLog accepts a new contact, it saves the contact and its initial delivery requests together in a SQLite transaction. Network calls happen after that commit.

That distinction matters. An unavailable online logbook should leave a saved contact with an outstanding delivery, rather than make the contact disappear. The interface can report local success separately from the outcome of an upload.

Matching contacts reuse the existing local record and delivery set when their identity can be resolved. If an incoming record identifies the same contact but contains different data, AutoLog retains the conflicting payload while preserving the original accepted record.

There are limits to what that guarantees. Local duplicate handling does not guarantee exactly one remote submission: an upload could succeed immediately before the application stops, leaving it unable to record the success locally. Handling that uncertainty is part of the remaining work.

Building integrations as we go

One part of AutoLog I’m particularly interested in is how it connects incoming QSOs to logbook providers.

Contacts enter a shared ingestion pipeline, whether they come from the watched ADIF file or manual entry. That pipeline handles local acceptance, duplicate detection and delivery without making each input source understand every destination.

On the other side, providers plug into a common contract. AutoLog discovers their assemblies at startup, and each provider module uses dependency injection to register its implementation when its configuration is ready.

That gives me room to build integrations incrementally. Adding another online logbook means implementing its provider, rather than rewriting the contact ingestion, local history or terminal interface. Each newly accepted contact gets a delivery request for every active provider, with the results tracked separately.

QRZ is the first real integration. A mock provider gives me somewhere to exercise the same pipeline without sending contacts to an external service. For the console capture below, I loaded synthetic history and appended another contact while AutoLog was running. It detected the new QSO, saved it locally and delivered it to the mock provider.

AutoLog console output showing seven synthetic contacts and an active mock provider. All callsigns are demonstration placeholders.

AutoLog console output rendered from a demo run with synthetic contacts and a mock logbook provider. Station and contact callsigns are demonstration placeholders. Select the image to view it at full size.

The input abstraction also leaves room for additional QSO sources. The current console uses ADIF monitoring and manual entry; other adapters can build on that shared foundation.

The same separation applies to the interface. The core runtime handles contact ingestion, persistence and delivery without depending on the terminal presentation. A future interface could reuse that behaviour, although the console is the current focus.

What still needs work

AutoLog is experimental and hasn’t been released yet.

Retryable submissions are stored with a backoff schedule, but eligible retries currently run at startup. A continuous retry worker is still needed.

File handling also has boundaries. The application supports an initial historical import without automatically uploading the entire old logbook. Reconciling contacts added while AutoLog was stopped, and dealing with replaced or truncated files, need further work.

Those are useful problems to tackle before calling it ready.

For me, that is the appeal of AutoLog. It combines radio with the parts of software engineering I enjoy: clear boundaries, persistent state, and working out what should happen when an apparently simple operation only partly succeeds.

Back to the workbench →