真実の楽譜(フルスコア)

プログラム関係の忘備録になるはず

C#で離散ウェーブレット変換

Scilabで離散ウェーブレット変換
http://truthfullscore.hatenablog.com/entry/2014/01/17/172746

C#で実装してみた。

ドベシィウェーブレット変換プログラム

DaubechiesWavelet.cs
using System;

namespace Wavelet
{
  class DaubechiesWavelet
  {
    //Daubechies係数を定義
    public static double[][] DaubechiesCoefficients =
    { 
      new double[] {1.0/Math.Sqrt(2.0),1.0/Math.Sqrt(2.0)},
      new double[] {0.6830127/Math.Sqrt(2.0),1.1830127/Math.Sqrt(2.0),0.3169873/Math.Sqrt(2.0),-0.1830127/Math.Sqrt(2.0)},
      new double[] {0.47046721/Math.Sqrt(2.0),1.14111692/Math.Sqrt(2.0),0.650365/Math.Sqrt(2.0),-0.19093442/Math.Sqrt(2.0),-0.12083221/Math.Sqrt(2.0),0.0498175/Math.Sqrt(2.0)},
      new double[] {0.32580343/Math.Sqrt(2.0),1.01094572/Math.Sqrt(2.0),0.8922014/Math.Sqrt(2.0),-0.03957503/Math.Sqrt(2.0),
              -0.26450717/Math.Sqrt(2.0),0.0436163/Math.Sqrt(2.0),0.0465036/Math.Sqrt(2.0),-0.01498699/Math.Sqrt(2.0)},
      new double[] {0.22641898/Math.Sqrt(2.0),0.85394354/Math.Sqrt(2.0),1.02432694/Math.Sqrt(2.0),0.19576696/Math.Sqrt(2.0),
              -0.34265671/Math.Sqrt(2.0),-0.04560113/Math.Sqrt(2.0),0.10970265/Math.Sqrt(2.0),-0.0088268/Math.Sqrt(2.0),
              -0.01779187/Math.Sqrt(2.0),4.72E-03/Math.Sqrt(2.0)}
    };

    static void Main(string[] args)
    {
      int totalLength = 1024;  //データ数(2のべき乗)
      int maxLevel = 2;   //分解レベル
      int N = 1;          //次数
      int kMax = 2 * N;
      int daubechiesIndex = N - 1;

      double[] inputSignal = new double[totalLength];
      double[] outputSignal = new double[totalLength];

      //確認用に入力データをCSVファイル出力
      System.IO.StreamWriter swriter = new System.IO.StreamWriter("input.csv");
      //初期化
      for (int signalIndex = 0; signalIndex < totalLength; signalIndex++)
      {
        inputSignal[signalIndex] = 10.0 * Math.Sin(signalIndex * 2.0 * Math.PI / (totalLength / 2.0))
                      + 1.0 * Math.Sin(signalIndex * 2.0 * Math.PI / (totalLength / 200.0));
        outputSignal[signalIndex] = 0.0;
        swriter.WriteLine(inputSignal[signalIndex]);
      }
      swriter.Close();

      for (int level = 1; level <= maxLevel; level++)
      {
        int scale = 1;
        for (int i = 0; i < level; i++)
        {
          scale *= 2;
        }

        //ダウンサンプリング
        for (int index = 0; index < totalLength / scale; index++)
        {
          int setApproximationIndex = index;
          int setDetailIndex = index + totalLength / scale;

          outputSignal[setApproximationIndex] = 0.0;
          outputSignal[setDetailIndex] = 0.0;

          for (int k = 0; k < kMax; k++)
          {
            int getInputIndex = 2 * index + k;

            if (getInputIndex >= 2 * totalLength / scale)
            {
              //配列外にデータにアクセスする際に折り返しを行う
              int over = getInputIndex % (2 * totalLength / scale) + 1;
              getInputIndex = (2 * totalLength / scale) - over;
            }

            //フィルタリング
            outputSignal[setApproximationIndex] += inputSignal[getInputIndex] * DaubechiesCoefficients[daubechiesIndex][k];
            outputSignal[setDetailIndex] += inputSignal[getInputIndex] * Math.Pow(-1.0, k) * DaubechiesCoefficients[daubechiesIndex][kMax - 1 - k];
          }
        }

        //配列コピー
        Array.Copy(outputSignal, inputSignal, totalLength);

        //結果出力
        swriter = new System.IO.StreamWriter("signalLevel"+level.ToString()+".csv");
        for (int signalIndex = 0; signalIndex < totalLength / scale; signalIndex++)
        {
          swriter.WriteLine(outputSignal[signalIndex] + "," + outputSignal[signalIndex + totalLength / scale]);
        }
        swriter.Close();
      }
    }
  }
}

