Skip to content

Drop the non-ADIF QSL_MANUAL tag from the desktop and iOS exporters - #719

Merged
patrickrb merged 1 commit into
devfrom
fix/697-drop-qsl-manual-ports
Aug 2, 2026
Merged

Drop the non-ADIF QSL_MANUAL tag from the desktop and iOS exporters#719
patrickrb merged 1 commit into
devfrom
fix/697-drop-qsl-manual-ports

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Fixes #697.

What was left

QSL_MANUAL is not a field in the ADIF spec, so strict consumers (LoTW's upload validator, Club Log, other loggers) can reject it or silently drop the record's tail.

Android was already fixed by #701, which moved the flag to APP_FT8AF_QSL_MANUAL — the spec's APP_<PROGRAMID>_<FIELD> escape hatch — and kept the bare name as a read-only legacy alias so older exports still import. That's a better answer than the issue proposed (drop it / use QSLMSG), because QSL_RCVD is not a substitute: it means a QSL was received, which is a different fact from "operator ticked manually confirmed".

The two ports were missed and still emitted the bare name:

Location What
desktop/src-tauri/src/db.rs:378 file export
desktop/src-tauri/src/db.rs:425 adif_record() — the WSJT-X "Logged ADIF" UDP message to JTAlert/N1MM
ios/FT8AFKit/Sources/FT8Engine/Adif.swift:19 iOS export

Why dropping is right here, not the APP_ field

On both ports the tag is a hardcoded N carrying no information:

  • Desktop's QSL_RCVD does vary with r.confirmed, but QSL_MANUAL was always N
  • iOS's QsoRecord has no confirmation state at all, so both QSL flags are constants

And nothing reads it back: desktop has no ADIF import path, and the iOS parser explicitly ignores QSL flags (Adif.swift:103). Android's importer sets isQSL only when the key is present, so an absent field and an explicit N produce the identical import. Emitting APP_FT8AF_QSL_MANUAL:N from these two would just be a conformant way to write nothing.

Two corrections to the issue

  • It cites DatabaseOpr.java:1255-1257 as an emitter. On current dev those are commented-out dead code (:1158/:1160); the live Android path goes through AdifRecord.
  • It flags QSLRecord.java:191-192's "present in LoTW" comment as suspect. That comment sits on QSL_RCVD (line 190), which genuinely is a real ADIF field present in LoTW exports — the QSL_MANUAL handling is separate and correctly documented as legacy-compat.

Testing

  • New Rust export_omits_the_non_adif_qsl_manual_tag covers both emitters and asserts QSL_RCVD still carries the confirmed flag. cargo test --lib: 108 passed, 0 failed.
  • The two iOS tests assert full-string equality, so they pin the absence by construction; updated to match.
  • cargo fmt: my changed lines are clean (the file has pre-existing drift at lines 127/203/241 that I deliberately left alone to keep the diff readable).

I could not run swift test — this was done on Windows, which has no Swift toolchain. The iOS changes are a mechanical string edit in one source line and two expected-string assertions, but they need the swift test + simulator build CI job to confirm.

🤖 Generated with Claude Code

QSL_MANUAL is not a field in the ADIF spec, so strict importers (LoTW's
validator, Club Log, other loggers) can reject or silently drop it. PR #701
fixed this on Android by moving the flag to APP_FT8AF_QSL_MANUAL — the
spec's APP_<PROGRAMID>_<FIELD> escape hatch — but the desktop and iOS ports
were missed and still emit the bare name.

Neither port needs the APP_ field, because on both the tag is a hardcoded
"N" carrying no information: desktop's QSL_RCVD tracks r.confirmed, but
QSL_MANUAL was always N, and iOS's QsoRecord has no confirmation state at
all. Nothing reads it back either — desktop has no ADIF import path, and
the iOS parser ignores QSL flags. Android's importer keys on the field
being present, so an absent field and an explicit N are the same import.
Dropping it is therefore lossless.

Covers both desktop emitters: the file export and adif_record(), which is
what goes out over the WSJT-X "Logged ADIF" UDP message to JTAlert/N1MM —
the one most likely to meet a strict parser.

Fixes #697

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 36.21%. Comparing base (efa264e) to head (42c4a48).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #719      +/-   ##
============================================
+ Coverage     36.12%   36.21%   +0.09%     
  Complexity      286      286              
============================================
  Files           245      245              
  Lines         29973    29986      +13     
  Branches       3930     3930              
============================================
+ Hits          10827    10860      +33     
+ Misses        18797    18777      -20     
  Partials        349      349              
Flag Coverage Δ
desktop 63.06% <100.00%> (+0.55%) ⬆️
ios 96.51% <100.00%> (ø)
native 9.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
desktop/src-tauri/src/db.rs 93.69% <100.00%> (+4.08%) ⬆️
ios/FT8AFKit/Sources/FT8Engine/Adif.swift 100.00% <100.00%> (ø)
ios/FT8AFKit/Tests/FT8EngineTests/AdifTests.swift 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the non-standard QSL_MANUAL ADIF tag from the desktop (Rust) and iOS (Swift) exporters so strict ADIF consumers don’t reject or truncate exported records, aligning those ports with the earlier Android fix strategy of not emitting non-spec fields.

Changes:

  • Desktop: stop emitting <QSL_MANUAL:1>N in both the file exporter and the WSJT-X “Logged ADIF” UDP adif_record() output.
  • Desktop: add a Rust unit test asserting QSL_MANUAL is absent while QSL_RCVD still reflects the confirmed flag.
  • iOS: remove QSL_MANUAL from export output and update string-equality tests accordingly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
ios/FT8AFKit/Tests/FT8EngineTests/AdifTests.swift Updates expected ADIF strings to match the removal of QSL_MANUAL.
ios/FT8AFKit/Sources/FT8Engine/Adif.swift Stops emitting the non-spec QSL_MANUAL field in iOS exports.
desktop/src-tauri/src/db.rs Removes QSL_MANUAL from both ADIF emitters and adds coverage to prevent regressions.
Suppressed comments (1)

ios/FT8AFKit/Sources/FT8Engine/Adif.swift:15

  • This docstring says export matches desktop export_adif "exactly", but iOS hardcodes QSL_RCVD to N while desktop derives it from r.confirmed. Consider softening this to "matches field set/order" (with the QSL_RCVD exception) to avoid misleading future readers.
    /// Render `records` as an ADIF string in the given order. Matches desktop
    /// db.rs export_adif exactly: a fixed header, then per record the CALL field,
    /// `QSL_RCVD` (iOS has no confirmation state yet, so always `N`), every
    /// non-empty optional field, and comment + <eor>.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ios/FT8AFKit/Sources/FT8Engine/Adif.swift
@patrickrb
patrickrb merged commit 69dafd2 into dev Aug 2, 2026
20 checks passed
@patrickrb
patrickrb deleted the fix/697-drop-qsl-manual-ports branch August 2, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants