「ubuntu」カテゴリーアーカイブ

csv を CQRlog に取り込み(続き)

実は 3/23 にフォーラムに投稿したのですが、ドラッグストア風に見えるフォーラムで埋もれてしまって OH1KH Saku の目に付かなかったようでした。
今朝 Saku からメールが届いて改訂を知ったのです。
それで Saku が改訂してくれまして、2024-03-26 (126) からは日本語文字のある adif からのインポートができるようになり、QTH・名前と連続しても問題なく取り込めます。

ただ <name:3> だと ーーー とエラーになるので必ず <name_intl:3> のようにしなければなりません。
以下が OK なので csv の順序も気にせずともよくなりました。

<call:6>JA4XZR <qso_date:8>19940102 <time_on:4>0357 <freq:7>18.1300 <band:3>17m <mode:3>SSB <rst_sent:2>59 <rst_rcvd:2>59 <cnty:4>3102 <gridsquare:0> <qth_intl:3>倉敷市 <name_intl:2>福岡 <app_cqrlog_qsls:1>B <app_cqrlog_qslr:1>Q <my_gridsquare:6>QN04ua <comment:0> <notes:8>JAG#0028 <award:8>DIG#5237 <eor>

adif 仕様上は _intl タグは adi ファイルには許可されず、adx ファイル(xml 形式)のみのようです。
恐らく Saku が独自タグ扱いで取り入れたものと考えます。
公式バージョンは対応していません、念の為。

さりとて何万もの QSO で日本語を英文字化するのも非現実的ですからねえ・・・

csv を CQRlog に取り込み

CQRlog を知る前は LibreOffice Calc に記録していた、
そして 30 QTH 近い移動運用も行っていた。

今 CQRlog に 40,000 を超えるログ・エントリーがあるが、SSB QSO は 4,000 余でしかない。それで Calc から csv に落として取り込もうと考えた。

win では csv も読み込める AdifMaster という優れたツールがあるが、Linux には存在しない。
と思っていたら adifmerg というツールがあるが、csv to adif を念頭に置いたものではなく、csv ヘッダーも特殊なスタイルだった。 単純に QSO を取り込むだけならソレでもいいのだが。
名前、QTH, JCC/JCG なども記録があるので・・・。

この adifmerg はアプリケーションではなく Perl スクリプトであることがわかった。 しかし Perl の素養は全くナシ。 それで AI に相談した。

AI が最初に提示したのが

#!/usr/bin/perl

use strict;
use warnings;

# CSVファイルのパスを指定
my $csv_file = 'input.csv';

# 出力するADIF形式のファイル名
my $adif_file = 'output.adif';

# CSVファイルを開く
open my $csv_fh, '<', $csv_file or die "Cannot open $csv_file: $!";

# ADIF形式のファイルを開く
open my $adif_fh, '>', $adif_file or die "Cannot open $adif_file: $!";

# ADIFヘッダーを出力
print $adif_fh "ADIF Export\n";

# CSVのヘッダーを読み込む
my $header = <$csv_fh>;
chomp $header;
my @fields = split ',', $header;

# CSVファイルからデータを読み込んでADIF形式に変換して出力
while (my $line = <$csv_fh>) {
    chomp $line;
    my @values = split ',', $line;
    print $adif_fh "<QSO_DATE:$values[1]>".$values[2]."<TIME_ON:$values[3]>".$values[4]."<CALL:$values[5]>".$values[6]."<QTH:$values[7]>".$values[8]."<GRIDSQUARE:$values[9]>".$values[10]."<DXCC:$values[11]>".$values[12]."<MODE:$values[13]>".$values[14]."<QSL_S:$values[15]>".$values[16]."<QSL_R:$values[17]>\n";
}

# ファイルを閉じる
close $csv_fh;
close $adif_fh;

print "Conversion completed.\n";

しかし CQRlog にはインポートできなかった。

  • 日本語文字が 3 byte で扱われている
  • 文字数カウントがないのも致命的
  • <eor> が抜けているのが致命的