解説

入力信号作成

前回の記事同様に、
振幅10, 2Hzの正弦波と、
振幅1 , 100Hzの正弦波を足し合わせた信号を作成。

      System.IO.StreamWriter swriter = new System.IO.StreamWriter("input.csv");
      //初期化
      for (int signalIndex = 0; signalIndex < totalLength; signalIndex++)
      {
        inputSignal[signalIndex] = 10.0 * Math.Sin(signalIndex * 2.0 * Math.PI / (totalLength / 2.0))
                      + 1.0 * Math.Sin(signalIndex * 2.0 * Math.PI / (totalLength / 200.0));
        outputSignal[signalIndex] = 0.0;
        swriter.WriteLine(inputSignal[signalIndex]);
      }
      swriter.Close();
||
フィルタリングとダウンサンプリング

離散ウェーブレット変換は、元の信号x(n)を下の図のように、
ハイパスとローパスの線形フィルタリングを実行。
その後フィルタリング処理データを1点おきに取得して、
信号をダウンサンプリング。
f:id:s_sikisya:20140122131835p:plain
このときに表れた低周波信号をApproximation
もう一方の高周波信号をDetailと呼ぶ。

そして指定した分解レベルになるまで、
低周波信号に対して再帰的に
フィルタリングとダウンサンプリングを実行。
f:id:s_sikisya:20140122130900p:plain

最終的に得られた信号列

Approximation Level N, Detail Level Detail N, Level N-1, … , Detail Level 2, Detail Level 1

Scilabの以下の式で求めたウェーブレット係数Cに該当します。

[C,L]=wavedec(wave,2,"haar");

フィルタリング式

これで離散ウェーブレット変換は入力信号から、
ApproximationとDetailを求めるフィルタリング式さえあれば、
順次求めることができそう。

フィルタリング式は、
DaubechiesWaveletの場合それぞれ、

{ \displaystyle
g_n = \sum_{k=0}^{2N-1} a_k x(n-k)
}

{ \displaystyle
h_n = \sum_{m=0}^{2N-1} b_k x(n-k)
}

となります。

Daubechies係数

また係数には以下の関係式が成り立つ。

{ \displaystyle
b_k = {(-1)}^{m} a_{2N-1-k}
}

{ \displaystyle
\sum_{m=0}^{2N-1} a_k = \sqrt{2}
}

{ \displaystyle
\sum_{m=0}^{2N-1} a_k^2 = 1
}

このDaubechies係数を求める方法があるようですが、
一般的には既に求められているのを使う模様。
英語版Wikipediaに次数N=10の場合までの係数が用意されています。

a_k D2 (Haar) D4 D6 D8 D10
0 1 0.6830127 0.47046721 0.32580343 0.22641898
1 1 1.1830127 1.14111692 1.01094572 0.85394354
2 0.3169873 0.650365 0.8922014 1.02432694
3 -0.1830127 -0.19093442 -0.03957503 0.19576696
4 -0.12083221 -0.26450717 -0.34265671
5 0.0498175 0.0436163 -0.04560113
6 0.0465036 0.10970265
7 -0.01498699 -0.0088268
8 -0.01779187
9 0.004717428

