rdsetroot

Table of Contents

FT8

前回ちょっと無線にも関係有りそうな記事を書いたので、その流れでFT8です。 2023年にもやってるけどね。当時の記事を見ると、FFTやらGoertzel algorithmが 出てきてたから、それの復習か?

Q: アマチュア無線で流行してる、FT8(通称)電波形式について質問します。 この電波をデコードする為の核心技術として、コスタス配列が使われているそうです。 この配列について解説してください。

A: アマチュア無線界隈で「F8」や「FT8モード」と呼ばれているデジタル方式(実際には/FT8 ではなく、8トーン系新興モード/を指すことが多い)では、 /シンボル系列の誤り検出・同期のために “コスタス配列 (Costas Array)” が核心技術として使われている/と言われます。

以下では、/コスタス配列とは何か、なぜ無線通信で有効なのか、どんな数学的性質を持つか/をわかりやすく解説します。


### ● 一言で言うと

/「2次元のパーマネーション行列で、どの2つの点のベクトル差も重複しないようにした特別なパターン」/です。

もっと簡単に言うと:

“どの点の並びも唯一無二の時間-周波数変化パターンになるようにした、レーダー・通信向けのスペシャルな配列”

です。


例えば 5×5 のコスタス配列はこんな感じ:

・ ・ X ・ ・
・ ・ ・ ・ X
・ X ・ ・ ・
X ・ ・ ・ ・
・ ・ ・ X ・

ここで “X” の位置(行=時間、列=周波数)だけを読み取ると、

[3,5,2,1,4]

という 置換(パーミュテーション) が得られます。


### ● どの2つの点も「同じ差分ベクトル」を持たない!

つまり、

  • どの2つのマーカー間の距離・ずれ(Δ時間、Δ周波数)
  • それらが決して他の2点のペアと一致しない

この性質のおかげで、 自己相関がめちゃくちゃ良い(サイドローブがない) という強力な特性を持ちます。


コスタス配列は元々 軍事レーダー(パルス圧縮) で有名になった技術ですが、 /雑音・マルチパス・ドップラー変動に強い時周波符号/として、 近年はデジタル通信でも応用されています。

### 無線通信でのメリット

  1. 極めて鋭い自己相関 ⇒ 同期捕捉が容易、弱い信号でもタイミングを検出できる。
  2. 他のパターンと混信しにくい ⇒ 他局の符号系列と衝突しにくい。
  3. オート・アンビギュイティ(曖昧度関数が良い) ⇒ 時間ズレ・周波数ズレの識別がしやすい。
  4. 低S/Nでも受信できる ⇒ ノイズに埋もれた状態からでも同期できる。

FT8 と異なり、F8(通称)は /広帯域・高速同期/を目的としており、 このような同期用パターンが必要なため /コスタス配列が採用/されるわけです。


多くの 8-tone MFSK 系モードでは、

  • 8個のトーンを使う
  • 複数シンボルに渡って特別な並びを送ることで
    • タイミング同期
    • 周波数同期
    • 位相の判定

を行います。

この「特殊な並び」が /コスタス配列/です。

### 構造イメージ(概念図)

時刻 t1:  トーン #3
時刻 t2:  トーン #7
時刻 t3:  トーン #1
時刻 t4:  トーン #5
時刻 t5:  トーン #8
...

この並びは数学的に

  • 他の任意の並びと混線しない
  • 逆相関でも一致しない
  • 雑音の中でも検出できる

ように作られています。


コスタス配列は 置換 π(i) を使って次の条件を満たします:

任意の i≠j に対し、

(i - j, π(i) - π(j)) が

他の (k - l, π(k) - π(l)) と一致しない。

これは「全ての点ペア間の差分ベクトルが異なる」という意味。

結果として、

  • 周波数ホッピングコードとして最強クラス
  • 時間周波数グラムで「十字相関ゼロ」
  • ドップラーにもロバスト

という美しい数学的性質を持ちます。


F8系モードでは、

  • /同期ヘッダ/にコスタス配列を利用
  • このパターンを使うことで
    • 周波数オフセットの推定
    • シンボルタイミング取得
    • キャリアトラッキング
    • ノイズ下のデコード安定化

