Skip to content

fix(sync): emit MHz frequency to LoTW + QRZ instead of dividing by 1M#212

Merged
patrickrb merged 1 commit into
mainfrom
fix/lotw-qrz-frequency-mhz
May 12, 2026
Merged

fix(sync): emit MHz frequency to LoTW + QRZ instead of dividing by 1M#212
patrickrb merged 1 commit into
mainfrom
fix/lotw-qrz-frequency-mhz

Conversation

@patrickrb
Copy link
Copy Markdown
Owner

@patrickrb patrickrb commented May 12, 2026

Summary

Both LoTW and QRZ uploads have been silently dropping every QSO since #185. They divide contacts.frequency by 1,000,000 thinking it's Hz, but the column (numeric(10, 6)) physically can't hold Hz for HF — 14.205 MHz in Hz is 14_205_000, which needs 8 integer digits, the column allows 4. The form, ADIF import, and QRZ download all write MHz.

For LoTW that meant the .tq8 carried <FREQ:8>0.000014 for a 14.205 MHz QSO. The canonical sign-string used the same wrong value, so the signature verified cleanly on both sides — LoTW accepted the upload, the app marked the rows lotw_qsl_sent='Y', and LoTW silently discarded each QSO because 0.000014 MHz isn't a valid amateur frequency. The uploads "succeeded" but nothing appeared on the LoTW site.

QRZ has the same bug and is almost certainly also dropping these silently.

The author of #185 left a comment (upload/route.ts:250-253) chasing this exact confusion the wrong way — they inferred Hz from QRZ's (contact.frequency / 1_000_000) line, when actually that was the bug.

Fix

  • src/lib/lotw.ts: rename freqToMhz(freqHz)formatFreqMhz(freqMhz) and drop the / 1_000_000 (input is already MHz). Update all 4 callers (signing + ADIF emit, freq + freq_rx).
  • src/lib/qrz.ts: same change in contactToQRZFormat.
  • src/app/api/lotw/upload/route.ts: replace the misleading comment.

Cleanup after merging

QSOs uploaded with the bad frequency are now marked lotw_qsl_sent='Y' / qrz_qsl_sent='Y' in the DB, so a fresh sync skips them. Reset the rows affected by the broken builds (#185 merged 2026-05-10):

-- LoTW: clear the 'sent' flag on contacts the broken builder touched.
UPDATE contacts
SET lotw_qsl_sent = NULL, updated_at = NOW()
WHERE lotw_qsl_sent = 'Y'
  AND created_at >= '2026-05-10';

-- QRZ: same idea, using the canonical qrz_qsl_sent column (the one the manual
-- /search "Sync to QRZ" button writes). Note: there's a separate schema-drift
-- bug where the Contact model reads/writes qrz_sync_status, but that column
-- isn't in the canonical Drizzle schema — fresh installs don't have it. Don't
-- use qrz_sync_status here; use qrz_qsl_sent which IS canonical.
UPDATE contacts
SET qrz_qsl_sent = NULL, qrz_qsl_sent_date = NULL, updated_at = NOW()
WHERE qrz_qsl_sent = 'Y'
  AND created_at >= '2026-05-10';

Adjust the date if you know exactly when the bad builder first ran. Then trigger a manual upload on /lotw (or wait for the 01:00 UTC cron) and the QSOs should land.

Test plan

  • After deploying, log a fresh QSO at e.g. 14.205 MHz / 20m / FT8
  • Click "Upload now" on /lotw — confirm upload log shows `completed`
  • Open LoTW website → Your QSOs → the QSO appears with the correct frequency
  • Same check on qrz.com/db//logbook
  • After running the SQL above, verify older affected QSOs also appear

🤖 Generated with Claude Code

contacts.frequency is numeric(10,6) and stores MHz directly — the column
precision can't physically hold Hz for HF (14.205 MHz in Hz is 14205000,
which needs 8 integer digits; the column allows 4). The form, ADIF import,
and QRZ download all write MHz.

Both upload paths assumed Hz and divided by 1_000_000:

  LoTW (src/lib/lotw.ts):  freqToMhz(14.205) = 0.000014 MHz
  QRZ  (src/lib/qrz.ts):   (14.205 / 1000000).toString() = "0.0000142..."

For LoTW that produced a .tq8 whose canonical sign-string used the wrong
frequency. The signature still verified (same wrong value on both sides),
so LoTW accepted the upload and the app marked contacts lotw_qsl_sent='Y'.
LoTW then silently dropped each QSO because 0.000014 MHz isn't a valid
amateur frequency — uploads "succeeded" but nothing appeared on the LoTW
site. The author of #185 chased this confusion the wrong way in the
upload-route comment, inferring Hz from the QRZ uploader's matching bug.

Fix:
- Rename freqToMhz → formatFreqMhz, drop the /1_000_000 (input is MHz).
- Same for contactToQRZFormat in qrz.ts.
- Replace the misleading "stored in Hz" comment in the upload route.

After this lands, contacts uploaded with the bad frequency are still
marked lotw_qsl_sent='Y'/'qrz_sync_status'='synced' in the DB and won't
re-upload. PR description has a one-liner to reset them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nodelog Ready Ready Preview, Comment May 12, 2026 1:30pm

Request Review

@patrickrb patrickrb merged commit f657dfb into main May 12, 2026
7 checks passed
@patrickrb patrickrb deleted the fix/lotw-qrz-frequency-mhz branch May 12, 2026 14:01
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.

1 participant