Daubechies wavelet - Wikipedia, the free encyclopediaより一部(N=5まで)引用

注意
この表は係数の二乗和が2で正規化されているので、
全てルート2で割った値を使用すること。

結果出力

上記プログラムは分解レベル毎に
ApproximationとDetailをCSVファイルで保存しています。
ウェーブレット変換結果をExcelでプロットとした図。

レベル1
f:id:s_sikisya:20140205171527p:plain
レベル2
f:id:s_sikisya:20140205171546p:plain

以前行ったScilabで離散ウェーブレット変換
での結果同様にLevel2の段階でDetailに高周波成分が出てきています。
f:id:s_sikisya:20140117131947p:plain
サンプリングができない点の処理(プログラムでは折り返し)が、
異なるのかデータの端だけScilabと値が異なるけど今回は気にしない方向で。

参考
離散ウェーブレット変換 - Wikipedia
Daubechies wavelet - Wikipedia, the free encyclopedia
直交ウェーブレット変換について

AndroidはKitKat以降アプリからSDカードへの書き込み不可に?

2014/01/30 18:30 内容一部修正

KitKat端末では今後アプリからMicro SDカードへの書き込みができくなる模様 | juggly.cn
この記事もうちょっと詳しく調べてみた。

情報元ソース

元ソースはGoogle+Google社のTod Liebeck氏書いた内容

KitKat will make your SD Card completely useless: per the Android API specification, apps can no longer write files to your SD card. And Samsung is following it.

This only applies to dual-storage devices, i.e., devices with a user-writable internal flash storage AND a removable SD card.


From http://source.android.com/devices/tech/storage/index.html:

"The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions."


If your device has user-accessible internal flash storage, your SD Card is a "secondary external storage device".


What this means is that with KitKat, applications will no longer be able create, modify, or remove files and folders on your external SD card. As a for-instance, you can no longer use a file manager to copy files from your computer to the SD card over a network. This ability, which has existed since the beginning of Android, has been taken away.


The only stated reason for this removal of functionality is that, "Restricting writes in this way ensures the system can clean up files when applications are uninstalled." I do not pretend to understand this logic. Apps are still allowed to write in arbitrary directories on the primary storage (with the appropriate permission), but are denied the same access to external storage.


Samsung has implemented this feature with their KitKat OTA updates. Note3 users are now complaining that FX File Explorer can no longer write to their external SD cards. There are solutions to this problem for users with root access. Users without root access appear to be screwed.

I'm not quite certain how Google intends for you to place files on your SD card. Perhaps you have to use proprietary Google apps that contain permissions unavailable to the rest of the developer world. Perhaps you're supposed to put everything on the cloud and pay carrier data fees to get it there. Perhaps you're supposed to use some kind of WIRE to attach your WIRELESS device to your computer and have the computer do that work for you.


In my opinion this is a horrible misstep by Google and the Android Open Source Project. Functionality has been removed without reason, to the severe detriment of users and developers alike.


I apologize for not bringing this to everyone's attention when KitKat 4.4 was released, but it was not mentioned in the Android 4.4 changes document: http://developer.android.com/about/versions/android-4.4.html. It's only mentioned in the article on source.android.com. I was only made aware of its existence from user reports as a result of Samsung implementing this change in its KitKat OTA updates.

https://plus.google.com/+TodLiebeck/posts/gjnmuaDM8sn

要約

翻訳ミスがあるかもしれないので注意してね。

KitKatから仕様変更
  • アプリはSDカードへの書き込みが完全にできなくなる
  • (例)ファイルマネージャーアプリでファイルをSDカードに保存できなくなる
  • サムスンはGALAXY Note3のアップデートで追従した
