. JI1FGX/DU9 CVS conversion software for DXCC listings (December 11, 2024)
JI1FGX/DU9 Mindanao, Philippines Amateur Radio Diary IOTA OC-130
English Japanese



Access until yesterday
Introduction. 【【Lily Diary]
Diary of Life in Mindanao

24/12/11 CVS conversion software for DXCC listings
24/12/05 Aggregation by country from JTDX
24/12/02 ADIF for one month from JTDX
Programs that create files
24/11/24 CwGet and CwType settings
24/11/24 WWDX Contest
24/11/21 CQ Ham Radio
24/11/20 Call Sign Assignment IOTA Number
24/11/13 IOTA 100 Awards Arrival
24/11/12 LoTW WAZ and DXCC
24/11/04 PLDT Optical Internet
24/10/30 Pile-up of 50MHz FT8
24/10/28 Camiguin Is Crowdfunding
24/10/26 50MHz Yagi feeder
24/10/21 RTTY with MMTTY
24/10/19 Firmware for UV-5K
24/10/17 Antenna group seen by drone
24/10/16 7MHz Antenna Pole
24/10/15 3.5MHz zepp transceiver antenna
24/10/14 Hexbeam Purchase Plan
24/10/13 24MHz dipole addition
24/10/13 50MHz Yagi repair completed
50MHz FT8 first pile
24/10/12 Antenna production for 24 MHz
24/10/09 Trouble with Gmail
24/10/09 The Shack
24/10/07 JO1CRA/7 AS-206
24/10/05 3D2V Rotuma Island IOC-060
24/10/01 Camiguin Island Trailer Video
24/09/30 Uruguay
24/09/29 50MHz antenna installation
24/09/25 Pile up on the caller's side
24/09/19 IOTA 100 Award
24/09/18 IOTA Application
24/09/12 DXCC Application
24/09/11 FT8 in communication with KH8T
24/09/08 50MHz Yagi Antenna
24/08/28 Pile up at FT8
24/08/07 Pile up from FT8EU
24/07/19 JW/WE9G in Svalbard
24/06/27 Purchase of air conditioner for wireless room
24/05/28 antenna switch
24/05/27 MFJ-259B Repair
24/05/10 Completed 3-band antenna
24/05/03 7MHz Dipole
24/04/30 7MHz Domestic
24/04/29 Reopening Project
24/04/04 Repair of FTDX3000
24/03/21 To NTC Cagayan
.
>
Number of communicating stations in JTDX log files (2024/12/05) 
I made a program to tabulate country names by time zone from log files spit out by JTDX and wsjtx
This is not a useful software at all, but it is a study of Python (Python).
Click here to download JTDX_time_Ver1.0.1.zip

Download count: 1 5_
The program isPython(Python) programming language.
I've tested the operation on a Windows 11 computer.python-3.13.0-amd64must be downloaded and installed.

External libraries must be installed after Python (Python) installation
  1. pytzOnly the "*" must be installed.
  2. pip install pytzcommand.

You now have all the packages you need to run this program.

Installation and Directory Preparation
Please unzip the file JTDX_time_Ver1.0.0.zip after downloading

The program defaults to
C:\Users\kueno\AppDataLocal\JTDX

 C:¥Logs
The red text should be rewritten to match your environment. Please rewrite the red text to match your environment.
To executerunjtdx_time.batPlease activate the
Execution screen of JTDX_time_Ver1.0.0.py

It is possible to see the trend of which countries were communicated with at different times of the day.

Start JTDX_time_Ver1.0.0.pyrunjtdx_time.batfile
Please rewrite the PATH relation according to your environment.


REM This is a batch file to run the JTDX_time_Ver1.0.01.py script
REM Change the Python executable path if necessary

@echo off
REM Set the path to Python executable
set PYTHON_EXECUTABLE=python

REM Set the path to the JTDX script
set SCRIPT_PATH=C:\Users\kueno\OneDrive\Desktop\JTDXLOG\JTDX_time_Ver1.0.0.py

REM Execute the script
%PYTHON_EXECUTABLE% %SCRIPT_PATH%.
pause
# Version No.
# JTDX_time_Ver1.0.0.py
# This program displays the countries of contact by time zone from the ADIF file
# adi files can be passed as arguments
# You can also enter an adi file. The file will be read from the PATH specified in LOGS_DIR.
# or you can enter the full path
# timezone. All processes are in UTC.
# Search all bands if you don't enter a band
# Input can be either 15m or 21MHz.
# Enter the date you want to search. If not specified, it will aggregate from all of the adi files
# You can enter something like 20240601 or 202406
#
# Import required modules
import os
import sys
from datetime import datetime, timedelta
import pytz

