QRcode

Table of Contents

眼から鱗の圧縮

前回FT8を調べていた時、大事な機能を見落としていた。通信データの圧縮技 術だ。当然、伸長とセットになるけど。

FT8のデータ圧縮(データエンコード)

普通のASCIIコードだと1文字が8ビットになるけど、それじゃアマチュア無線 の判で押したような通信(ラバースタンプQSO)では無駄が多過ぎる。それで無駄を排除しましょって 訳。電文が短かければ効率的に通信できるし、エラー耐性が向上する。

さようなら、goodbye なんてのを電信で叩いていたんじゃ超無駄。決まりきっ た電文は番号にしちゃってのの名残が77って数字。ここまでくると隠語ってか 一種の暗号になっちまう。QRA あなたのお名前何ですか なんてのは頭にQがつ くので、Q符号と言う一種の短縮系の隠語だ。

きっと、電電公社時代の電信も、同様だったのだろう。カネオクレとか、チチ キトク、スグカエレ とかは、カタログ化されて、番号で表現されてたに違い ない。

電信と言えばモールス符号。可変長のコードによる究極のデジタル通信だ。英 文で良く出現する文字、e が12.7%の頻度。qとかzは0.1%程度。だから、e に 短いコードを割り振れば合理的。良く考えてある。

ああ、横道にそれた。圧縮と伸長をオイラーも、やってみるか。

仕様決め

コールサインだけをエンコード(圧縮)/デコード(伸長)する事にする。 FT8に習って、37✕36✕10✕27✕27✕27 の6文字が対象。 プログラミング言語は、最近やり始めたemacs lispとしましょう。

ASCII文字と数字、それとブランクの表現(本来ならスペースだけど、_ にしと く)を、固有の数値に割り当て。どう割り当ててもよい。そう、昔の大型コン ピュータでは、EBCDCICという変なわりあてが使われていた。ddコマンドにそ の片鱗が残っている。これは、昔のタイプライターのキー配置をそのまま、採 用した為らしい。

キーボードの配置は、合理的になっていない。幾つか提案が有ったみたいだけ ど、昔のよしみで、不合理な配列が健在である。また、脱線したな。

今ならASCIIだな。数字を先にする? ブランクはどうする? 悩み所だ。 えいやっと

_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

と言う並びにする。このインデックス番号を、スペシャル文字コードとしよう。 並びに意味はないので、アルファベット・グループ内及び、数字グループ内で、 配置を入れ替えれば、換字暗号っぽくなるぞ。

toolbox

elispなんで癖強だ。インデックス番号(私製のキャラクターコード)を与えて文字を引き出す方法