これが可能になっています。

強いノイズ下での同期性能は /「コスタス配列の自己相関が鋭い」おかげ/です。


## まとめ

項目 説明
------- -----------------------
コスタス配列 置換行列で、差分ベクトルが全て異なる特別な配列
特徴 自己相関が鋭い、混信しにくい、同期に強い
用途 レーダー・通信の同期、符号設計
F8系での役割 同期ヘッダ、周波数・時間オフセット推定

必要なら、以下も説明できます:

  • コスタス配列の生成アルゴリズム
  • Python/MATLABでのシミュレーションコード

どこまで掘りますか?

sample

未同期(アラインメント不明)ケースでスライディングウィンドウ検出を行い、さらに簡単なMonte-CarloでSNRごとの検出率を評価するワンファイルPythonスクリプトを用意しました。Goertzelで各シンボルのトーンエネルギーを計算し、ウィンドウをスライドして「期待するCostas位置の合計エネルギー」をスキャンします。使い方はスクリプト先頭のパラメータを変更するだけです。

ft8 sim

うーん、難解だ。

python

どうやらpythonではnumpy + matplotlib が鉄板っぽい。使い方を探してみる。

numpy + matplotlib + scipy

【NumPy入門】よく使う機能一覧

簡単な例として、最高(最低)血圧と平均血圧の相関計算。

import numpy as np

# ====== データ読み込み ======
# データ形式: "最高,最低" の CSV 形式行が並ぶ bp.log
data = np.loadtxt('bp.log', delimiter=',')

# data[:,0] = 最高血圧 (SBP)
# data[:,1] = 最低血圧 (DBP)
sbp = data[:, 0]
dbp = data[:, 1]

# ====== 平均血圧 (MAP) の計算 ======
# 看護師さんの式:
#   MAP = (SBP - DBP) / 3 + DBP
map_bp = (sbp - dbp) / 3 + dbp

# ====== 相関係数の計算 ======
# NumPy の corrcoef を使うと 2x2 の相関行列が得られる
corr_sbp_map = np.corrcoef(sbp, map_bp)[0, 1]
corr_dbp_map = np.corrcoef(dbp, map_bp)[0, 1]

print("相関係数(最高血圧 vs 平均血圧):", corr_sbp_map)
print("相関係数(最低血圧 vs 平均血圧):", corr_dbp_map)

何でこんな簡単な例題をチャッピーにお願いしたか? それは、平均血圧の入れ物を どうやって確保するか知恵を拝借したかったから。事前にnp.zeros(N)なんてやらなくても 、良きに計らってくれるのね。schemeとかのmap関数機能が実現されてるんだな。 相関係数も一発計算してくれてる。普通に使う分には、問題ないな。

fuguita

ちょっと間が空いちゃったけど、以前から利用してるUSB memoryに入れた7.7の 構成を調べてみる。

precheck

問題の発端は、7.7は起動が速い。その後にUSB HDDに入れた7.8は遅い。その原因と おぼしきものに、saveasで保存されるエリア問題が有るっぽい。

起動直後のtopのメモリー状況

7.7 起動完了まで、2分40秒
Memory: Real: 1458M/1778M act/tot Free: 6017M Cache: 68M Swap: 0K/0K
7.8 起動完了まで、6分50秒
Memory: Real: 3077M/3834M act/tot Free: 3960M Cache: 318M Swap: 0K/16M

別の観点からって事で、RAM DISKの使用状況。

7.7   df -h
mfs:25388      5.7G    1.2G    4.3G    22%    /ram
7.8   df -h
mfs:24911      5.7G    2.7G    2.8G    50%    /ram

これを見て解る通り、起動時にメディアからRAMに大量のデータ転送が行なわれている (だから余分な時間がかかる)。更にメモリーが圧迫されて、自由に使える領域が 減少してんだな。

fu$ doas usbfadm -i
 Version/Arch: 7.8/amd64  (FuguIta-7.8-amd64-202510311)
 :
