Division By Zero

ゼロで割る

6LowPANについて少し考えてみた - その2

前回の続き。

6LowPANのヘッダ圧縮についてはRFC4944(2007年9月)にて標準化され、その後、RFC6282(2011年9月)にて更新されている。

LOWPAN_HC1 and LOWPAN_HC2 are insufficient for most practical uses of
IPv6 in 6LoWPANs.

http://www.ietf.org/rfc/rfc6282.txt

とあるように、RFC4944で標準化されたHC1(IPヘッダの圧縮)とHC2(UDPヘッダの圧縮)だけでは現実の問題を解決できない、と判断されたようだ。

LOWPAN_HC1 is most effective for link-local
unicast communication, where IPv6 addresses carry the link-local
prefix and an Interface Identifier (IID) directly derived from IEEE
802.15.4 addresses. In this case, both addresses may be completely
elided. However, though link-local addresses are commonly used for
local protocol interactions such as IPv6 Neighbor Discovery
[RFC4861], DHCPv6 [RFC3315], or routing protocols, they are usually
not used for application-layer data traffic, so the actual value of
this compression mechanism is limited.

http://www.ietf.org/rfc/rfc6282.txt

HC1はIPv6近隣探索(ND)やDHCPv6、その他ルーティングプロトコルで使われる、IPv6リンクローカルユニキャストでは効果的だが、(IPv6リンクローカルユニキャストでない)アプリケーションレイヤのデータでの効果は限定されてしまうとのこと。これはどういうことか。

HC1ではIPv6ヘッダ40オクテットを下記のように最小で3オクテットに圧縮する。

IPv6 LOWPAN_HC1
HC1 Dispatch 0 bit 8 bit
Version 4 bit 0 bit
Traffic Class/Flow Label 28 bit 1 bit
Payload Length 16 bit 0 bit
Next Header 8 bit 2 bit
Hop Limit 8 bit 8 bit
Source/Destination 256 bit 4 bit
HC2 encoding 0 bit 1 bit
total 320 bit 24 bit

考え方を整理すると、こんなところか。

  1. 不要なものは削除(Version)
  2. 必要なものはそのまま(Hop Limit)
  3. L2等の情報と重複するものは省略(Payload Length, Source, Destination)
  4. 省略できない場合のためにフラグを残し、必要な場合のみインラインに載せる(Source, Destination, TC/FL, HC2)

SourceとDestinationでは、それぞれ2 bitを使い、前半64 bitをインラインに載せる/載せない、後半64 bitをインラインに載せる/載せないを表現。リンク内の通信であれば、SourceとDesitinationの両方で、前半/後半共に「載せない」ことができるため、ヘッダ長は短くなる。それが、リンク外との通信になると、SourceかDesitinationどちらかの128 bitと、一方の前半64 bitをインラインに「載せる」必要があり、残った後半64 bitもshort addressを使っている802.15.4リンクでは「載せる」必要が出てくる。また、マルチキャストアドレスを圧縮する仕組みがなく、128 bit全て「載せる」必要がある。

これをRFC6282ではどう解決するのか。長くなったので今日はこれで終了!

続きはこちら

JPEGminiフォトサーバーのリサイズ機能

JPEGminiフォトサーバーは、JPEGminiLinuxスタンドアローン版。コマンドラインで入力フォルダ、出力フォルダ等を指定する以外に、リサイズも指定できるのだが、最も使用しそうな - いろいろな解像度の写真をアスペクト比を維持したまま一定の解像度に変換 - ができない。

jpegminiフォトサーバーのリサイズオプションは、以下のように指定する。

  • rsz=: アスペクト比を維持したまま、元の幅と高さのx%にリサイズします。 x の値は1 から99
  • rsz=width x height: アスペクト比を維持せずに、width x height ピクセルにリサイズ

となると、冒頭の目的を達するにはImageMagick等を使用する必要する必要があるが、下記のどちらがいいだろうか。結果に画質やファイルサイズなどの差は出るだろうか。

  1. ImageMagick/convertでquality 100でリサイズしてから、JPEGmini
  2. ImageMagick/identifyでオリジナルの解像度を調べ、それをベースにJPEGmini

フリーの画像十数枚(解像度:1600x1200程度)とスマートフォンで撮った十数枚(解像度:4128x3096)をWVGA(800x480)、XGA(1024x768, iPad2/mini)、QXGA(2048x1536, iPad Retina)に変換してみたが、大きな差は出なかった。

JPEGminiに与える情報量が多い2.の方が、いい結果が出るかと思ったが、あまり関係ないようだ。つまり、ImageMagickを使ってリサイズを含む色々な変換を行う既存のシステムに対して、ファイルサイズを減らすためにJPEGminiフォトサーバー処理を加える場合、単純に最後に加えるだけでOK。

