Hann窓の実装

信号処理でよく出てくるHann窓(別名raised cosine)をCommon Lispで実装しました。例えば64点の窓が欲しいときにはx = [0...63]/63とやってあげて、w(x) = 0.5 - 0.5 cos(2πx)に突っ込んであげればOKです。簡単ですね。

(defun hann (n)
  "Hann window of length n."
  (assert (> n 0))
  (let ((nn (1- n)))
    (if (= n 1)
        1
        (mapcar #'(lambda (x)
                    (- 0.5 (* 0.5 (cos (/ (* 2 pi x) nn)))))
                (linspace 0 (1- n) n)))))

Octaveのhanning実装を参考にしたので当たり前ですが、計算結果はOctaveの結果と一致してます。

CL-USER> (hann 10)
(0.0d0 0.116977778440511d0 0.4131759111665348d0 0.7499999999999999d0
 0.9698463103929542d0 0.9698463103929542d0 0.7500000000000002d0
 0.413175911166535d0 0.1169777784405111d0 0.0d0)
>> hanning(10)
ans =
        0
   0.1170
   0.4132
   0.7500
   0.9698
   0.9698
   0.7500
   0.4132
   0.1170
        0

本日の給油(スーパーカブ110プロ/JA07)

給油日 オドメーター (km) 給油量 (L) 単価 (円/L) 燃費 (km/L) 距離単価 (円/km)
2021-11-04 12711.0 2.44 156.97 60.37 2.60
2022-01-03 12814.3 2.18 153.21 47.39 3.23
2022-02-23 12910.1 2.55 158.04 37.57 4.21
2022-03-06 13058.0 2.83 160.07 52.26 3.06
2022-05-26 13182.1 2.46 158.10 50.45 3.09
2022-06-22 13278.4 1.99 165.83 48.39 3.43
2022-07-27 13405.0 2.21 157.92 57.29 2.76
2022-08-31 13546.7 2.92 156.16 48.53 3.22
2022-11-05 13699.4 3.21 157.01 47.57 3.30

遠出する時はアフリカツインに乗るようになったため、スーパーカブでは近所しか走らなくなりました。なので燃費はそれなりになってしまっています。(とは言うものの、2022年に入ってから1000 km近くは走っていますね)

本日の給油(CRF1100Lアフリカツイン/2BL-SD10)

youtu.be

給油日 オドメーター (km) 給油量 (L) 単価 (円/L) 燃費 (km/L) 距離単価 (円/km)
2021-10-30 267 13.46 162 16.27 9.96
2021-11-06 507 15.29 157 15.70 10.00
2021-11-14 737 13.01 157 17.68 8.88
2021-11-27 924 13.07 157 14.31 10.97
2021-12-25 1188 15.90 156 16.60 9.39
2022-01-10 1398 13.71 153 15.32 9.99
2022-02-05 1740 18.22 158 18.77 8.42
2022-03-07 2007 16.46 162 16.22 9.99
2022-04-10 2283 14.23 163 19.40 8.40
2022-06-26 2628 18.97 166 18.19 9.13
2022-07-18 2860 12.74 160 18.21 8.78
2022-07-31 3128 13.63 157 19.66 7.99
2022-08-27 3401 16.63 157 16.42 9.56
2022-11-03 3727 17.20 158 18.95 8.34

9〜10月は週末に台風が来たり雨が降ったりすることが多くて、ほとんど乗れませんでした。今シーズンのキャンプの予定も流れてしまって、宿泊キャンプは8月の1回だけ。デイキャンプに1〜2回行ったくらいになってしまいました。

本来の12ヶ月点検の時期に海外出張の予定が入っていたため、少し早かったのですが10月初旬に12ヶ月点検を受けました。ちょうどアフリカツインのリコールもあったので、同じタイミングで対応してもらいました。エンジン制御コンピューターのプログラム書き換えということでしたが、対応後はアイドリング時の回転数が上がったような気がします。前の方が静かで良かったなぁ。