デュアルストレージデバイスが対象
  • 対象はフラッシュストレージと取り外し可能なSDカードを持つデバイス
  • WRITE_EXTERNAL_STORAGEパーミッションでは、主要なストレージのパッケージ固有ディレクトリにしか書き込めない
  • WRITE_EXTERNAL_STORAGEパーミッションがあれば主要なストレージ内のどこにでも書き込みが可能 (2014/01/30 18:30 修正)
  • SDカードは2番目の外部ストレージになり書込みが許可されない
  • SDカードは2番目の外部ストレージになりパッケージ固有ディレクトリにしか書込みが許可されない (2014/01/30 18:30 修正)
  • root権限がないと回避できない
仕様決定理由
  • 書込みを制限させ、アプリのアンインストール時に完全にクリーンアップできるから
Tod Liebeck氏は不満
  • 今後Googleがどうしたいかはわからない
  • でも代価としてクラウドサービスとかを用意して金取るかも
  • オープンソースプロジェクトとして失策
  • 変更内容に書かれてなくてサムスンのアップデートで知ったごめん

修正について

完全に書き込みができないのではなく、
SD内のアプリケーションのパッケージ固有ディレクトリ[SD\Android\data\パッケージ名\]
なら保存できるってのが正確な仕様のようです。

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.

External Storage Technical Information | Android Developers

Tod Liebeck氏が

KitKat will make your SD Card completely useless

KitKatではSDカードが完全に使えなくなる」
って書いていて矛盾してるのは記述ミスなんだろうか?

ILSpyを使ってC#プログラムをリバースエンジニアリング

C#の実行プログラムやDLLはそのままだと、
リバースエンジニアリングし放題ってことの確認。

シーザー暗号プログラムテスト

今回はテストとして、入力文字列をシーザー暗号で
暗号化と復号化を行うプログラムを作成しました。
その後リバースエンジニアリングで、
プログラムがどう見えるのか確認してみます。

シーザー暗号とは

シーザー暗号(シーザーあんごう、英語:Caesar cipher)は、単一換字式暗号の一種で、平文の各文字を辞書順に3文字だけシフトして暗号文をつくる暗号である。カエサル暗号とも呼ばれる。文字のシフト数は固定であるが、3に限る必要はなく、シフト数を3以外にした方式もシーザー暗号(あるいはシフト暗号)と呼ぶことがある。

参考:シーザー暗号 - Wikipedia

シーザー暗号プログラム

CaesarTest.cs
using System;

namespace Encryption
{
  static class CaesarTest
  {
    private static int rool = 3;

    private static string Encryption(string input)
    {
      string ret = "";
      foreach (char c in input)
      {
        ret += ((char)(c + rool)).ToString();
      }
      return ret;
    }

    private static string Decryption(string input)
    {
      string ret = "";
      foreach (char c in input)
      {
        ret += ((char)(c - rool)).ToString();
      }
      return ret;
    }

    public static void Main()
    {
      string input;
      string encrypted;
      string decrypted;

      //復号化
      Console.WriteLine("入力した文字を暗号化します。");
      input = Console.ReadLine();
      encrypted = Encryption(input);
      Console.WriteLine("暗号化データ:" + encrypted);

      //復号化
      Console.WriteLine("入力した文字を復号化します。");
      input = Console.ReadLine();
      decrypted = Decryption(input);
      Console.WriteLine("復号化データ:" + decrypted);

      Console.ReadLine();
    }
  }
}
実行例

入力した文字を暗号化します。
password
暗号化データ:ufxx|twi
入力した文字を復号化します。
ufxx|twi
復号化データ:password

Encryption(暗号化)で「password」が「sdvvzrug」に、
Decryption(復号化)で「sdvvzrug」が「password」として出力されました。

ILSpyでリバースエンジニアリング

ILSpyのダウンロード

http://ilspy.netから画面上部のDownload Binariesを選択し、
ILSpyをダウンロード。

2013/01/29時点 最新 Version 2.1.0.1603