Filesystem     Size    Used   Avail Capacity  iused    ifree %iused  Mounted on
/dev/sd1d      129G   28.4G    101G    22%   279859 77273419    1%   /usbfadm.d/mnt1

7.8の方はたっぷりエリアを使っているからなあ(おっと、saveasなエリア以外に、 場所を確保してて、そこに色々と入れちゃったから、比較にはならんか)。ともかく、 色々なpkgを、これでもかって具合に入れたのは、間違いない。

2.7G    7.8/amd64/fudat
512B    7.8/amd64/noasks

ああ、これが実績か。

もう少し突き詰めると、7.8の方はこうなってた。

fu$ mount
/dev/rd0a on / type ffs (local)
/dev/sd1a on /sysmedia type ffs (local, read-only)
/dev/vnd5a on /fuguita type ffs (local, read-only)
mfs:24911 on /ram type mfs (local, wxallowed, size=12378112 512-blocks)

対して7.7の方

fu$ mount
/dev/rd0a on / type ffs (local)
mfs:25388 on /ram type mfs (local, wxallowed, size=12378112 512-blocks)
/dev/sd1d on /ram/my type ffs (local)

構成が違うじゃん。

作者に聞いてみた

> Fuguita 7.8 を入れてみました。
> 夏頃に入れた 7.7 と構成が代ったのでしょうか?
>
> /dev/vnd5a     1.2G    1.2G    3.7M   100%    /fuguita
>
> これが、read-only に、今は設定されてて、改変不可です。

/dev/vnd5aを書込み不可ででマウントする仕様は当初から変えていませんね。

GitHubのblame機能、blame prior to機能で追っかけてもらえばわかると思い
ます。

https://github.com/ykaw/FuguIta/blame/master/rdroot/boottmp/rc

check myself

お勧めのrcを追求してくと、fstabを自動生成していた。

fu$ cat /boottmp/fstab
# generated automatically at boot time
/dev/rd0a / ffs rw 0 0
/dev/sd1a /sysmedia ffs ro 0 0
/dev/vnd5a /fuguita ffs ro 0 0
mfs /ram mfs rw,wxallowed 0 0

最終的に、このテーブルを使ってマウントしていくんだな。それなのに運用状態になった 河豚板では、下記の様であった。

fu$ ls -l /
total 50
drwxr-xr-x   2 root  wheel    512 Jun 15 09:52 CVS/
lrwxr-xr-x   1 root  wheel     11 Nov 13 06:06 altroot@ -> ram/altroot
lrwxr-xr-x   1 root  wheel      7 Nov 13 06:06 bin@ -> ram/bin
lrwxr-xr-x   1 root  wheel      7 Jun 15 09:52 bootetc@ -> boottmp
drwxr-xr-x   3 root  wheel   1536 Nov 13 06:06 boottmp/
drwxr-xr-x   6 root  wheel  20992 Nov 13 06:06 dev/
lrwxr-xr-x   1 root  wheel      7 Nov 13 06:06 etc@ -> ram/etc
drwxr-xr-x   2 root  wheel    512 Jun 15 09:52 fuguita/
lrwxr-xr-x   1 root  wheel      8 Nov 13 06:06 home@ -> ram/home
drwxr-xr-x   2 root  wheel    512 Jun 15 09:52 mnt/
lrwxr-xr-x   1 root  wheel      6 Nov 13 06:06 my@ -> ram/my
drwxr-xr-x  14 root  wheel    512 Aug 11 16:42 ram/
lrwxr-xr-x   1 root  wheel      8 Nov 13 06:06 root@ -> ram/root
lrwxr-xr-x   1 root  wheel      8 Nov 13 06:06 sbin@ -> ram/sbin
lrwxr-xr-x   1 root  wheel      7 Nov 13 06:06 sys@ -> ram/sys
drwxr-xr-x   2 root  wheel    512 Jun 15 09:52 sysmedia/
lrwxr-xr-x   1 root  wheel      7 Nov 13 06:06 tmp@ -> ram/tmp
lrwxr-xr-x   1 root  wheel      7 Nov 13 06:06 usr@ -> ram/usr
lrwxr-xr-x   1 root  wheel      7 Nov 13 06:06 var@ -> ram/var