www.honda.co.jp

スーパーカブ用にヘルメット購入

いまスーパーカブ用に使っているヘルメットは2016年に買ったもので、だいぶへたってきています。

marui.hatenablog.com

カブも10年目の定期点検を終えたところでもあるので、新しいヘルメットを購入することにしました。今回も「白バイっぽいもの」というのが選択のポイントです。また、アフリカツイン用のヘルメットは別に持っているので、カブ専用には気軽に使えるキャップ型のヘルメットにすることにしました。

で、見つけたのが以下のポリススタイルなヘルメットです。三億円事件の頃の白バイ隊員をほうふつとさせるデザインです。

komineshop.shop21.makeshop.jp

コスプレするのには良さそうですがダサい普段使いしにくそうなので、無地のものにすることにしました。こっちもダサい趣きがありますが、スーパーカブに合わせるとぴったりな雰囲気になりそうです。

行列の転置(Common Lisp)

Common Lispで音を扱うときに、どのようなデータ構造で保持しておくのが良いのか少し考えました。

Matlabと同様にサンプル数×チャンネル数という2次元配列で保持することもできますし、例えば、1チャンネル分のサンプル列をvectorとしておき、それを複数あつめたリストにするという方法もあります。後者の場合はvectorごとに異なるサンプル数になったり、要素のデータ型が異なる可能性もあるので、データの利用時に注意が必要になります。前者のほうが単純に扱えそうなので、とりあえずはMatlab同様に、Common Lisp内蔵のarrayを使って2次元配列にすることにしました。

さて、Common Lispの配列では#2A((1 2) (3 4) (5 6))とすることで、Matlabでいうところの[1 2 ; 3 4 ; 5 6]と同様の3×2行列になります。配列要素にアクセスするときには、たとえば(aref #2A((1 2) (3 4) (5 6)) 1 0)のようにして1行目・0列目の指定をします(添字は0はじまりで、このarefの結果は3です)。また(row-major-aref #2A((1 2) (3 4) (5 6)) 4)とすると、各行を一列に並べたときの何番目の要素というのが取れます(ここでは5が戻ります)。column-major-arefというのはなさそうなので、おそらく配列は各行を一列に並べた状態でメモリに確保されているのでしょう*1。各チャンネルのデータを個別に扱うことが多いのであればチャンネル数×サンプル数としたほうが良いのかもしれませんが、複数チャンネルが同期しているほうが好都合なのであればサンプル数×チャンネル数とするほうが良いということになりそうです。

さて、オレオレ音ライブラリに使う最終的なデータ構造をどうするかはもう少し悩むことにして*2、サンプル数×チャンネル数とチャンネル数×サンプル数を行ったり来たりできるようにしておきたいと思います。つまりは、転置行列を作りたいということです。

与えられた行列がm×nであればn×mの配列を作って、対応する行列の要素をコピーするだけですので、それほど複雑ではありません。以下のように作ってみました。

(defun matrix-transpose (mtrx)
  "Transpose a matrix (two-dimensional array)."
  (assert (= (array-rank mtrx) 2))
  (let ((res (make-array (reverse (array-dimensions mtrx)))))
    (loop for r from 0 to (1- (array-dimension mtrx 0)) do
      (loop for c from 0 to (1- (array-dimension mtrx 1)) do
        (setf (aref res c r) (aref mtrx r c))))
    res))

こんなかんじになります。

(matrix-transpose #2A((1 2) (3 4)))
;;=> #2A((1 3) (2 4))

(matrix-transpose #2A((1 2) (3 4) (5 6)))
;;=> #2A((1 3 5) (2 4 6))

車輪の再発明してないでnumclを使えばいいんですが、なんとなく楽しくて……。

github.com

*1:C/C++などが代表的なrow-major orderingです。一方でMatlab、R、Juliaといった科学計算に強い言語はcolumn-major orderingになっていることが多い気がします。

*2:標本化周波数などのメタデータも保持したいので構造体かCLOSを使うことになるかと思います。