ILSpyの実行

ダウンロードしたファイルを解凍し、
「ILSpy.exe」を起動。

メニューバーの[File]→[開く]から、
今回作成したプログラムの実行ファイルを選択。

左側のツリービューに追加された
「Encryption」を展開していくと…

f:id:s_sikisya:20140129201156p:plain

ソースの中身がほぼ丸見え!
このままではアルゴリズムがバレバレで暗号化の意味が無い。

三角形ボタンの作り方(C# Form)

C# Formアプリケーションで、
上下左右や、進む・戻る等を指定するときに便利な三角形ボタンの作り方。

雑なやり方(非推奨)

標準のボタンをFormに貼り付けて、
テキストに三角形の記号文字を指定。

public Form1()
{
  InitializeComponent();
  button1.Text = "▲";
}

しかしこれだと右向きや左向きの三角形の場合特殊文字を使うため、
表示されない可能性もあります。

ボタン内に三角形を描画するやり方

System.Windows.Forms.Buttonクラスを継承し、
ボタン内に三角形を描画するように拡張を行います。

Button継承クラスの作成

Buttonクラスを継承した以下のクラスを作成します。

ButtonBGTriangle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace ButtonTriangleTest
{
  public partial class ButtonBGTriangle : System.Windows.Forms.Button
  {
    //三角形の方向指定用列挙体
    public enum TriangleDirection
    {
      Up = 1,
      Down = 2,
      Left = 4,
      Right = 8,
    }

    //三角形の方向指定プロパティ
    private TriangleDirection TRIANGLE_DIRECTION = TriangleDirection.Left;
    public TriangleDirection Direction
    {
      get { return this.TRIANGLE_DIRECTION; }
      set
      {
        this.TRIANGLE_DIRECTION = value;
        this.Invalidate();
      }
    }

    //三角形の塗りつぶし色プロパティ
    private Color TRIANGLE_COLOR = System.Drawing.SystemColors.ControlText;
    public Color TriangleColor
    {
      get { return this.TRIANGLE_COLOR; }
      set
      {
        this.TRIANGLE_COLOR = value;
        this.Invalidate();
      }
    }

    public ButtonBGTriangle()
    {
      this.Text = "";
    }

    protected override void OnTextChanged(EventArgs e)
    {
      base.OnTextChanged(e);
      //テキストを空に変更し無効化
      this.Text = "";
    }

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
      base.OnPaint(e);

      Graphics g = e.Graphics;
      g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
      PointF[] drawPoint = new PointF[3];

      //TRIANGLE_DIRECTIONに応じて三角形を描画
      switch (TRIANGLE_DIRECTION)
      {
        case TriangleDirection.Up:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) / 2.0f, (this.ClientSize.Height - 1.0f) / 4.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) / 6.0f, (this.ClientSize.Height - 1.0f) * 3.0f / 4.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) * 5.0f / 6.0f, (this.ClientSize.Height - 1.0f) * 3.0f / 4.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
        case TriangleDirection.Down:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) / 2.0f, (this.ClientSize.Height - 1.0f) * 3.0f / 4.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) / 6.0f, (this.ClientSize.Height - 1.0f) / 4.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) * 5.0f / 6.0f, (this.ClientSize.Height - 1.0f) / 4.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
        case TriangleDirection.Left:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) * 3.0f / 4.0f, (this.ClientSize.Height - 1.0f) / 6.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) * 3.0f / 4.0f, (this.ClientSize.Height - 1.0f) * 5.0f / 6.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) / 4.0f, (this.ClientSize.Height - 1.0f) / 2.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
        case TriangleDirection.Right:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) / 4.0f, (this.ClientSize.Height - 1.0f) / 6.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) / 4.0f, (this.ClientSize.Height - 1.0f) * 5.0f / 6.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) * 3.0f / 4.0f, (this.ClientSize.Height - 1.0f) / 2.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
      }
    }
  }
}