ただ、JPEGminiのリサイズはconvertに比べるとずいぶん速い(当方環境で4.6倍ほど。JPEGminiは最適化を含めた速度で比較)。改造が面倒でなければ、2.の方法がよさそうだ。今日はこれで終了!

Spyder3 Expressで複数のディスプレイをキャリブレーションしたい

写真の印刷をする際に、ちょっと違うかなと感じたこともあり、ディスプレイのキャリブレーションをしたくて、3年前にSpyder3 ExpressというモニタキャリブレーターをUSアマゾンから購入した。その後、ディスプレイを追加して、色の違いが気になることもあったが、そのまま利用していた。モノを整理していたら、このキャリブレーターが見つかったので、サブのディスプレイもキャリブレーションしてみようと思い立った。

ところが、このExpressという廉価版はマルチディスプレイに対応していないようだ。検索して調べてみたところ、具体的には、

  1. メインディスプレイ側のみでしか、キャリブレーションアプリが動作しない
  2. ロードされるプロファイルがメインとサブと同一のもの

ということらしい。

一つ目の制限はメインディスプレイのプロファイルを別名保存した後で、それをとり外した状態でPCを起動することで回避できる(当方の環境では「画面の解像度」パネルでメインを移してもだめだった)。なお、プロファイルは下記フォルダに生成される。

%SystemRoot%\System32\spool\drivers\color\

二つ目の制限は、「色の管理」で、ディスプレイ毎に作成したプロファイルを「既定」に設定することで回避できた。つまり、Spyder3Utilityが作成するプロファイル名がSpyder3Express.icmというファイルで固定されていることだけが問題だったのか、あるいはWindows7だとうまくいくだけなのか。

Windows7はディスプレイ毎にプロファイルを設定できるが、反映させるにはアプリが必要だ。Spyder3 Expressの場合は、スタートアップフォルダにあるSpyder3Utilityが起動する際に、ディスプレイアダプタに反映してくれる。

また、ディスプレイアダプタのドライバがOSに既定としてセットしたプロファイルを反映させるのを防いでしまうこともあるようだ。その場合は個別に対応する必要がある。

全く同じとは言えないまでも、ディスプレイ間の明らかな違和感はずいぶん減った。うまくいったので今日はこれで終了!

反比例の意味

入力データ量とCPU利用率をグラフにしたところ、原点を通らない比例関係にあることがわかった。このx切片の意味を調べようといろいろやっているうちに、入力データ速度とCPU利用率が反比例であることがわかった。

反比例と言えば、普通の会話の中で「負の比例係数を持つ比例」と混同している人が多いと感じていて、きっと同じことを思う人も多いだろうと思い検索してみたら、やはりここに。

英語圏ではどうだろう。ぱっと調べた限りでは、数学的な意味での反比例は「inverse proportionality」、そうでない反比例は「inverse relationship」となっているように思える。価格と需要の関係などでは、「inverse relationship」となっており、単に右下がりであることを示しているようだ。

英語版wikipediaでの「inverse relationship」の説明は、「負の比例係数を持つ比例」となっているが、「引用がない」とされてしまっている。この「inverse relationship」自体の言葉のゆるさゆえに、需要と価格の関係は「inverse relationship」といっておけば問題なさそうだ。

さて、日本語の「反比例」。すぐに、反比例曲線を思い浮かべてしまう人にとっては、「需用と価格の関係が反比例」と言われると、「えっ」と思ってしまうことだろう。「反比例」という言葉を使いたがるのは、「inverse relationship」に代わる、右下がりを示して、かつ、ちょっとかしこまった言葉がない、ということが原因なのではないだろうか。「比例」という言葉は数学以外でも使う。「proportionality」もそうだが、数学の比例関係自体がわかりやすく、誤解の余地が少ないことから、とりたてて問題になることはないと思う。「読んだ本の数に比例して、文章力は増すよね」と言ったところで、2冊から20冊に増やした人の文章力の伸び具合と、200冊から2000冊に増やした人のそれは同じ?とか食い下がらない。

需要と価格の関係は右下がりなだけで「負の比例係数を持つ比例」だとは限らないが、仮にそのようなモデルを考えたとしても「需要と価格は比例する」とは言わないだろう。数学的には誤りでないにしても。とはいえ、個人的には、定数が負の反比例は「反比例」としかいいようがない。

ここに異論がある人がいるのかもしれない。「比例」という言葉から、右上がり・右下がりに関わりなく直線を思い浮かべる人とか、定数の正負に思考を囚われない人が。


このエントリ、ずいぶん書きかけのまま放置していたのだが、冒頭の「入力データ速度とCPU利用率が反比例」って何だったのか思い出せないまま、今日は終了!

WPFアプリでバックグラウンドスレッドを使う

カスタムDOSコマンドを起動するWPFアプリを作ってみたが、そのコマンドで利用する実デバイスをつなげてテストしてみたところ、思ったより処理に時間がかかることがわかり、別スレッドで起動するようにした際のメモ。プログレスバーとキャンセル処理も加えてある。

