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点のペアと一致しない
この性質のおかげで、 自己相関がめちゃくちゃ良い(サイドローブがない) という強力な特性を持ちます。
コスタス配列は元々 軍事レーダー(パルス圧縮) で有名になった技術ですが、 /雑音・マルチパス・ドップラー変動に強い時周波符号/として、 近年はデジタル通信でも応用されています。
### 無線通信でのメリット
- 極めて鋭い自己相関 ⇒ 同期捕捉が容易、弱い信号でもタイミングを検出できる。
- 他のパターンと混信しにくい ⇒ 他局の符号系列と衝突しにくい。
- オート・アンビギュイティ(曖昧度関数が良い) ⇒ 時間ズレ・周波数ズレの識別がしやすい。
- 低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位置の合計エネルギー」をスキャンします。使い方はスクリプト先頭のパラメータを変更するだけです。
うーん、難解だ。
python
どうやらpythonではnumpy + matplotlib が鉄板っぽい。使い方を探してみる。
簡単な例として、最高(最低)血圧と平均血圧の相関計算。
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
README
最近クマのニュースでもちきりだ。緊急猟銃とか警察のライフル部隊の投入とか。
この本の帯にもクマは「増え過ぎ」なのか? なんて大書してあったぞ。 思わず記述時期をみたら、2025年7月となってた。著者は、この時点でクマ被害を 予測してた事になる。占い師か? いや、北大の先生だった。ちゃんと科学すると こうなるって分った訳だね。
オイラーが就職した時に1冊のプログラミング本の勉強を勧められた。BASICなんて 言語の奴ね。そこに、キツネ(捕食者)とウサギ(被捕食者)の増減をシュミレーション しろって課題が載ってた。
ウサギが増えれば、キツネから見れば食料たっぷりでキツネも繁栄する。でも、ウサギ を食べ尽してしまえばキツネも衰退する。キツネが極小になれば、ウサギは繁殖の チャンスを得て、ウサギ算的な増加する。長い目で見れば、2種の増減は、ある周期 で振動する。思い出すなあ。この問題。
現実世界では、こんな単純なものではない。他の要因がからんでくるからね。 この本は、そんな事を交えて色々と解説されている。楽しいぞ。