fu$ ls -l /fuguita/
total 0
fu$ ls -l /sysmedia/
total 0

河豚板たらしめている、特性群は、usr/fuguita内に収納されてた。 sd1aが隠れてしまっているので、開陳してみた。

fu$ doas mount /dev/sd1a /mnt
fu$ ls -l /mnt
total 2355120
-rw-r--r--  1 root  wheel       88932 May  2  2025 boot
-rwxr--r--  1 root  wheel        2048 Jul  1 02:24 boot.catalog*
-rw-r--r--  1 root  wheel    14279784 Jul  1 02:12 bsd-fi
-rw-r--r--  1 root  wheel    14300357 Jul  1 02:16 bsd-fi.mp
-rw-r--r--  1 root  wheel       90116 May  2  2025 cdboot
-rw-r--r--  1 root  wheel        2048 May  2  2025 cdbr
-rw-r--r--  1 root  wheel      358400 Jun 12 08:39 eficdboot
drwxr-xr-x  2 root  wheel         512 May  2  2025 etc/
-rw-r--r--  1 root  wheel  1176330240 Jul  1 02:30 fuguita-7.7-amd64.ffsimg

fu$ ls /mnt/etc
boot.conf
fu$ cat /mnt/etc/boot.conf
echo
echo >> bsd-fi.mp is for
echo >> multiprocessor kernel (default).
echo >> Enter 'bsd-fi' for Uniprocessor.
echo
set timeout 10
set image /bsd-fi.mp

起動時に、bsd-fi.mpがロードされて、そのカーネルがfuguita-7.7-amd64.ffsimg を利用するんだな。そいつを取り出してきて、 強引にこじ開けてみる。

fu$ doas vnconfig vnd0 /ram/home/sakae/fuguita-7.7-amd64.ffsimg
fu$ doas mount /dev/vnd0a /mnt
fu$ ls -l /mnt
total 44
drwxr-xr-x   2 root  wheel   512 Jun 10 11:27 altroot/
drwxr-xr-x   2 root  wheel  1024 Jun 10 11:32 bin/
drwxr-xr-x   2 root  wheel   512 Jul  1 02:23 dev/
drwxr-xr-x  25 root  wheel  1536 Jul  1 02:23 etc/
drwxr-xr-x   2 root  wheel   512 Jun 10 11:27 home/
drwxr-xr-x   2 root  wheel   512 Jun 10 11:27 mnt/
drwx------   3 root  wheel   512 Jul  1 02:23 root/
drwxr-xr-x   2 root  wheel  1536 Jul  1 02:23 sbin/
lrwxr-xr-x   1 root  wheel    11 Jun 10 11:27 sys@ -> usr/src/sys
drwxrwxrwt   2 root  wheel   512 Jun 10 11:27 tmp/
drwxr-xr-x  14 root  wheel   512 Jul  1 02:23 usr/
drwxr-xr-x  24 root  wheel   512 Jun 10 11:27 var/

/bsdとかが無いけど、普通のOpenBSDと一緒だな。bsd-fi.mpとかが最初に起動して いるんで、ここには無いんだな。

read rc

お勧めに従って、rc を読んでみる。

#====================
# Active code from here
#====================
# setup file systems;
#   1. /         ...  rd - ram disk attached to kernel
#   2. /sysmedia ...  CD or USB flashdrive - containing FFS system image
#   3. /fuguita  ...  vnode disk - mounted FFS system image
#   4. /ram      ...  user-modifiable file system
#

cd /

#-------------------------------
# setup root fs and record starting timestamp of boot
#
mount -w /dev/rd0a /

懇切丁寧なコメントが記述されてて、技術の継承に貢献されている。で、しょっぱな から気になる部分を発見した。/dev/rd0a って何? vndの仲間かな? 躊躇しないで、 man rd してみる。そしたらrdsetrootも觀ておけと言われた。

NAME
     rdsetroot – insert disk image into RAMDISK kernel

どうやらカーネルに同梱できるファイルシステムっぽい。