バックグラウンドスレッドの起動は以下のように行った。

  1. BackgroundWorkerインスタンスを生成して、プログレスバーの更新とキャンセル処理に必要な設定を行う
  2. DoWorkにバックグラウンドでやりたい処理を記述。ここのみバッググラウンド処理となる
  3. ProgressChangedに進捗情報更新時にやりたい処理を記述
  4. RunWorkerCompletedにやりたい処理を記述
  5. RunWorkerAsync()でバックグラウンドスレッドを起動
    bw = new BackgroundWorker();

    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;

    bw.DoWork += (s, evt) =>
    {
        job(evt);
    };

    bw.ProgressChanged += (s, evt) =>
    {
        プログレスバー.Value = evt.ProgressPercentage;
    };

    bw.RunWorkerCompleted += (s, evt) =>
    {
        if (e.Cancelled) {
            job_cancelld(evt);
        }
        job_completed(evt);
    };

    bw.RunWorkerAsync();

バックグラウンドスレッドをキャンセル、つまり、メインスレッド側から途中で停止させる際の流れは以下のようになる。

  1. メインスレッド側でCancelAsync()を呼び出すと、CancellationPendingがtrueになる
  2. バックグラウンドスレッド側で、CancellationPendingを定期的に確認するようにする
  3. CancellationPendingがtrueならCancelをtrueに変更してバックグラウンドスレッドからreturnする
  4. 上二つは自分で記述する
  5. キャンセル時もRunWorkerCompletedに記述した処理が開始するので、Canceledをチェックすれば、キャンセルされたかどうかがわかる
  6. 必要ならば、キャンセル時の、通常時と異なる処理を記述

また、プログレスバーの更新などは、適宜ReportProgress()をバックグラウンドスレッドから呼び出すことになる。これらを踏まえると、バックグラウンドスレッドから呼び出す処理は下記のようになる。

private void job(DoWorkEventArgs evt)
{
    for (int i = 0; i <= 100; i += 10) {
        if (bw.CancellationPending) {
            e.Cancel = true;
           return;
         }
        10_percent_job();
        bw.ReportProgress(i);
    }
}

たとえば、キャンセルボタン等のイベントハンドラからCancelAsync()を呼べば、バックグラウンドスレッドをキャンセルできるはず。

今日はここで終了!

WPFアプリで特定プロセスを強制停止させる

WPFアプリから、あるカスタムなDOSコマンドを起動して、あるハードウェアを制御しているのだが、そのコマンドが終了しない場合がある。原因はいろいろだが、CTRL+Cに相当する手段をWPFアプリにも設けたい。

当該コマンドはWPFアプリから下記のようにProcess.Startで起動している。

ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = Environment.GetEnvironmentVariable("ComSpec");
psi.RedirectStandardInput = false;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.Arguments = @"/c カスタムDOSコマンド"

Process p = Process.Start(psi);
string results = p.StandardOutput.ReadToEnd();
p.WaitForExit();

子プロセスをKillすればいいように思えたが、WPFアプリにとっての子はcmd.exeであり、カスタムDOSコマンドではない。また、cmd.exeをKillしてもカスタムDOSコマンドは残ってしまうようだ。

cmd.exeの子プロセスをKillすればいいのだろうが、どうも簡単にはいかないようだ。Performance Counter経由で、あるプロセスの親を探すことはできるようだが、今回は面倒だったので、直接、コマンド名称で終了することにした。

var processes = Process.GetProcessesByName("カスタムDOSコマンド");
int i = 0;

for (; i < processes.Length; i++)
{
    processes[i].Kill();
}

もともと当該コマンドは複数起動すべきではないので、今回はこれでいいことにする。うまくいったので、今日はこれで終了!

WPFアプリで二重起動の防止を行う

以下の手順で、WPFアプリで二重起動を防止することができた。

  1. ソリューションエクスプローラーからApp.xamlを開く
  2. プロパティウィンドウの「イベント」タブで、Startupをダブルクリック
  3. 下記のようなコードをApp.xaml.csに加える
private void Application_Startup(object sender, StartupEventArgs e)
{
    mutex = new System.Threading.Mutex(false, "Mutexの名称");

    if (!mutex.WaitOne(0, false))
    {
    MessageBox.Show("起動済み");
    mutex.Close();
    mutex = null;

    this.Shutdown();
    }
}
  1. App.xamlを開く
  2. プロパティウィンドウの「イベント」タブで、Exitをダブルクリック
  3. 下記のようなコードをApp.xaml.csに加える
private void Application_Exit(object sender, ExitEventArgs e)
{
    if (mutex != null)
    {
        mutex.ReleaseMutex();
        mutex.Close();
    }
}

こちらを参考にさせていただいた。

目的達成できたので、今日はこれで終了。