2016-04-08 授業日誌
1・2限
Java。教科書は林晴比古本。識別子の命名で変な省略が見られて気持ちがよくないが、ある程度はカバーしているから入門ならこれくらいなのだろうか。クラス設計については書かれていない。
今回は開発環境構築とHello worldで終了。浜松校での総合制作の時間で習ったときと開発環境はまったく一緒だったので、安心感はある。
3限
OpenCVによる画像処理とかをやるらしい。教科書をパラパラ見て、だいぶ昔にやった線形代数を復習しようと決めた。初回なのでガイダンスとWebカメラの確認のみ。
HHVM 上で動かしている MediaWiki の二重 gzip 圧縮問題
スパロボ Wikiをはじめとするクリエイターズネットワークの MediaWiki は、1 月末から表示速度改善・負荷削減のため HHVM + Nginx で動かしているのだが、そのときから特定の環境で表示がおかしいという問題が発生していたようだった。手元の Firefox、Chrome(PC・Android)では一切発生しなかったので気づかなかったが、改めて確認すると IE と Safari で CSS が一切適用されないという問題が発生していた。
原因
原因を探してもなかなか見つからなかったが、開発者ツールを見ていると load.php によるスタイルシート読み込みの一部で変な文字化けが発生していた。本家の「ResourceLoader/Features#Resource: Styles」を見ると gzip 圧縮機能が含まれていた。このあたりが怪しいと思い、検索していくと以下のページがヒットした。
- php - How to disable Nginx double gzip encoding when fastcgi backend occasionally serves gzipped with content-encoding? - Stack Overflow
- HHVM always compresses output if Accept-Encoding: gzip is set, even if zlib.output_compression=Off is set · Issue #5316 · facebook/hhvm
どうやら HHVM と Nginx の両方の設定(HHVM については標準・省略時設定)により二重に gzip 圧縮されていたらしい。この場合、HTTP レスポンスヘッダの Content-Encoding ヘッダが gzip, gzip
という値になる。ここで厄介だったのが、Firefox と Chrome はこの値に対応しており圧縮されていたファイルが正しく展開されるが、IE や Safari は未対応で文字化けファイルと化してしまうということ。この挙動は予想できず対応が遅れてしまった。
対策
ではどうやって対処するか。調べると以下のコミットが見つかった。
片方の gzip 圧縮を無効にすると良いということだった。このコミットでは HHVM 側の gzip 圧縮機能を無効にしている。この他に、MediaWiki 側の圧縮機能を無効にすることもできる(「Manual:$wgDisableOutputCompression - MediaWiki」)。HHVM 側の方がカバーできる範囲が広いので、今回はこちらを選択した。サーバー用の hhvm.ini に以下を記述する。
zlib.output_compression = "Off"
これにより、手元の全ブラウザで正常に表示されることを確認した。
今回のメンテナンス、学校の関係で時間がない中行った結果、確認不足だったりすぐに対応できなかったりしたので申し訳ない限りなのだが、特殊な環境の一部でのみ発生する問題だったので、仮に早く見つかったとしてもすぐに解決できたか怪しい。HHVM 上で MediaWiki を動かしているところはほとんど見ないし、日本語の情報も一切ないので、こういう問題が発生したときの対処は結構難しい…
WiFi アクセスポイント化した Raspberry Pi に iPhone・iPad から接続できなかった問題を解消した
卒研で使う Raspberry Pi を「Raspberry PiをWi-Fiアクセスポイントにする」あたりを見て WiFi アクセスポイント化していたのだけれど、なぜか iPhone・iPad から WiFi 接続できなかった症状に悩まされていた。いろいろ試してそれを解消することができた。
症状
WiFi アクセスポイント化した Raspberry Pi に対して iPhone・iPad から WiFi 接続しようとすると、ユーザー名とパスワードを要求される。ここを突破できなくて WiFi 接続できない。
原因と対策
hostapd.conf の wpa_key_mgmt に WPA-EAP が含まれていたこと。wpa_key_mgmt=WPA-PSK
だけにしたら直った(WPA パスフレーズのみ要求されるようになった)。
hostapd.conf は /usr/share/doc/hostapd/examples/hostapd.conf.gz を改変していたものだったが、これの wpa_key_mgmt の例をそのまま使っていた。
EAP というのは IEEE802.1Xという規格に採用されている認証プロトコルらしい。RFC 3748 で規定されているようだ。
新しい XCTU での XBee ファームウェアの復元
卒研で ZigBee 通信デバイスの XBee ZB S2 を使っているのだが、設定ソフトの XCTU (http://www.digi.com/support/productdetail?pid=3430&type=utilities) を使ってファームウェアを更新するときに失敗してしまって、PC から見えなくなってしまった。検索すると古い XCTU でファームウェアを復元する方法は見つかるが、新しい XCTU v6.2.0 で行う方法は見つからなかったのでまとめてみる。
USB 接続アダプタを準備する
XCTU を使っている時点で大丈夫だとは思うが、USB 接続アダプタが必要となる。自分が使ったのは秋月電子通商の XBee USB インターフェースボードキット。
XCTU を使ってファームウェアを復元する
『Xbeeファームウェアが飛んじゃったら。その対処法 - zimudomuzidomuの日記』にあるように、XBee を USB 接続アダプタに接続せずに XCTU を起動した。ただし、新しくなった XCTU でも最初に未接続にする必要があるかどうかは不明である。
起動したら、下図赤丸の「Add devices」をクリックする。
いつもの「Add a radio module」画面が出る。アダプタが接続されているポートと通信の設定を確認して次へ進む。
しばらくデバイスを探すが、ファームウェアが死んでいるので反応しない。「Action required」画面が出たらあきらめて「Cancel」を押す。
キャンセルすると「Could not found any radio module」画面が出る。下図赤丸の「Recovery」を押す。
ファームウェア選択画面が出る。直前まで使っていたファームウェアを選択する。外してある XBee を手元に置いてから、「Recover」を押す。
「Recovering radio module」画面が出たら、素早く USB 接続アダプタに XBee を挿す。数十秒未接続だと、上記の「Action required」画面が出てしまうので注意。自分の使っていた XBee では、挿すとすぐにファームウェアの書き込みが始まった。
認識されることを確認し、再設定を行う
ファームウェアの書き込みが終わると自動的にモジュールの探索が行われる。正常に書き込まれたならば、初期状態に戻った XBee が一覧に現れる。
XBee が無事に現れたら、以前と同じ状態になるように設定しなおせばよい。
和歌山県の道路規制情報のリポジトリ
先月はドライブ日和な日が多かったので、静岡 r389(水窪→山住神社→春野)とか r263(春野→川根本町)といった険道で楽しんできた。ところで GitHub を眺めていると、交通関係で気になるリポジトリが。
https://github.com/wakayama-pref-org/information-of-road-traffic-regulation
和歌山県の道路規制情報のリポジトリ。オープンデータとしてCSVが出たのは初めてではないだろうか。
これに対して国土地理院の藤村英範さんが GeoJSON 化の pull request を投稿している。GitHub 上のプレビュー画面を見ると、地図上に情報が出ていて分かりやすい。
それなりの距離のドライブをするときには道路の交通規制情報は重要なのだけれど、自分が知る限り、日本では再利用可能なデータとしては一切公開されていない。集約して公開する権利は日本交通情報センター(JARTIC)が握っていて、一般の人がまとめることは非常に難しい。このような事情であるから、他の都道府県にもこの例のようにまずオープンデータとして公開する流れが広まってほしいと思っている。理想的には形式の統一まで進んでほしい。
2015-06-10 の日記
今日は午後から、マイコン(授業で使っている AKI-H8/3069F ボード)に RS-232C 接続のバーコードリーダー経由で情報を取り込む実験を行った。取り込んだ情報は最終的にモデルに流し込み、LCD に表示することとなる。インターフェース技術の授業で習ったとはいえ、実際に回路を組むのは初めてなので分からないことばかりだった。
# 機材を取りに校内を行ったり来たりしていたときになおやさん(@naoya_24)と出会った。校長先生も一緒だったけれど、どんなご用事だったのだろう。
最終目的がバーコードリーダーを使うことだとしても、それ以前にシリアル通信で情報を取り込めなければ話にならないので、まずはそこから。授業で、TeraTerm を起動しキーボードで打った 1 文字を読み込む実習は行ったが、文字を貯め込むのはやったことがなかった。ただループで回すと同じ文字が大量に入力されることになったので、同じ文字が打たれている間は無視するようにしなければならない。大まかな流れは以下の形。
/* * シリアル通信で文字を取り込む */ #include "reg3067.h" // シリアル通信関連のビット #define SSR_RDR_FULL 0x40 // SCI1 を使って 1 文字受信する unsigned char SCI1_getchar(void); int main(void) { unsigned char ch; // 読み込んだ文字 unsigned char last_ch = '\0'; // 最後に読み込んだ文字 // シリアル通信の設定 SMR1 = 0x00; BRR1 = 10; // 20 MHz, 57600 bps SCR1 = 0x30; // RE on, TE on while (1) { ch = SCI1_getchar(); if (ch != last_ch) { // 同じ文字が入力された際の初回のみ // 文字を貯め込んで LCD 等に表示する } last_ch = ch; } return 0; } unsigned char SCI1_getchar(void) { // SCI1 受信:キー入力待ち SSR1 = 0x00; while (!(SSR1 & SSR_RDR_FULL)); return RDR1; // 読み出し }
バーコードは形式によって桁数が異なるが、今回は 8 桁程度あれば大丈夫そうなので、簡単な 8 桁分のリングバッファに貯めることにした。メモリを動的に確保しなくても使える。
/* * 8 文字分のリングバッファ */ // 8 文字分のリングバッファの構造体 typedef struct ring_buffer8 { unsigned int length; // 貯めこまれている文字列の長さ unsigned int current_index; // 現在の位置 char chars[9]; // printf デバッグをしやすいように 1 つ大きくした } ring_buffer8_t; // リングバッファを初期化する void ring_buffer8_init(ring_buffer8_t* buffer) { unsigned int i; buffer->length = 0; buffer->current_index = 0; for (i = 0; i < 9; ++i) { buffer->chars[i] = '\0'; } } // リングバッファに 1 文字追加する void ring_buffer8_push(ring_buffer8_t* buffer, unsigned char ch) { buffer->chars[buffer->current_index] = ch; if (buffer->length < 8) { ++buffer->length; } buffer->current_index = (buffer->current_index + 1) % 8; } // リングバッファの内容を文字列として取り出す void ring_buffer8_to_string(ring_buffer8_t* buffer, char* dest) { unsigned int i; if (length < 8) { for (i = 0; i < buffer->length; ++i) { dest[i] = buffer->chars[i]; } } else { unsigned int j; // 折り返し前:現在の位置から最後まで for (i = 0, j = buffer->current_index; j < 8; ++i, ++j) { dest[i] = buffer->chars[j]; } // 折り返し後:最初から現在の位置の手前まで for (j = 0; j < buffer->current_index; ++i, ++j) { dest[i] = buffer->chars[j]; } } dest[buffer->length] = '\0'; }
ここまでやって TeraTerm 上でキーボードを打つことで情報を入力できるようになったが、今日はバーコードリーダーへの移行は完了しなかった。TeraTerm では通信設定さえ合わせれば、きちんとバーコードを読み取って数字が表示された。オシロスコープで見ると、バーコードリーダーで読み取った情報は RxD 端子(D-Sub 9 ピンコネクタ 2 番)から流れてくるようだった。マニュアルを見ると JP1 と SCI0 がつながっていて、また電圧レベル変換 IC の ADM3202AN にも接続されているので、コネクタと JP1 の対応する端子同士を線材+はんだ付けで接続すれば良さそう。次回はそんな感じで進めたい。
すごい E 本・第 8 章の逆ポーランド記法計算機
「すごい Erlang ゆかいに学ぼう!」第 8 章の逆ポーランド記法(RPN)計算機を打ち込んでみた。パターンマッチのおかげで簡潔に書けるのが好み。こういう例は、今まで自分が使ってきた言語ではどうしても長くなってしまいそう。
演習問題
いくつか演習問題があったので考えてみた。
和
まずは和 sum
。6 章でやったように fold が使える。スタックとして使っているリストへの数の追加の順番を考えると foldr
が自然だけれど、foldl
にしてみた。加算は可換だから同じ結果になり、またマニュアルを見ると foldl
は末尾再帰だから、こちらの方が速そう。
rpn("sum", Stack) -> [lists:foldl(fun(N, Sum) -> Sum+N end, 0, Stack)];
積
積 prod
も同様。ちょうどこの前書いた SICP の問題の解答にもあった。初期値(単位元)が 1 であることに注意。
rpn("prod", Stack) -> [lists:foldl(fun(N, Prod) -> Prod*N end, 1, Stack)];
badarith
エラーを上げるようにする
pp. 100—101 の「NOTE」に書いてあること。よく分からない演算子やスタックに値が残ってクラッシュするときに、badarith
エラーを上げるようにする。
まずはスタックに余分な値が残っているとき。rpn/1
を修正する。
rpn(L) when is_list(L) -> case lists:foldl(fun rpn/2, [], string:tokens(L, " ")) of [Res] -> Res; _ -> erlang:error(badarith) % 追加 end.
続いて、よく分からない演算子が見つかったとき。read/1
の list_to_integer
に失敗することを利用すると、この場合を検出することができた。
read(N) -> case string:to_float(N) of {error,no_float} -> %% ここから追加 try list_to_integer(N) catch error:badarg -> erlang:error(badarith) end; %% 追加ここまで {F,_} -> F end.
さらに演算子を追加する例
例えば階乗を追加してみたらどうか、と思ってやってみた。
rpn("fact", [N|S]) -> [factorial(N)|S];
log10
等と同様にスタックから取り出す数は 1 つ。長くなるので factorial
は別に書いた。
%% 階乗関数 factorial(N) -> factorial(N, 1). factorial(0, Acc) -> Acc; factorial(N, Acc) when N > 0 -> factorial(N-1, N*Acc).
実行例。
14> calc:rpn("0 fact"). 1 15> calc:rpn("3 fact"). 6 16> calc:rpn("7 fact"). 5040
まとめ
本からいろいろと追加した分を含めた calc.erl を載せておく。
SICP: Exercise 1.36
問題 1.36
問題 1.22 で示した基本の
newline
とdisplay
を使い、生成する近似値を順に印字するようfixed-point
を修正せよ。次に \( x \mapsto \log(1000)/\log(x) \) の不動点を探索することで、\( x^x = 1000 \) の解を見つけよ。(自然対数を計算する Scheme の基本log
手続きを使う。)平均緩和を使った時と使わない時のステップ数を比べよ。(fixed-point
の予測値を 1 にして始めてはいけない。\( \log(1) = 0 \) による除算を惹き起すからだ。)
fixed-point
の方は、printf
デバッグのように display
を追加するだけ。
(define tolerance 0.00001) (define (fixed-point f guess) (define (close-enough? v1 v2) (< (abs (- v1 v2)) tolerance)) (define (try guess) (let ((next (f guess))) (display "guess: ") (display guess) (display ", next: ") (display next) (newline) (if (close-enough? guess next) next (try next)))) (try guess))
平均緩和を行わない場合は
(define (f x) (/ (log 1000) (log x))) (define (solve-x^x=1000) (fixed-point f 2.0)) (solve-x^x=1000) ; guess: 2.0, next: 9.965784284662087 ; guess: 9.965784284662087, next: 3.004472209841214 ; guess: 3.004472209841214, next: 6.279195757507157 ; guess: 6.279195757507157, next: 3.759850702401539 ; guess: 3.759850702401539, next: 5.215843784925895 ; guess: 5.215843784925895, next: 4.182207192401397 ; guess: 4.182207192401397, next: 4.8277650983445906 ; guess: 4.8277650983445906, next: 4.387593384662677 ; guess: 4.387593384662677, next: 4.671250085763899 ; guess: 4.671250085763899, next: 4.481403616895052 ; guess: 4.481403616895052, next: 4.6053657460929 ; guess: 4.6053657460929, next: 4.5230849678718865 ; guess: 4.5230849678718865, next: 4.577114682047341 ; guess: 4.577114682047341, next: 4.541382480151454 ; guess: 4.541382480151454, next: 4.564903245230833 ; guess: 4.564903245230833, next: 4.549372679303342 ; guess: 4.549372679303342, next: 4.559606491913287 ; guess: 4.559606491913287, next: 4.552853875788271 ; guess: 4.552853875788271, next: 4.557305529748263 ; guess: 4.557305529748263, next: 4.554369064436181 ; guess: 4.554369064436181, next: 4.556305311532999 ; guess: 4.556305311532999, next: 4.555028263573554 ; guess: 4.555028263573554, next: 4.555870396702851 ; guess: 4.555870396702851, next: 4.555315001192079 ; guess: 4.555315001192079, next: 4.5556812635433275 ; guess: 4.5556812635433275, next: 4.555439715736846 ; guess: 4.555439715736846, next: 4.555599009998291 ; guess: 4.555599009998291, next: 4.555493957531389 ; guess: 4.555493957531389, next: 4.555563237292884 ; guess: 4.555563237292884, next: 4.555517548417651 ; guess: 4.555517548417651, next: 4.555547679306398 ; guess: 4.555547679306398, next: 4.555527808516254 ; guess: 4.555527808516254, next: 4.555540912917957 ; guess: 4.555540912917957, next: 4.555532270803653 4.555532270803653
となった。34 ステップ。
\( x \mapsto \log(1000)/\log(x) \) の不動点探索で平均緩和を行う場合は、本文にあるとおり両辺に \( x \) を足して 2 で割る。 $$ \frac{x + x}{2} = \frac{x + \log(1000)/\log(x)}{2} \quad \therefore\ x = \frac{x + \log(1000)/\log(x)}{2} $$
コードを書いて実行すると
(define (solve-x^x=1000-with-average-damping) (fixed-point (lambda (x) (average x (f x))) 2.0)) (solve-x^x=1000-with-average-damping) ; guess: 2.0, next: 5.9828921423310435 ; guess: 5.9828921423310435, next: 4.922168721308343 ; guess: 4.922168721308343, next: 4.628224318195455 ; guess: 4.628224318195455, next: 4.568346513136242 ; guess: 4.568346513136242, next: 4.5577305909237005 ; guess: 4.5577305909237005, next: 4.555909809045131 ; guess: 4.555909809045131, next: 4.555599411610624 ; guess: 4.555599411610624, next: 4.5555465521473675 ; guess: 4.5555465521473675, next: 4.555537551999825 4.555537551999825
となった。9 ステップと大幅にステップ数が縮まった。