JTDXのログファイルで交信局数を集計 (2024/12/05) 
JTDXとwsjtxが吐き出すログファイルから時間帯別国名を集計するプログラムを作ってみました
全然役に立つソフトではないですがPython(パイソン)のお勉強です
JTDX_time_Ver1.0.1.zipのダウンロードはこちら

ダウンロード回数: 5
プログラムはPython(パイソン)というプログラミング言語で書いています。
動作確認をしたのはWindows11のパソコンですがpython-3.13.0-amd64をダウンロードしてインストールしておく必要があります。

Python(パイソン)のインストール後に外部ライブラーのインストールが必要です
  1. pytzのみインストールする必要があります。
  2. pip install pytzコマンドでインストールできます。

これで、このプログラムを実行するために必要なすべてのパッケージが揃います。

インストールとディレクトリの準備
JTDX_time_Ver1.0.0.zipをダウンロード後に解凍してください

プログラムはデフォルトで 
 C:\Users\kueno\AppData\Local\JTDX

 C:¥Logs
になっています。赤字のところを環境合わせて書き換えてください
実行にはrunjtdx_time.batを起動してください。
JTDX_time_Ver1.0.0.pyの実行画面

時間帯ごとに何処の国と交信したか傾向が分かるようになっています。

JTDX_time_Ver1.0.0.pyを起動するrunjtdx_time.batファイル
PATH関係はそれぞれの環境に合わせて書き換えてください


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
# バージョン番号
# JTDX_time_Ver1.0.0.py
# このプログラムはADIFファイルから時間帯別交信国を表示させるものです
# adiファイルは引数として渡せます
# adiファイルを入力も出来ます。その時LOGS_DIRで指定したPATHから読み込まれます
# またはフルパスで入力することもできます
# timezoneを指定して表示することができます。処理はすべてUTCです。
# bandを入力しなければすべてのバンドを検索します
# 15mまたは21MHzのどちらでも入力できます。
# 検索したい年月日を入力します。指定しなければadiファイルのすべてから集計します
# 20240601または202406 のように入力できます
#
# 必要なモジュールのインポート
import os
import sys
from datetime import datetime, timedelta
import pytz

# 定数と設定
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"]

# MHz入力用のバンドリスト
ALLOWED_MHz = [
"1.8MHz", "1.9MHz", "3.5MHz", "3.7MHz", "5MHz", "7MHz", "10MHz", "14MHz", "18MHz", "21MHz", "24MHz", "28MHz", "40MHz", "50MHz"
]

# バンドのMHz入力からm形式に変換する辞書
BAND_MHZ_TO_M = {
"1.8MHz": "160m",
"1.9MHz": "160m",
"3.5MHz": "80m",
"3.7MHz": "80m",
"5MHz": "60m",
"7MHz": "40m",
"10MHz": "30m",
"14MHz": "20m",
"18MHz": "17m",
"21MHz": "15m",
"24MHz": "12m",
"28MHz": "10m",
"40MHz": "8m",
"50MHz": "6m",
}

# バンド名を 'm' 形式に変換する関数
def convert_band_to_m(band):
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

# タイムゾーンの選択を行う関数
def select_timezone():
while True:
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...\n")
sys.exit(0) # プログラムを正常終了

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>":
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:
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 # デバッグフラグを追加
file_path = get_file_path()
if not file_path:
return

timezone, tz_label = select_timezone()

while True:
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
print("Invalid band. Please choose from:\n"
f"{', '.join(ALLOWED_BANDS)}\n"
f"{', '.join(ALLOWED_MHz)}")

target_month = input("Enter the search month\n(YYYY, YYYYMMDD, YYYY/MM, YYY/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()

 ちなみにこれもChatGPTが沢山手助けしてくれたことは内緒
でも2日かかりました。
ChatGPTに仕様を伝えるとプログラムを書いてくれるのですがいったん間違った方向へ向かうと
なかなか治りません。何度も間違いを指摘しても解決しないのです。そうすると世代前のソースを示して
この時は動いたから間違い個所を見つけて何という事が度々あります。
結局1日目は夜中3時までChatGPTにつきあいました。
 例えばこれ
print("Invalid band. Please choose from:\n"
f"{', '.join(ALLOWED_BANDS)}\n"
f"{', ".join(ALLOWED_MHz)}")

3行目のダブルクォーテーションはシングルクォーテーション
である必要が有るのに最後まで間違っています。
2024年12月02日  2024年12月11日
フレーム表示に戻す