# Constants and settings
DEFAULT_ADIF_FILE= 'C:\\Users\\kueno\\AppData\\Local\\\JTDX\\\wsjtx_log.adi'
DEFAULT_TEXT_FILE= 'C:\Logs\filtered_log.txt'
LOGS_DIR= "C:\\Logs"
ALLOWED_BANDS = ["160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "8m", "6m"]

# Bandwrist for MHz input
ALLOWED_MHz = [
"1.8MHz", "1.9MHz", "3.5MHz", "3.7MHz", "5MHz", "7MHz", "10MHz", "14MHz", "18MHz", "21MHz", "24MHz", "28MHz", "40MHz", "50MHz"
]

# Dictionary to convert from band MHz input to m format
BAND_MHZ_TO_M = {
"1.8MHz": "160m", "1.8MHz".
"1.9MHz": "160m", "1.9MHz".
"3.5MHz": "80m", "80m".
"3.7MHz": "80m", "80m".
"5MHz": "60m", "5MHz".

"10MHz": "30m", "10MHz".
"14MHz": "20m", "20m".
"18MHz": "17m", "17m".

"24MHz": "12m", "12m".
"28MHz": "10m", "10m".
"40MHz": "8m", "8m".
"50MHz": "6m", "6m".
}

# Function to convert band names to 'm' format
def convert_band_to_m(band):.
if band.endswith("MHz"): if band.endswith("MHz")
return BAND_MHZ_TO_M.get(band, None)
elif band in ALLOWED_BANDS:.
return band
else:.
print("Invalid band format.")
return None

def get_file_path():.
if len(sys.argv) > 1:.
return os.path.join(LOGS_DIR, sys.argv[1])
else:.
file_name = input(f "Specify the ADIF log file path (default in {DEFAULT_ADIF_FILE}): ").strip()
file_path = file_name if file_name else DEFAULT_ADIF_FILE
if os.path.exists(file_path):.
return file_path
else:.
print(f "File not found: {file_path}")
return None

# Function to select time zone
def select_timezone():.
while True: The
try:.
tz_input = input("Select timezone (Enter 'UTC', 'JST', 'PHT' or a custom UTC offset (e.g., UTC+8)): ").strip()

if not tz_input:.
tz_input = "UTC"

if tz_input.upper() == "JST":.
return pytz.timezone("Asia/Tokyo"), "JST".
elif tz_input.upper() == "PHT":.
return pytz.timezone("Asia/Manila"), "PHT"
elif tz_input.upper() == "UTC":.
return pytz.UTC, "UTC"
elif tz_input.upper().startswith("UTC"):.
try:.
offset = int(tz_input[3:])
return pytz.FixedOffset(offset * 60), f "UTC{tz_input[3:]}"
except ValueError:.
print("Invalid UTC offset. Please enter a valid UTC offset like 'UTC+8'.")
else:.
print("Invalid input. Please enter one of the following: 'UTC', 'JST', 'PHT' or a valid UTC offset (e.g., 'UTC+8').")
except KeyboardInterrupt:.
print("\nProgram interrupted. Exiting... Exiting... \n")
sys.exit(0) # Exit program normally

def parse_adif(lines, target_band, target_month, target_date, timezone, tz_label, DEBUG_FLAG=False):.
results = {}
current_record = ""

if DEBUG_FLAG:.
print("Process started.")

for line in lines:.
current_record += line.strip()
if line.strip() == "<EOR>": if line.strip() == "<EOR>": if line.strip() == "<EOR>
qso_date = extract_field(current_record, "QSO_DATE")
time_on = extract_field(current_record, "TIME_ON")
band = extract_field(current_record, "BAND")
qth = extract_field(current_record, "QTH") or "Unknown"

if not qso_date or not time_on or not band:
current_record = ""
continue

if target_date and qso_date ! = target_date: !
current_record = ""
continue

if target_month and not qso_date.startswith(target_month):.
current_record = ""
continue

normalized_band = convert_band_to_m(band)
if target_band and normalized_band ! = target_band: !
current_record = ""
continue

if len(time_on) == 4: if len(time_on)
time_on += "00"

try:.
utc_time_str = qso_date + time_on + " UTC"
utc_time = datetime.strptime(utc_time_str, "%Y%m%d%H%M%S UTC")
utc_time = pytz.UTC.localize(utc_time)
local_time = utc_time.astimezone(timezone)

start_hour = local_time.replace(minute=0, second=0, microsecond=0)
end_hour = start_hour + timedelta(hours=1)

utc_start = start_hour.astimezone(pytz.UTC).strftime("%H:00")
utc_end = end_hour.astimezone(pytz.UTC).strftime("%H:00")

local_start = start_hour.strftime("%H:00")
local_end = end_hour.strftime("%H:00")

if timezone == pytz.UTC:.
hour_range = f "UTC {utc_start}-{utc_end}"
else:.
hour_range = f "UTC {utc_start}-{utc_end} {tz_label} {local_start}-{local_end}"

if hour_range not in results:
results[hour_range] = {}

if qth not in results[hour_range]:
results[hour_range][qth] = 0

results[hour_range][qth] += 1

except ValueError as e:.
print(f "Error processing record: {e}")
current_record = ""
continue

current_record = ""

if DEBUG_FLAG:.
print(f "Results: {results}")

return results

def extract_field(record, field):.
start = record.find(f"<{field}:")
if start == -1:.
return None
start = record.find(">", start) + 1
end = record.find("<", start)
return record[start:end].

def display_results(results, target_band):.
if not results:.
print("No data found matching the criteria.")
return

total_contacts = 0
print(f"\nHourly QTH Counts for Band: {target_band}")
for hour_range, qths in results.items():.
total_hour_contacts = sum(qths.values())
total_contacts += total_hour_contacts
print(f"{hour_range}:")
print(f" Total contacts: {total_hour_contacts}")
for qth, count in sorted(qths.items(), key=lambda x: x[1], reverse=True):.
print(f" {count} {qth}")

print(f"\nTotal contacts for all hours: {total_contacts}")

def main():.
try:.
DEBUG_FLAG = False # Add debug flag
file_path = get_file_path()
if not file_path:.
return

timezone, tz_label = select_timezone()

while True: The
target_band = input("Enter the target band (e.g., 15m, 21MHz, or press Enter to skip): ").strip()
if not target_band or target_band in ALLOWED_BANDS or target_band in ALLOWED_MHz:.
target_band = convert_band_to_m(target_band) if target_band else None
break (e.g. rip)
print("Invalid band. Please choose from:\n")
f"{', '.join(ALLOWED_BANDS)}\n"
f"{', '.join(ALLOWED_MHz)}")