これを作成すると、
こんな感じ↓のボタンコンポーネントが作れます。

f:id:s_sikisya:20140129114952p:plain

プロパティ

三角形の方向と塗りつぶし色はプロパティで設定します。

Direction:三角形の向き(Up,Down,Left,Right)
TriangleColor:三角形のカラー

オープンソースのウェーブレット変換ライブラリ

C言語系でウェーブレット変換を行うライブラリを調査。
GPLライセンスのライブラリなので、
組み込んだ際の配布時にはご注意を。

Geophysical Wavelet Library

http://users.math.uni-potsdam.de/~gwl/

blitzwave C++ Wavelet Library

http://oschulz.github.io/blitzwave/

Signal Processing Library in C++

https://code.google.com/p/tspl/

GNU Scientific Library

http://www.gnu.org/software/gsl/manual/html_node/Wavelet-Transforms.html

GPLに接触したくないなら、
1次元連続ウェーブレット変換限定になりますが、
以下を参考にするとよさげ。

C/C++言語でガボールウェーブレット変換により時間周波数解析を行うサンプルプログラム
http://hp.vector.co.jp/authors/VA046927/gabor_wavelet/gabor_wavelet.html
FFTを使った連続ウェーブレット変換の高速化
http://r9y9.github.io/blog/2013/10/20/continuous-wavelet-tranform/

C#でCOMポート番号とシリアル接続機器名を同時に取得する方法

マイコン機器とデータ通信を行う方法としては、
シリアル通信(RS-232)を行うことがメジャーだと思います。

.NET Framework 2.0以降ではSerialPortクラスが実装され、
シリアル通信を行うプログラムを書くのがお手軽になりました。
http://msdn.microsoft.com/ja-jp/library/system.io.ports.serialport(v=vs.110).aspx

有効なCOM番号の取得

シリアル機器とPCを接続しているときに、
SerialPortクラスのGetPortNameクラスを呼べば、
現在有効なCOM番号が出てきます。

ShowSerialPortNum.cs
using System;
using System.IO.Ports;

namespace ShowSerialPortNum
{
  class ShowSerialPortNum
  {
    public static void Main()
    {
    string[] ports = SerialPort.GetPortNames();
    foreach(string port in ports)
    {
      Console.WriteLine(port);
    }
    Console.ReadLine();
    }
  }
}

出力例(COM8が有効なとき)

COM8

接続している機器が1台だけなら気にせずそこに繋げればOK。
でも複数台接続をしていたらどのCOM番号が何と接続しているのかわからない。

そんなときは、
マイコンピュータ右クリック」→「プロパティ」
→「デバイスマネージャ」→「ポート(COMとLPT)」

とデバイスマネージャを参照して確認する必要があり、
ユーザーに対して不親切だし非常にメンドクサイ。

頼みのSerialPortクラスもそこまでは教えてくれないので、
頑張って別の方法を使って探しに行きます。

COM番号と接続機器名の取得

方法としてはWMIのWin32_PnPEntityクラスを使って、
上記したデバイスマネージャに記述されている内容を拾いに行きます。

ShowSerialPortName.cs
using System;
using System.Management;

namespace ShowSerialPortName
{
  class ShowSerialPortName
  {
    public static void Main()
    {
      string[] ports = GetDeviceNames();
      if(ports != null)
      {
        foreach (string port in ports)
        {
          Console.WriteLine(port);
        }
      }
      Console.ReadLine();
    }