それはいいんだけど、そもそも論で、いきなりrcが出てきてる。また、同じ階層に mount*とかkshとかも有るぞ。しかもこれらのバイナリーは23という驚異のリンクを 誇っている。Linuxで使われるbusyboxなんだな。これらは、どうやって供給されるの?

rdsetroot

このコマンドを調べてみるか。いやその前にrdとかのドライバーだな。dev/rd.cに すぐ見付かった。

u_int32_t rd_root_size = ROOTBYTES;
char rd_root_image[ROOTBYTES] = "|This is the root ramdisk!\n";

こんな刻印が入るのかな。そして、usr.sbin/rdsetroot/rdsetroot.c

この中で、カーネルがELFって事を確認してからデータゼグメントにシンボルテーブルが 存在する事を確認し、書き込んでいる。

じゃ、これを作るにはどうするんだ? src/sys/arch/amd64/conf/RAMDISK なんて言うカーネルのconfファイルを見ればいいんだな。

option          RAMDISK_HOOKS
option          MINIROOTSIZE=4480
config          bsd root on rd0a swap on rd0b and wd0b and sd0b
pseudo-device   rd 1

これが特徴的な設定。カーネル自体も機能を極端に絞った設定になってる。 RAMDISK_CD ってconfigも有るけど、こちらは機能が豊富に設定されてた。

実際にこのRAMDISKを作成する現場を訪問してみる。 src/distrib/amd64/ramdiskA

bsd.rd: mr.fs
        cp bsd bsd.rd
        rdsetroot bsd.rd mr.fs

bsd:
        cd ${.CURDIR}/../../../sys/arch/${MACHINE}/compile/${RAMDISK} && \
            su ${BUILDUSER} -c '${MAKE} config && ${MAKE} clean && exec ${MAKE} ${MFLAGS}'
        cp -p ${.CURDIR}/../../../sys/arch/${MACHINE}/compile/${RAMDISK}/obj/bsd bsd

mr.fs: instbin bsd
        rm -rf $@.d
        install -d -o root -g wheel $@.d
        mtree -def ${MTREE} -p $@.d -u
        CURDIR=${.CURDIR} OBJDIR=${.OBJDIR} OSrev=${OSrev} \
            TARGDIR=$@.d UTILS=${UTILS} RELEASEDIR=${RELEASEDIR} \
            sh ${UTILS}/runlist.sh ${LISTS}
        rm $@.d/instbin
        makefs ${MRMAKEFSARGS} $@ $@.d

bsdなターゲットで作成したカーネル(bsd.rd)にmr.fsを追加してる。mr.fs の方は、別に用意したこんなlistsを元に、busyboxを作り出している。コメントにも 記述してあるけど、クランチバイナリーって呼ぶんか。

# copy the crunched binary, link to it, and kill it
COPY    ${OBJDIR}/instbin                       instbin
LINK    instbin                                 bin/arch
LINK    instbin                                 bin/cat
LINK    instbin                                 bin/chmod bin/chgrp sbin/chown
 ;

RAMDISKはCD用も準備されてて、listsを見るとfarmwareの一部も同梱されてた。

bsd.rd

bsd.rdの正体が見えてきたので、実体を普通のOpenBSDから抽出して調べてみる。

-rwx------   2 root  wheel  32244218 Oct 26 05:12 bsd*
-rw-------   1 root  wheel   4804509 Oct 26 05:10 bsd.rd

サイズは通常版の15%と小粒である。gzipで圧縮されているんで、展開してあげると 2.2倍に膨らんだ。