target_month = input("Enter the search month\n(YYYYY, YYYYYMMDD, YYYYY/MM, YYYY/MM/DD, etc) or press Enter to search all months: ").strip()

if target_month:.
if '/' in target_month:.
target_month = target_month.replace('/', '')
elif len(target_month) == 6:.
target_month = target_month

target_date = input("Enter the target date (YYYYMMDD) or press Enter to skip: ").strip()

# Read lines from the ADIF file
with open(file_path, 'r') as file:.
lines = file.readlines()

results = parse_adif(lines, target_band, target_month, target_date, timezone, tz_label, DEBUG_FLAG)
display_results(results, target_band)

save_results = input("Would you like to save the results to a TEXT file? (y/n): ").strip().lower()
if save_results == 'y':.
result_file = input(f "Enter the file name (default: {DEFAULT_TEXT_FILE}): ").strip() or DEFAULT_TEXT_FILE
with open(result_file, 'w') as f:.
f.write(f "Hourly QTH Counts for Band: {target_band}\n")
for hour_range, qths in results.items():.
f.write(f"{hour_range}:\n")
for qth, count in qths.items():.
f.write(f" {count} {qth}\n")
print(f "Results saved to {result_file}")
except KeyboardInterrupt:.
print("\nProgram interrupted by user. Exiting...")
except Exception as e:.
print(f "Error: {e}")

if __name__ == "__main__":.
main()

 By the way, don't tell anyone that ChatGPT helped me a lot with this too!
But it took two days.
When I tell ChatGPT my specifications, they write a program for me, but once I start going in the wrong direction, I'm not sure I'm going to get it right.
It does not heal easily. I have pointed out the mistake many times, but it is not resolved. Then you have to show them the source before the generation.
Often, we find a mistake because we have moved at this time.
I ended up staying with ChatGPT until 3am on the first day.
For example, this one
print("Invalid band. Please choose from:\n")
f"{', '.join(ALLOWED_BANDS)}\n"
f"{',''".join(ALLOWED_MHz)}")

Double quotation marks in the third line are single quotation marks
The last one is wrong, even though it needs to be.
December 02, 2024   December 11, 2024