私のログに中国局が漢字で記録されているエントリーが完全に「文字数カウント」であることがわかった。
そして csv ヘッダーは adif タグと同一でなければならず、日本語文字が入るところは連続して並べると、前者が無視されるので、最終的には影響の小さいアイテムを日本語項目の後ろに置くことにした。
則ち Calc 側で予め列の移動が必要になる。
私の場合 adif の日付や時刻の仕様を踏まえたカタチで Calc にインプットしていたので、今回新たに変換する作業が省略できた。

call,qso_date,time_on,freq,band,mode,rst_sent,rst_rcvd,cnty,gridsquare,qth_intl,\
app_cqrlog_qsls,app_cqrlog_qslr,my_gridsquare,name_intl,comment,notes,award

で app_cqrlog_qsls は「ビューロー発送済み」の B なので、group edit で B を入れた。 award は私の場合 DIG を選んでいるので、対象が少ないから手で追加しても良いとした。
comment も古い QSO には殆どないし、JAG/ACC 或いは YL は notes でよいとした。

インポートした adif のカタチ(サイレントキー):

<call:6>JA4XZR <qso_date:8>19940102 <time_on:4>0357 <freq:7>18.1300 <band:3>17m <mode:3>SSB <rst_sent:2>59 <rst_rcvd:2>59 <cnty:4>3102 <gridsquare:0> <qth_intl:3>倉敷市 <app_cqrlog_qsls:1>B <app_cqrlog_qslr:1>Q <my_gridsquare:6>QN04ua <name_intl:2>福岡 <comment:0> <notes:8>JAG#0028 <award:8>DIG#5237 <eor>

インポート後 B 挿入後の adif のカタチ:

<qso_date:8>19940102 <time_on:4>0357 <call:6>JA4XZR<mode:3>SSB <freq:5>18.13 <band:3>17m <rst_sent:2>59 <rst_rcvd:2>59 <name_intl:2>福岡 <qth_intl:3>倉敷市 <qsl_rcvd:1>Y <iota:6>AS-007 <award:8>DIG#5237 <app_cqrlog_dxcc:2>JA <dxcc:3>339 <notes:8>JAG#0028 <ituz:2>45 <cqz:2>25 <cnty:4>3102 <app_cqrlog_qslr:1>Q <eor>

適用した pl ファイル(csv と adif ファイル名指定して処理)

#!/usr/bin/perl

use strict;
use warnings;

# 入力ファイルのパスをユーザーに尋ねる
print "Enter input CSV file path: ";
my $csv_file = <STDIN>;
chomp $csv_file;

# 出力ファイルのパスをユーザーに尋ねる
print "Enter output ADIF file path: ";
my $adif_file = <STDIN>;
chomp $adif_file;

# CSVファイルを開く
open my $csv_fh, '<:encoding(utf8)', $csv_file or die "Cannot open $csv_file: $!";

# ADIF形式のファイルを開く
open my $adif_fh, '>:encoding(utf8)', $adif_file or die "Cannot open $adif_file: $!";

# ADIFヘッダーを出力
print $adif_fh "ADIF Export\n";

# CSVのヘッダーを読み込む
my $header = <$csv_fh>;
chomp $header;
my @fields = split ',', $header;

# CSVファイルからデータを読み込んでADIF形式に変換して出力
while (my $line = <$csv_fh>) {
    chomp $line;
    my @values = split ',', $line;
    foreach my $field (@fields) {
        my $value = shift @values // ''; # 未初期化の値を防ぐためにデフォルト値を設定
        my $tag = lc $field; # タグを小文字に
        my $length = length($value); # フィールドの文字数を取得
        # 2 バイト文字を 2 文字としてカウント
        my $byte_length = (() = $value =~ /./g); # 文字列内の文字数をカウント
        print $adif_fh "<$tag:$byte_length>$value ";
    }
    print $adif_fh "<eor>\n"; # ADIFレコードの終わりを追加
}

# ファイルを閉じる
close $csv_fh;
close $adif_fh;

print "Conversion completed.\n";

pl 文のコメントにも「# 2 バイト文字を 2 文字としてカウント」、これは「# 2 バイト文字を 1 文字としてカウント」の誤りでしょうね。

それにしてもこういうとき、AI に完全丸投げでも、パーフェクトな回答を得られるものではないんですね。 <eor> が抜けるなんて人間並じゃありませんか。 尤も有料サービスならこんなこともないんでしょうが。