    public static string[] GetDeviceNames()
    {
      var deviceNameList = new System.Collections.ArrayList();
      var check = new System.Text.RegularExpressions.Regex("(COM[1-9][0-9]?[0-9]?)");

      ManagementClass mcPnPEntity = new ManagementClass("Win32_PnPEntity");
      ManagementObjectCollection manageObjCol = mcPnPEntity.GetInstances();

      //全てのPnPデバイスを探索しシリアル通信が行われるデバイスを随時追加する
      foreach (ManagementObject manageObj in manageObjCol)
      {
        //Nameプロパティを取得
        var namePropertyValue = manageObj.GetPropertyValue("Name");
        if (namePropertyValue == null)
        {
          continue;
        }

        //Nameプロパティ文字列の一部が"(COM1)~(COM999)"と一致するときリストに追加"
        string name = namePropertyValue.ToString();
        if (check.IsMatch(name))
        {
          deviceNameList.Add(name);
        }
      }

      //戻り値作成
      if (deviceNameList.Count > 0)
      {
        string[] deviceNames = new string[deviceNameList.Count];
        int index = 0;
        foreach (var name in deviceNameList)
        {
          deviceNames[index++] = name.ToString();
        }
        return deviceNames; 
      }
      else
      {
        return null;
      }
    }
  }
}

出力例(COM8にArduino Unoを接続しているとき)

Arduino Uno (COM8)

やっていることとしては、
ManagementClassのコンストラクタ
Win32_PnPEntityクラスを指定して、
GetInstances()メソッドを呼び出すことで、
プラグアンドプレイデバイスのコレクション配列を取得

ManagementClass mcPnPEntity = new ManagementClass("Win32_PnPEntity");
ManagementObjectCollection manageObjCol = mcPnPEntity.GetInstances();

コレクションの要素を順次アクセスし、
GetPropertyValue関数でNameプロパティを取得。
Nameプロパティで得られたオブジェクトが、
デバイスマネージャ上で表示される値みたいです。
その後正規表現で"(COM1-999の番号)"が含まれるデータのみを抽出。

foreach (ManagementObject manageObj in manageObjCol)
{
  //Nameプロパティを取得
  var namePropertyValue = manageObj.GetPropertyValue("Name");
  if (namePropertyValue == null)
  {
    continue;
  }

  //Nameプロパティ文字列の一部が"(COM1)~(COM999)"と一致するときリストに追加"
  string name = namePropertyValue.ToString();
  if (check.IsMatch(name))
  {
    deviceNameList.Add(name);
  }
}

これでデバイス名とCOM番号を同時に取得します。
COM番号は文字列から数値を抜き出せばOKです。

参考
http://www-ice.yamagata-cit.ac.jp/ken../senshu/sitedev/index.php?AVR%2Favrdude36#xceb3d84
http://www.wmifun.net/library/win32_pnpentity.html

Androidで外部保存域の空き容量を取得する

Androidで無造作にデータを保存して、
空き容量が無いってことをしないために。

検索でよく引っかかったのが
StatFsクラスのgetAvailableBlocksメソッドを使う以下の例

long freeByteSize = 0;
String state = Environment.getExternalStorageState();

//外部保存領域が使用可能かチェック
if(Environment.MEDIA_MOUNTED.equals(state) || 
    Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
{
  //サイズ取得
  StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
  freeByteSize = (long)statFs.getBlockSize() * (long)statFs.getAvailableBlocks();
}

しかしこのソースEclipse(ADT)では、

メソッド getBlockSize() は型 StatFs で使用すべきではありません
メソッド getAvailableBlocks() は型 StatFs で使用すべきではありません

と警告を受けます。

警告は無視しても作れますが気になるので別の方法を調べたところ、
どうやらAndroid API 9からは FileクラスのgetFreeSpaceが使えるらしい。

long freeByteSize = 0;
String state = Environment.getExternalStorageState();

//外部保存領域が使用可能かチェック
if(Environment.MEDIA_MOUNTED.equals(state) || 
    Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
{
  //サイズ取得
  freeByteSize = Environment.getExternalStorageDirectory().getFreeSpace();
}

これで警告も消えて一安心。

参考
http://stackoverflow.com/questions/8133417/android-get-free-size-of-internal-external-memory
http://www.techdoctranslator.com/android/guide/data-storage