Hi Saku!
Recently, after enabling QRZ on CQRLOG Alpha 142, I noticed that only QSOs after activation are synced, which I can update later. All previous QSOs are unsynced because I get an error related to the NO LOGIDS parameter, which blocks subsequent uploads. Currently, the only way to use QRZ on CQRLOG is to create a new log from scratch, but that's not the case for me; so I've gone back to manually uploading ADIF files to QRZ. Here's my question: Is it possible to add QRZ sync to CQRLOG?
73 de IK0DWJ (Beppe).




Hi Beppe!
Yes, that is a known problem that old qsos do not have QRZ log ID.
It looks like it can be fetched with XML command "FETCH", but as well it seems to exist also in ADIF data that is manually downloaded from QRZ logbook.
As the operation is needed only once per log I think there is no mind to add it to online log upload actions.
Instead it could be separate function, or even separate bash script, that could set the QRZ log IDs to qsos uploaded manually before QRZ online log option.
I can consider that kind of solution, when time permits.
--
Saku
OH1KH
Hi Saku!
I tried your script but it didn't work. I got all QSOs NOT FOUND. Example:
QSO NOT FOUND: 2024-12-10 16:37 ZF2OO 15m CW QRZlog_logid:1296111613
After some research, the problem was resolved; it was a case discrepancy in the Bandwidth field.
Google AI pointed this out to me. In the CQRlog database, the bandwidth is recorded in uppercase, for example, 15M, which I verified with the following command:
mysql -S /home/$USER/.config/cqrlog/database/sock cqrlog001 -e "SELECT qsodate, time_off, callsign, band, mode FROM cqrlog_main WHERE callsign='ZF2OO';
+----------------+-----------+------------+--------+--------+
| qsodate | time_off | callsign | band | mode |
+----------------+-----------+------------+--------+--------+
| 2024-12-10 | 16:37 | ZF2OO | 15M | CW |
+----------------+-----------+------------+--------+--------+
The script instead extracts the lowercase band from the QRZ ADIF file: 15m.
So, as suggested by the AI, I modified the following block of your script, on line 81 if I remember correctly:
if grep -qi "band:" <<< "$line"; then
band=${line##*">"}
fi
and I added a line to convert the band to uppercase, like this:
if grep -qi "band:" <<< "$line"; then
band=${line##*">"}
band=${band^^}
fi
It's working now, but it'll take a few hours to process about 16,000 QSOs with my old Core 2 Duo. It's late at night here, and I'm going to sleep, but I'll leave the PC on. I'll let you know tomorrow if everything went well. Hopefully...
73 de Beppe IK0DWJ
--
Giuseppe
IKØDWJ
HI Beppe!
Very good point!
It is just funny how that happened so. Here is beginning of my log extracted from QRZ:
As you see I got it as upper case and that's why all worked here.
It makes no harm to put it uppercase in any case, so that is a good fix.
You are right, it is sloooooow!. That is because it uses both bash and grep. There are also other ways to find substring from line, but I did not get them work.
Perhaps I need to test again.
Anyhow, this is needed only once for a log and can be run at background so it does not matter a lot.
--
Saku
OH1KH
Hi Saku!
The first script, although very slow, worked, but it left me with about 150 unsynchronized QSOs out of a total of 16,745. The second script you sent me was much faster and more accurate, reducing the unsynchronized QSOs to about 50. Then, using the AI, I was able to synchronize them all by querying the database and executing specific commands and scripts. Finally, I asked the AI to adapt and improve your second script based on all the experience it gained working with my database. I'm attaching it for your evaluation. I haven't tested it from scratch yet with one of my unsynchronized backups, but I will soon.
#!/bin/bash
clear
if [ $# -ne 2 ]; then
echo "====================================================================="
echo " CQRLOG to QRZ.com - SMART SYNC SCRIPT (Enhanced Version 2026)"
echo "====================================================================="
echo "Usage: $0 "
echo "Example: $0 ik0dwj.adi cqrlog001"
echo
echo "Note: Start CQRLOG before running the script to activate the socket."
exit
fi
# Automatic socket configuration based on current user
sqlcmd="mysql -S /home/$USER/.config/cqrlog/database/sock $2 --skip-column-names -se "
qsook=0
qsofail=0
total=0
# Subroutine to clear variables for each record
function newqso(){
call=""
dat=""
tim_off=""
band=""
mode=""
qrzid=""
}
function idstoresize() {
local count=$($sqlcmd "SELECT count(id) FROM id_store" 2>/dev/null)
echo "${count:-0}"
}
# Initial database connection check
if ! $sqlcmd "SELECT 1" &>/dev/null; then
echo "❌ ERROR: Unable to connect to CQRLOG via socket."
echo "Verify that CQRLOG is open and that the database name ($2) is correct."
exit 1
fi
echo "====================================================================="
echo " Starting smart synchronization of ADIF records..."
echo "====================================================================="
echo "Reading ADIF file : $1"
echo "CQRLOG Database : $2"
echo "id_store records : $(idstoresize) (Current)"
echo "---------------------------------------------------------------------"
echo "Processing... Please wait until the process finishes."
echo "---------------------------------------------------------------------"
# Count total blocks for the graphical counter
total_lines=$(grep -c "" "$1" 2>/dev/null || echo "0")
newqso
while IFS= read -r line || [[ -n "$line" ]]; do
# Extract and normalize data from ADIF file
if [[ "$line" == *""}; call=${call^^}; fi
if [[ "$line" == *""}; band=${band^^}; fi
if [[ "$line" == *""}; mode=${mode^^}; fi
if [[ "$line" == *""}
dat="${dat:0:4}"-"${dat:4:2}"-"${dat:6:2}"
fi
if [[ "$line" == *""}
tim_off="${tim_off:0:2}":"${tim_off:2:2}:00"
fi
if [[ "$line" == *""}; fi
# Process at the end of each ADIF record ()
if [[ "$line" == *""* ]]; then
total=$((total+1))
# Show dynamic graphical counter on the same line
echo -ne " ⏳ Processing: QSO $total of $total_lines...\r"
cqid=""
# If the record already has an ID in id_store, skip it upfront (Speeds up the process)
# Prevents Duplicate Entry key conflicts
if [ -n "$qrzid" ]; then
# --- LEVEL 1: Strict Original Search (Exact Time and Mode) ---
cqid=$($sqlcmd "SELECT id_cqrlog_main FROM cqrlog_main WHERE qsodate='$dat' AND callsign='$call' AND time_off=LEFT('$tim_off',5) AND band='$band' AND mode='$mode' AND id_cqrlog_main NOT IN (SELECT id_cqrlog_main FROM id_store) LIMIT 1")
# --- LEVEL 2: Time Tolerance and Character Correction (O vs 0) ---
if [ -z "$cqid" ]; then
# Generates a variant replacing 'O' with '0' and vice versa to work around character encoding bugs
call_alt=$call
if [[ "$call" == *"O"* ]]; then call_alt="${call//O/0}"; fi
if [[ "$call" == *"0"* ]]; then call_alt="${call//0/O}"; fi
cqid=$($sqlcmd "SELECT id_cqrlog_main FROM cqrlog_main WHERE qsodate='$dat' AND (callsign='$call' OR callsign='$call_alt') AND band='$band' AND (mode='$mode' OR mode='DIGI' OR mode='DATA' OR mode='RTTY') AND ABS(TIME_TO_SEC(TIMEDIFF(CONCAT(time_off,':00'), '$tim_off'))) <= 180 AND id_cqrlog_main NOT IN (SELECT id_cqrlog_main FROM id_store) ORDER BY time_off ASC LIMIT 1")
fi
# --- LEVEL 3: Surgical Solution for Portable / Foreign Stations (Slashed /) ---
if [ -z "$cqid" ] && [[ "$call" == *"/"* ]]; then
p1=$(echo "$call" | cut -d'/' -f1); p2=$(echo "$call" | cut -d'/' -f2)
if [ ${#p1} -ge ${#p2} ]; then call_base="$p1"; else call_base="$p2"; fi
cqid=$($sqlcmd "SELECT id_cqrlog_main FROM cqrlog_main WHERE qsodate='$dat' AND callsign LIKE '%${call_base}%' AND band='$band' AND ABS(TIME_TO_SEC(TIMEDIFF(CONCAT(time_off,':00'), '$tim_off'))) <= 300 AND id_cqrlog_main NOT IN (SELECT id_cqrlog_main FROM id_store) ORDER BY time_off ASC LIMIT 1")
fi
fi
# Local database write phase
if [ -n "$qrzid" ] && [ -n "$cqid" ]; then
# Safe insertion protected against duplicates
$sqlcmd "INSERT INTO id_store (id_cqrlog_main, qrz_logid) SELECT $cqid, '$qrzid' WHERE NOT EXISTS (SELECT 1 FROM id_store WHERE id_cqrlog_main=$cqid)"
# Clears the counter line and prints the synchronized record to the screen
echo -ne "\033[K"
echo "✅ Synchronized: $call ($dat $band) -> Local ID: $cqid"
qsook=$((qsook+1))
else
qsofail=$((qsofail+1))
fi
newqso
fi
done < "$1"
# Final cleanup of the terminal interface and report
echo -ne "\033[K"
echo "---------------------------------------------------------------------"
echo " Synchronization Finished!"
echo "---------------------------------------------------------------------"
echo " Records successfully aligned in this session : $qsook"
echo " Records skipped (already aligned or not found) : $qsofail"
echo " Total ADIF records examined : $total"
echo " New total record count in id_store : $(idstoresize)"
echo "---------------------------------------------------------------------"
exit 0
Thank you for your time.
73 de IK0DWJ (Beppe)
--
Giuseppe
IKØDWJ
HI Beppe!
Oh, looks very AI !
There are lot of things I do not understand.
For example why letter "O" should be replaced with number "0" (zero) ?
And also the part that collects call, mode and band.(#Extract and normalize data from ADIF file)
if [[ "$line" == *""}; in every substring fetch looks nonsense to me. (maybe putting to forum message has dropped something?)
But if it works for you, be happy :-)
--
Saku
OH1KH
Hi Saku!
The problem may have arisen in the log file exported by QRZ, which should be in ASCII format and not UTF-8 as I believe CQRLOG expects. Can you confirm this? I'm a simple user, not a programmer. Checking the errors returned by your script, I discovered that a callsign had been misinterpreted. Investigating the cause, I discovered that QRZ had incorrectly exported it with the letter O, while I had saved it in the log with the number 0 (as in the case of the Romanian centenary callsign YR100R). To fix the problem, I selectively selected only that callsign, and the AI then summarized all the individual operations performed in a final script, which obviously might not be suitable for bulk synchronization. You're right: AI isn't the solution, but it can help identify individual problems. And there are problems with the synchronization process. Another thing I need to check, especially since I started using automatic uploading to QRZ.COM as soon as it was made available on CQRLOG, is that some QSOs used by QRZ for prizes may no longer be editable. CQRLOG first deletes and then uploads the edited QSO. Someone familiar with how QRZ.COM works could shed some light on this. My idea, however, is to disable QRZ from CQRLOG and manually upload the ADIF files, as I did before, without any problems or wasted time. Hi Saku, take care and thanks for your contribution.
73 from Beppe IK0DWJ
--
Giuseppe
IKØDWJ
OH, i can see my reply from this morning.
Possible forgot to save and used only preview.
ADIF standard says that .adi file tags should use only 7bit ASCII letters in tags. That means roughly numbers and alphabets A-Z.
Most programs and web sites roll over this standard and place UTF-8 characters in tags like NAME and QTH, etc.
Problem arises when count of charcters differs from count of bytes used to show each charcter. What you see is NOT what you get if we are looking from programmers side.
You can not count bytes, corresponding characters of name, if you do not know what encoding is used. That is why ADIF standard says that 7bit ASCII is the one to be used. Then one letter is always one byte.
There are some tags named with extension _INTL, like NAME_INTL those tags can have UTF-8 character set. The limitation by standard is that INTL tags may only be used in XML formatted adif ".adx"-files.
My version of Cqrlog tries to follow standard, but breaks it by using also INTL tags in ".adi" files. That is needed because Cqrlog uses adif files as log backups and without using INTL with Names and QTHs, etc. they do not restore properly.
----------------
What comes to QRZlog QSO updates Cqrlog uses "first delete, then insert" method when qso must be updated to online logs (all supported online logs).
QRZlog support follows this same way.
If QRZ uses QRZlog_ID for connection qso to some other functions (awards, or what ever) updating qso will break this because updated qso gets new QRZlog_ID with delete+insert action.
On the other hand QRZ API says that INSERT action can have option REPLACE. But warns that:
WARNING: This WILL overwrite confirmed QSOs with the supplied unconfirmed QSO until the new QSO data is verified as a match
What ever that means, it looks like same problem that we get using delete+insert method as replace.
That's why I have not implemented that because there is no better description what happens. Specially does the QRZlog_ID change, or not.
--
Saku
OH1KH
Okay. I'm not going to inject myself into the middle of a "debate" between two parties with different ideas over standards. I do have workarounds. They're not perfect, requiring extra steps, but they do work. I respectfully withdraw my request.
73,
Mark, W1MM
Hi Saku,
I completely understand your point. The QRZ support on CQRLOG should have been limited to the initial entry of new QSOs only. While CQRLOG already includes some options, they are global settings that apply to all enabled online logs. To be clear, QRZ does not consider a LoTW confirmation sent by CQRLOG, and the same goes for paper QSL, eQSL, and other confirmations.
QRZ awards stars and credits confirmations in only two ways: either the contact uploads their log to QRZ as well, or you enable LoTW integration directly on the QRZ website to download confirmations from LoTW. Therefore, updating a QSO already sent to QRZ via CQRLOG is just a waste of time and a source of issues. The QRZ policy enforces strict rules to ensure the integrity of awarded credits. Once a confirmed QSO officially contributes to an award, the record is permanently locked to protect the historical data of that award.
If I wanted to edit a simple comment field in one of these locked QSOs, I could not do it through CQRLOG. During the modification process, CQRLOG would first attempt to delete the existing QSO. Since the record is locked and cannot be deleted, CQRLOG would receive an error that blocks the upload queue for new QSOs, causing a process disruption. For these reasons, I had to disable QRZ in CQRLOG.
As things stand, the only workaround would be to enable: "Ignore change caused LoTW/eQSL upload or download" (which I actually need for HRDLOG), "Ignore changes QSL sent/received", and "Ignore changes caused by QSO edit". I am currently testing QLog to see how it handles QRZ. It features a beautiful interface, but it can be distracting. I still prefer CQRLOG for its powerful database and log-centric focus, which is essential to me. It looks professional, and you are improving it in an exemplary way—thank you so much for your hard work.
In conclusion, would it be possible to make all the "Ignore change" functions in CQRLOG active by default specifically for QRZ? This would settle the matter once and for all, resolving the synchronization issues completely.
73
--
Giuseppe
IKØDWJ
Hi Giuseppe!
Thanks for information about QRZ.
I have always said I need only one log, and it is Cqrlog. I do not need any Online logs but now have involved to them (for a while) because I got one year paid subscription of QRZ as support.
I am quite disappointed for information about APIs. API descriptions, effects and examples are quite limited, or I can not find/understand them all properly.
That's why making support for them is more or less shooting in dark.
Your comment about "ignore change" made me think that separating those settings individual for every log might be good idea. Thanks!
73.
--
Saku
OH1KH
Boy oh boy! Would I L-O-V-E to see this too!
73,
Mark, W1MM