所謂 2 バイト文字は 3 バイトに換算されるようで、コンパイルツールである lazarus や fpc でも日本語文字の扱いがまだパーフェクトな段階に至ってないらしい。 となれば、CQRlog が影響を受けるのは致し方ないのかも知れない。
その辺をキラって JA のエントリーも英文字で行うというのも頷ける。

AI って何様?

私は一往メール添付 QSL も受け入れている。
GIMP 拡げてデータ・インプットすればソレで済む話だが、
ここんとこちょっと溜めてしまった。

そこで「コマンドで画像に文字入れ」で検索すると、
ImageMagick の convert でできるという。

convert QSLImage.jpg -family 'Monaco-Bold' -style Italic -pointsize 36 -fill Black -annotate +400+497 'CALLSIGN' -style Any -family Monaco -pointsize 24 -fill Black -annotate +140+525 'DATE' -style Any -family Monaco -pointsize 24 -fill Black -annotate +470+525 'TIME' -style Any -family Monaco -pointsize 24 -fill Black -annotate +750+525 'MODE' -style Any -family Monaco -pointsize 24 -fill Black -annotate +140+558 'FREQ' -style Any -family Monaco -pointsize 24 -fill Black -annotate +485+558 'RST' -style Any -family Monaco -pointsize 24 -fill Blue -annotate +45+600 'Rmks' output.jpg

ってな感じだ。

しかし溜めてしまったことで「スクリプトでできないか」と考えた。
Python もこないだやったので、どのように使うかだけ理解した。
ChatGPT に相談した。

一発目の回答、何も表示されない。
何度かのやり取りで AI が「勝手に」出力項目数や出力位置を変更して、トンデモナイ位置に出力しているのに気づいた。人間相手ならケンカ売ってたろう・・・
七箇所もあるのに二個しか出力されていない。こっちは七つもインプットしてるのに。

elif で追加すればいいことを確認して、終了した。勉強にはなったが。

import subprocess

def main():
    items = {}
    item_order = ['call', 'date', 'time', 'mode', 'freq', 'rst', 'rmks']
    for item_name in item_order:
        item_text = input("Enter {}: ".format(item_name))
        items[item_name] = item_text

    output_file = input("Enter the output file name: ")

    cmd = ['convert', 'QSLImage.jpg']
    for item_name, item_text in items.items():
        if item_name == 'call':
            cmd.extend(['-family', 'Monaco-Bold', '-style', 'Italic', '-pointsize', '28', '-weight', 'Bold', '-fill', 'Black', '-annotate',
                        '+400+497', item_text])
        elif item_name == 'date':
            cmd.extend(['-family', 'Monaco', '-weight', 'Normal', '-pointsize', '24', '-fill', 'Black', '-annotate',
                        '+140+525', item_text])
        elif item_name == 'time':
            cmd.extend(['-family', 'Monaco', '-weight', 'Normal', '-pointsize', '24', '-fill', 'Black', '-annotate',
                        '+470+525', item_text])
        elif item_name == 'mode':
            cmd.extend(['-family', 'Monaco', '-weight', 'Normal', '-pointsize', '24', '-fill', 'Black', '-annotate',
 	 		'+750+525',item_text])
        elif item_name == 'freq':
            cmd.extend(['-family', 'Monaco', '-weight', 'Normal', '-pointsize', '24', '-fill', 'Black', '-annotate',
 	 		'+140+558',item_text])
        elif item_name == 'rst':
            cmd.extend(['-family', 'Monaco', '-weight', 'Normal', '-pointsize', '24', '-fill', 'Black', '-annotate',
 	 		'+485+558',item_text]) 	 		                                               
        else:
            cmd.extend(['-family', 'Monaco', '-weight', 'Normal', '-pointsize', '24', '-fill', 'Blue', '-annotate',
                        '+45+600', item_text])

    cmd.append(output_file)

    try:
        subprocess.run(cmd, check=True)
        print("Conversion completed successfully. Output file:", output_file)
    except subprocess.CalledProcessError as e:
        print("Error occurred while running convert command:")
        print(e)


if __name__ == "__main__":
    main()