vm$ strings bsd.rd | grep RAMDISK
RAMDISK_CD#54
    @(#)OpenBSD 7.8 (RAMDISK_CD) #54: Sun Oct 12 13:03:06 MDT 2025
OpenBSD 7.8 (RAMDISK_CD) #54: Sun Oct 12 13:03:06 MDT 2025
    deraadt@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/RAMDISK_CD

汎用製を持たせてCD版が搭載されてるんだな。

vm$ rdsetroot -x bsd.rd > mini.img
vm$ ls -l mini.img
-rw-r--r--  1 sakae  wheel  3768320 Nov 21 06:45 mini.img

全体のサイズが10.2Mのうち3.7MがRAMDISKになってた。そんじゃ、中身を 拝見しましょ。

vm$ doas vnconfig vnd0 /tmp/mini.img
vm$ doas mount /dev/vnd0a /mnt
vm$ ls -l /mnt
total 155
lrwxr-xr-x  1 root  wheel     11 Oct 13 04:03 autoinstall@ -> install.sub
drwxr-xr-x  2 root  wheel    512 Oct 13 04:03 bin/
drwxr-xr-x  2 root  wheel   2048 Oct 13 04:03 dev/
drwxr-xr-x  5 root  wheel    512 Oct 13 04:03 etc/
lrwxr-xr-x  1 root  wheel     11 Oct 13 04:03 install@ -> install.sub
-rw-r--r--  1 root  wheel   2942 Oct 13 04:03 install.md
-rwxr-xr-x  1 root  wheel  65092 Oct 13 04:03 install.sub*
drwxr-xr-x  2 root  wheel    512 Oct 13 04:03 mnt/
drwxr-xr-x  2 root  wheel    512 Oct 13 04:03 mnt2/
drwxr-xr-x  2 root  wheel   1024 Oct 13 04:03 sbin/
drwxrwxrwt  2 root  wheel    512 Oct 13 04:03 tmp/
lrwxr-xr-x  1 root  wheel     11 Oct 13 04:03 upgrade@ -> install.sub
drwxr-xr-x  6 root  wheel    512 Oct 13 04:03 usr/
drwxr-xr-x  6 root  wheel    512 Oct 13 04:03 var/

インストーラー用なんだね。マシン依存のinstall.mdの他は、共通部のinstall.subに おまかせ。ユーザーランドは、

vm$ ls -l /mnt/bin
total 120224
-rwxr-xr-x  76 root  wheel  2362368 Oct 13 04:03 arch*
-rwxr-xr-x  76 root  wheel  2362368 Oct 13 04:03 cat*
-rwxr-xr-x  76 root  wheel  2362368 Oct 13 04:03 chgrp*
-rwxr-xr-x  76 root  wheel  2362368 Oct 13 04:03 chmod*
 :

こんな具合に、しっかりとチャンクバイナリーになってた。

vm$ less /mnt/var/tzlist
Africa/
Africa/Abidjan
Africa/Accra
 :
vm$ wc /mnt/var/tzlist
     622     622    9358 /mnt/var/tzlist

国と地域のタイムゾーン・リストが同梱されてて、インストール時に設定できるのね。

vm$ ls -l /mnt/etc/ssl/
total 432
-r--r--r--  1 root  bin  214173 Oct 13 04:03 cert.pem

後は、とっても大事な大事な証明書ですかね。

build

河豚板がどんな風に調理されるか、資料を探してみた。

河豚板のビルドシステム

河豚板のEFI CD boot / arm64 ISO 対応

後はこれを参考にMakefileを参照すればいいのかな。

README

最近クマのニュースでもちきりだ。緊急猟銃とか警察のライフル部隊の投入とか。

動物たちの「増え過ぎ」と絶滅を科学する

この本の帯にもクマは「増え過ぎ」なのか? なんて大書してあったぞ。 思わず記述時期をみたら、2025年7月となってた。著者は、この時点でクマ被害を 予測してた事になる。占い師か? いや、北大の先生だった。ちゃんと科学すると こうなるって分った訳だね。

オイラーが就職した時に1冊のプログラミング本の勉強を勧められた。BASICなんて 言語の奴ね。そこに、キツネ(捕食者)とウサギ(被捕食者)の増減をシュミレーション しろって課題が載ってた。

ウサギが増えれば、キツネから見れば食料たっぷりでキツネも繁栄する。でも、ウサギ を食べ尽してしまえばキツネも衰退する。キツネが極小になれば、ウサギは繁殖の チャンスを得て、ウサギ算的な増加する。長い目で見れば、2種の増減は、ある周期 で振動する。思い出すなあ。この問題。

現実世界では、こんな単純なものではない。他の要因がからんでくるからね。 この本は、そんな事を交えて色々と解説されている。楽しいぞ。


This year's Index

Home