RUMlogNG ADIF > SOTA対応変換ツール
前回のスクリプト作成の元データは、同じRUMlogNGなのでお題のツールもトライしました。RUMlogNGの機能には、SOTA データベースupload用csv形式に変換(下記)できます。しかし、時刻をJSTからUTCへ修正が必要です。又、相手の移動運用地がSOTAに登録された山であればJA/XX-xxxのリファレンスも変換されると思うが、RUMlogNGの使い方がわからず反映されません。
RUMlogNGでSOTA データベースupload用csv形式書出し結果
V2,JC1CCC/1,JA/TG-109,26/03/26,1630,144.205MHz,USB,JC1DEF/1,,
そこで、今年のSOTA Challenge 2026は相手局のグリットロケーターも含めて登録するので、上記課題も含めてcsv形式変換ツールをChatGPTに問合せてキャッチボール(やりとり)しながら作成してもらいました。csv形式へ変換した時の列順は、月刊FB NewsのSummits On The Air (SOTA)の楽しみ。その100 2026 SOTAチャレンジ-2を参考にしました。
機能
RUMlogNGで書き出したログadif形式からSOTA データベースupload用csv形式(Challenge 2026対応含む)へ変換する。
・RUMlogNGのログ(交信記録)
例1)相手局が固定や移動局の場合の交信ログ

例2)相手局がSOTAに登録された山へ移動した場合の交信ログ

・ADIF形式で書き出し
ADIF Export from RumLogNG by DL2RUM For further info visit: https://www.dl2rum.de <ADIF_VER:5>3.1.6 <CREATED_TIMESTAMP:15>20260406 072628 <PROGRAMID:8>RUMlogNG <PROGRAMVERSION:3>6.2 <EOH> <call:6>JC1BBB <qso_date:8>20260326 <time_on:6>101400 <qso_date_off:8>20260326 <time_off:6>101400 <band:2>2m <freq:10>144.205000 <mode:3>SSB <submode:3>USB <rst_sent:2>55 <rst_rcvd:2>55 <app_rumlog_qsl:1>- <qsl_rcvd:1>N <qsl_sent:1>I <dxcc:3>339 <cont:2>AS <name:3>かきく <qth:3>埼玉県 <comment:27>宇都宮市JCC#1501TG-109寅巳山PM96vq <gridsquare:6>PM96VQ <tx_pwr:2>10 <app_rumlog_power:2>10 <eqsl_qsl_rcvd:1>N <eqsl_qsl_sent:1>N <lotw_qsl_rcvd:1>N <lotw_qsl_sent:1>N <pfx:3>JC1 <clublog_qso_upload_status:1>M <app_rumlog_userfield_1:15>IC-705/2mHRH770 <app_rumlog_userfield_2:21>JA/TG-109寅巳山:JC1CCC/1 <app_rumlog_userfield_3:6>PM96pe <app_rumlog_colorcode:1>0 <eor> <call:8>JC1DEF/1 <qso_date:8>20260326 <time_on:6>101100 <qso_date_off:8>20260326 <time_off:6>101100 <band:2>2m <freq:10>144.205000 <mode:3>SSB <submode:3>USB <rst_sent:2>59 <rst_rcvd:2>59 <app_rumlog_qsl:1>- <qsl_rcvd:1>N <qsl_sent:1>I <dxcc:3>339 <cont:2>AS <name:3>あいう <qth:3>茨城県 <comment:27>宇都宮市JCC#1501TG-109寅巳山PM96vq <gridsquare:6>PM96VQ <tx_pwr:2>10 <app_rumlog_power:2>10 <eqsl_qsl_rcvd:1>N <eqsl_qsl_sent:1>N <lotw_qsl_rcvd:1>N <lotw_qsl_sent:1>N <pfx:3>JC1 <clublog_qso_upload_status:1>M <app_rumlog_userfield_1:15>IC-705/2mHRH770 <app_rumlog_userfield_2:21>JA/TG-109寅巳山:JC1CCC/1 <app_rumlog_userfield_3:6>QM06bg <app_rumlog_userfield_4:12>JA/IB-006足尾山 <app_rumlog_colorcode:1>0 <eor>
・ADIF形式から変換したSOTA upload用csvファイル
V2,JC1CCC/1,JA/TG-109,26/03/26,0114,144MHz,SSB,JC1BBB,,%QRA%PM96pe% V2,JC1CCC/1,JA/TG-109,26/03/26,0111,144MHz,SSB,JC1DEF/1,JA/IB-006,
(1行目タイトルは削除するスクリプトです。
V2,My call,SOTA Ref.,Date,Time(UTC),Freq,Mode,Callsign,Your SOTA Ref.,Your Locator)
スクリプト作成時の環境
・OS :macOS 15.7.3
・Hard:Mac Book Air M4 2025
・pythonがインストールしてあること
スクリプトの動作手順(CSV形式ファイル作成)
1.準備するファイル(拡張子を間違えなければファイル名は任意です。)
RUMlogNGからADIF形式で書き出したファイル名:input.adi
スクリプトのファイル名 :sota_script.py
2.ターミナルから