ELISP> (elt "abcdefg" 0)
97 (#o141, #x61, ?a)
ELISP> (elt "abcdefg" 5)
102 (#o146, #x66, ?f)
ELISP> (elt "abcdefg" 10)
 *** Eval error ***  Args out of range: "abcdefg", 10

次は、逆に、文字を与えて、インデックス番号を引き出す方法。

ELISP> (with-temp-buffer
         (erase-buffer)
         (insert "abcdefg")
         (beginning-of-buffer)
         (search-forward "b"))
3 (#o3, #x3, ?\C-c)
ELISP> (with-temp-buffer
         (erase-buffer)
         (insert "abcdefg")
         (beginning-of-buffer)
         (search-forward "x"))
 *** Eval error ***  Search failed: "x"

goshだと綺麗なんだけどね。emacsはあくまでeditorなんで、ターゲットはバッ ファー相手にならざるを得ない。search-forardを探ったら秘密の関数が見付 かるかと思ったけど、主流は正規表現関係ばかりだった。諦めろ。

gosh$ (string-scan "abcdefg" "c")
2

でも良く使う演算だよな。それが無いっておかしい。駄目元と思って、正規表現を試したら、これ使えるじゃん。何事も果敢に挑戦 する事だな。

ELISP> (string-match "c" "abcdefg")
2 (#o2, #x2, ?\C-b)
ELISP> (string-match "x" "abcdefg")
nil

Emacs Lisp の文字列操作まとめ こういう便利なまとめは、ありがたく利用さ せて頂くこと。

ELISP> (apply 'string (reverse (append "abcdef" nil)))
"fedcba"

早速、役立ちそう。ああ、concat は、自由度が高いから、こういう技は不要 かな。まあ、引き出しが増えるって事は好だな。

code

要素技術の予習が出来たので、コードを作成。

;; encode/decode call sign

(defconst tbl "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
(defconst lmt '((0 . 36) (1 . 36) (27 . 36) (0 . 26) (0 . 26) (0 . 26)))

(defun dgt (i)
  (let ((bw (elt lmt i)))
    (1+ (- (cdr bw) (car bw)))))

(defun encode (cs) ;; call sign -> number
  (let ((rv 0)
        (c 0))
    (dotimes (i (length cs))
      (setq c (string-match (char-to-string (elt cs i)) tbl))
      (if (= i 2)
          (setq c (- c 27)) c)
      (if (= i 0)
          (setq rv c)
        (setq rv (+ (* rv (dgt i)) c))))
    rv))

(defun decode (num) ;; number -> call sign
  (let ((rv "")
        (w 0)
        (i 5))
    (while (<= 0 i)
      (setq w (dgt i))
      (if (= i 2)
          (setq rv (concat (char-to-string (elt tbl (+ (% num w) 27))) rv))
        (setq rv (concat (char-to-string (elt tbl (% num w))) rv)))
      (setq num (/ num w))
      (setq i (- i 1)))
    rv))

本当は、コールサインの妥当性を調べる必要が有る。そんなの正規表現で出来 るよ。でも、emacsのそれは、特殊だからなあ。そもそもhaskellに触れてから は、すっかり正規表現が嫌いになった。正規表現が許されるのは、せいぜい sed か awk ぐらいまででしょう。

そんな事で、is-number とか is-alpha ぐらいは出来るように、lmt と言うのを用意 してる。それぞれの桁で許されるコード範囲を保持させた。これを元にチェッ ク関数を作成すればいいんだけど、ものぐさなんでサボってしまった。

その変わり、dgtという関数で、これを利用させてもらった。桁の重みを算出 してる。

check

ELISP> (encode "999ZZZ")
262374389 (#o1750701765, #xfa383f5)
ELISP> (log 262374389 2)
27.967051660968128
ELISP> (log (encode "JA1FG_") 2)
26.082937828775353

まずは、一番の目的である、圧縮が出来ているかだ。私製コードで一番最大に なるコールサインもどきを計算。それを、2進数で表現するには何ビット必要 か確認。確かに28Bitあれば十分となった。

JA1FGさんは、オイラーが昔お世話になった方。送信機の設計と制作なんて本 を、穴のあく程読み込んで、TVの水平偏向管を終段にして、送信機を作ったよ。 勿論、その球は、ガベコレしてきたものだけどね。

ELISP> (decode (encode "JG9QRZ"))
"JG9QRZ"
ELISP> (decode (encode "JA1FG_"))
"JA1FG_"
ELISP> (decode (encode "_K1JT_"))
"_K1JT_"

次は、往復ビンタの試験。エンコードで数値に変換して、それをデコードすれ ば元に戻るはず。一応、大丈夫そう。

反省会

上記のコードを眺めていて、失敗したなってのが出てきた。それは、 encode/decodeの両ルーチン共、(if (= i 2) …) が有る事。コールサインの 3文字目の数字だけ、別扱いせざるを得なかったので(オフセット数の27を足し たり、引いたりする操作)こうした。

端的に言えば、エィヤァと決めたtblの並びが悪い。

(defconst tbl "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_")

とでも、すべきである。こうしておけば、オフセットが0になるとの、加減が 不要、すなわち特別扱いが不要になる。待て待て、これじゃ今度はアルファベッ ト文字がオフセットを持ってしまうぞ。

(defconst tbl "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")

頭の中にカルノー図を描いてみて、これが一番無難そう。

データ構造によって、プログラミング・コードが影響を受けるって言う悪い例 の見本になっちまったな。反省しきりである。

と、計画、実装、チェック、アクションと、PDCAのループを一周しました。 よもや、現役を離れて、PDCAを回すなんて、思いもしなかったよ。

リード・ソロモン符号

間違った電文は自動訂正して欲しいという要求から、色々な方法が提案されて る。そのうちの一種にリード・ソロモン符号ってのが有るそうな。

リード・ソロモン符号(Reed-Solomon code)は、誤り訂正符号の一種であり、データ転送やデータ保存の際に誤りが生じた場合に、それを検出し訂正するために使用される数学的な手法です。リード・ソロモン符号は非常に強力で広く使用されており、例えばディスクドライブ、QRコード、バーコード、無線通信、デジタルテレビ、CD、DVD、ブルーレイディスク、デジタル通信など、さまざまな応用分野で利用されています。

リード・ソロモン符号の主要な特徴と関連するガロア理論について説明します。

  1. ガロア理論への基盤: リード・ソロモン符号は、ガロア体(Galois field)と呼ばれる数学的な概念に基づいています。ガロア体は、有限個の元(要素)から構成される代数的な構造で、通常はGF(q)と表されます。ここで、qは素数またはその冪であり、符号の強度や性能はqの値に依存します。リード・ソロモン符号は、この有限体上で定義され、ガロア理論のアイデアを活用して誤り訂正を行います。
  2. シンボルと多項式: リード・ソロモン符号では、データをシンボル(要素)の集合として扱います。これらのシンボルは、通常、ビットやバイトの列として表現されます。また、符号化されたデータは多項式として表現され、これらの多項式操作を通じてエラー検出と訂正が行われます。
  3. 誤り訂正の原理: リード・ソロモン符号では、元のデータに冗長性を追加し、誤りが生じた場合にその冗長性を活用してエラーを検出し、必要であれば訂正します。ガロア体上で定義された多項式の演算を使用して、誤り位置の特定と訂正が行われます。
  4. 符号の性能: リード・ソロモン符号の性能は、qの値やエラー訂正能力に依存します。一般的に、高次のガロア体(大きなqの値)を使用するほど、符号の強度が向上します。しかし、高次のガロア体を使用すると、計算が複雑になるため、バランスを取る必要があります。

リード・ソロモン符号は、データ通信やデータ保存の分野で幅広く応用され、信頼性の向上と誤り訂正能力の提供に貢献しています。ガロア理論の数学的な概念を活用して、効果的な誤り訂正符号を設計できることが、リード・ソロモン符号の魅力的な特徴の一つです。

ハードディスクは壊れる物と割り切って、余分なDISKを追加した構成もあるな。 いわゆるRAIDって奴だ。これなんかも一種の謝り訂正だな。

QRcode

んな事で、身近にあるQRコードを調べてみる。数学が出てくるなら、そりゃ haskellでしょ。

Haskellでテキスト "hello QR code" をQRコードに変換するには、まずQRコードを生成するための外部ライブラリを使用する必要があります。一般的には、`qrcode` ライブラリのようなライブラリをHaskellで直接使用することは難しいです。しかし、HaskellからPythonを呼び出してQRコードを生成する方法があります。

以下は、HaskellからPythonを呼び出してQRコードを生成するサンプルコードです。この方法では、HaskellでPythonスクリプトを実行し、PythonでQRコードを生成します。

  1. まず、PythonのQRコード生成スクリプトを作成します。たとえば、 generate_qr_code.py という名前のファイルを作成します。
# generate_qr_code.py

import qrcode

text = "hello QR code"

qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=10,
    border=4,
)
qr.add_data(text)
qr.make(fit=True)

img = qr.make_image(fill_color="black", back_color="white")
img.save("hello_qr_code.png")
  1. 次に、Haskellのプログラムを作成し、Pythonスクリプトを実行します。HaskellからPythonスクリプトを呼び出すために、`System.Process` モジュールを使用します。以下はHaskellプログラムのサンプルです。
-- QRCodeGenerator.hs

import System.Process

main :: IO ()
main = do
    -- Pythonスクリプトを実行してQRコードを生成します
    callCommand "python generate_qr_code.py"

    -- 生成されたQRコードを表示することもできます
    -- callCommand "xdg-open hello_qr_code.png" -- Linuxの場合
    -- callCommand "open hello_qr_code.png"      -- macOSの場合
    -- callCommand "start hello_qr_code.png"     -- Windowsの場合

上記のHaskellプログラムは、Pythonスクリプト generate_qr_code.py を 呼び出してQRコードを生成し、生成されたQRコードを表示する方法を示してい ます。QRコードは hello_qr_code.png という名前のファイルに保存されます。QRコードの表示方法は、Linux、macOS、Windowsに応じてコメントアウトされたコマンドの中から選択できます。

この方法を使用すると、HaskellでQRコードを生成できますが、QRコード生成にはPythonを使用する必要があります。

for python

そんな訳で舞台は反転して、pythonです。本家はこちら、 qrcode by pure python

(mypy) [sakae@deb ~]$ qr --help
Usage: qr - Convert stdin (or the first argument) to a QR Code.

When stdout is a tty the QR Code is printed to the terminal and when stdout is
a pipe to a file an image is written. The default image format is PNG.

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  --factory=FACTORY     Full python path to the image factory class to create
                        the image with. You can use the following shortcuts to
                        the built-in image factory classes: pil, png, svg,
                        svg-fragment, svg-path or pymaging.
  --factory-drawer=FACTORY_DRAWER
                        Use an alternate drawer. For svg-path and svg, use:
                        circle, gapped-circle or gapped-square.
  --optimize=OPTIMIZE   Optimize the data by looking for chunks of at least
                        this many characters that could use a more efficient
                        encoding method. Use 0 to turn off chunk optimization.
  --error-correction=ERROR_CORRECTION
                        The error correction level to use. Choices are L (7%),
                        M (15%, default), Q (25%), and H (30%).
  --ascii               Print as ascii even if stdout is piped.
  --output=OUTPUT       The output file. If not specified, the image is sent
                        to the standard output.

実行例

(mypy) [sakae@deb ~]$ qr --ascii "hello QRcode" > /tmp/z.asc
(mypy) [sakae@deb ~]$ wc /tmp/z.asc
  15   54 1054 /tmp/z.asc
(mypy) [sakae@deb ~]$ cat /tmp/z.asc
                             
                             
    █▀▀▀▀▀█ █ ▄ ▀ █▀▀▀▀▀█    
    █ ███ █ ▀▄█ █ █ ███ █    
    █ ▀▀▀ █ ▀ ▄▄█ █ ▀▀▀ █    
    ▀▀▀▀▀▀▀ ▀▄█▄█ ▀▀▀▀▀▀▀    
    █▄▄▀██▀████ █▀▄▄█▄█▀▀    
     █▄▀▄▄▀ ▀ ▀▄ ▄ ▄▄ █▀▀    
         ▀▀ ▄▀  ▄█▄█▀█ █     
    █▀▀▀▀▀█ █ ▀▄█▄█ █▄█ ▄    
    █ ███ █ ██▄▄▀▄▄▀ ▀ ▀     
    █ ▀▀▀ █     █ ▄██████    
    ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀          
                            

文字表現でもOKとな。SVGもお勧めかな。

QRコードのしくみ

QR コードの概要 本家のデンソーさん

QRコードのしくみ

参考資料 総務省って、なんだかなあのタイトルを付けるな。JRAも使ってるか ら、農水省とでも、タイアップしてみ。それともChatGPTにお願いする?

QR コードを作ろう! (1) QR コードを作ろう! (2) good

仕組みが分かれば、スマホなどいらぬ……ッ! 肉眼のみで解読するQRコード講座

QRコードの符号化・復号アルゴリズム解説

Goodbye 日経Linux

25年の渡って楽しませてくれた日経Linuxが休刊するらしい。 最後の号は、12月らしいんで、記念に買っておこうかな。 まるで、廃線になる列車に群がる人々みたいだな。 これで、立ち読み雑誌も一つ減る。残念であります。

この雑誌の後はマイクロソフトにお任せて事で、早速第一段の記事が 掲載された。

まずはインソトールって事だ。

How to download and install Linux


This year's Index

Home