(例:cd Desktop はデスクトップへ移動)
スクリプトを動かすため、python3 + スペース + sota_script.pyを入力してエンターします。
上部のRUMlogNG ADIF→CSV変換ダイアログの変換開始をクリックします。
ADIFファイル選択のダイアログ表示 選択後にOpenをクリックします。



スクリプト(sota_script.py)
import tkinter as tk
from tkinter import filedialog, messagebox
import re
import csv
from datetime import datetime, timedelta
def parse_adif(file_path):
# =========================
# ① ファイル読み込み
# =========================
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
data = f.read()
# =========================
# ② BOM除去(1行目消失対策)
# =========================
data = data.lstrip("\ufeff")
# =========================
# ③ <EOH>安全除去(超重要)
# =========================
pos = data.upper().find("<EOH>")
if pos != -1:
data = data[pos + len("<EOH>"):]
# =========================
# ④ レコード分割
# =========================
records = re.split(r"<EOR>", data, flags=re.IGNORECASE)
result = []
# =========================
# ⑤ 各レコード処理
# =========================
for rec in records:
# 空行除去
if not rec.strip():
continue
fields = {}
# =========================
# ⑥ フィールド抽出
# =========================
for match in re.finditer(r"<([^:>]+):(\d+)>([^<]*)", rec, re.IGNORECASE):
key = match.group(1).upper()
value = match.group(3).strip()
fields[key] = value
# CALLが無いものは無視
if not fields or "CALL" not in fields:
continue
try:
no = "V2"
# =========================
# ⑦ 自局情報(RUMlogNG独自)
# =========================
uf2 = fields.get("APP_RUMLOG_USERFIELD_2", "")
parts = uf2.split(":")
if len(parts) >= 2:
my_sota = parts[0][:9]
my_call = parts[1]
else:
my_sota = ""
my_call = ""
# =========================
# ⑧ 日付変換
# =========================
date_raw = fields.get("QSO_DATE", "")
date_fmt = datetime.strptime(date_raw, "%Y%m%d").strftime("%d/%m/%y") if date_raw else ""
# =========================
# ⑨ 時刻変換(JST→UTC)
# =========================
time_raw = fields.get("TIME_ON", "")
time_fmt = ""
if time_raw:
if len(time_raw) == 6:
dt = datetime.strptime(time_raw, "%H%M%S")
elif len(time_raw) == 4:
dt = datetime.strptime(time_raw, "%H%M")
else:
dt = None
if dt:
dt -= timedelta(hours=9)
time_fmt = dt.strftime("%H%M")
# =========================
# ⑩ 周波数整形
# =========================
freq_raw = fields.get("FREQ", "")
freq = ""
if freq_raw:
try:
f = float(freq_raw)
freq = f"{f:.3f}MHz" if f < 30 else f"{int(f)}MHz"
except:
pass
# =========================
# ⑪ 通信情報
# =========================
mode = fields.get("MODE", "")
call = fields.get("CALL", "")
# =========================
# ⑫ 相手SOTA
# =========================
your_sota = fields.get("APP_RUMLOG_USERFIELD_4", "")[:9]
# =========================
# ⑬ グリッド処理
# =========================
grid_raw = fields.get("APP_RUMLOG_USERFIELD_3", "")
if your_sota:
your_grid = ""
else:
if grid_raw:
grid = grid_raw.strip()
if len(grid) >= 6:
grid = grid[:4] + grid[4:6].lower()
your_grid = f"%QRA%{grid}%"
else:
your_grid = ""
# =========================
# ⑭ 出力
# =========================
result.append([
no,
my_call,
my_sota,
date_fmt,
time_fmt,
freq,
mode,
call,
your_sota,
your_grid
])
except Exception as e:
print("エラー:", e)
continue
return result
def convert():
# 入力ファイル選択
input_file = filedialog.askopenfilename(
title="ADIFファイル選択",
filetypes=[("ADIF files", "*.adi")]
)
if not input_file:
return
# 出力ファイル選択
output_file = filedialog.asksaveasfilename(
title="CSV保存先",
defaultextension=".csv"
)
if not output_file:
return
data = parse_adif(input_file)
if not data:
messagebox.showwarning("警告", "変換データがありません")
return
# =========================
# ★ ヘッダー無し(元仕様)
# =========================
with open(output_file, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerows(data)
messagebox.showinfo("完了", f"{len(data)}件変換しました")
# =========================
# GUI
# =========================
root = tk.Tk()
root.title("RUMlogNG ADIF → CSV 変換")
root.geometry("420x180")
tk.Label(root, text="ADIF → CSV変換ツール(SOTA対応)").pack(pady=10)
tk.Button(root, text="変換開始", command=convert, width=25, height=2).pack(pady=10)
root.mainloop()
次回から交信記録は、これを使ってSOTAデータベースへアップロードするのが楽しみ。