ラベル プログラミング の投稿を表示しています。 すべての投稿を表示
ラベル プログラミング の投稿を表示しています。 すべての投稿を表示

2014/08/11

[プログラミング]Pythonでのデータ処理と、numpy.log2(4096)≠12.0になった話

,

概要

・Pythonで、xlsxを用いてExcelファイルを読み込んだ。
・numpyを用いてFFT、pylabで結果を表示した。
・np.log2()の精度?に疑問が生じた。

ExcelファイルからFFTする

データ処理にPythonを使おうとしています。今は、4096個の電圧データをFFT(高速フーリエ変換、Fast Fourier Transform)しようとしています。Pythonには、numpy、scipy、pylabといった強力なライブラリが揃っているので、あまり自分で実装しなくとも、簡単にFFTなど演算が出来ます。楽でいいですね。

 今回は、"testdata.xlsx"というExcelファイルにデータが収まっています。このファイルのSheet1、1列目の1~4096行目に、小数でデータが入っています。この時、これらを取り出して、numpyでFFTを行い、pylabで表示するだけなら以下のスクリプトで出来ます。
# -*- coding: utf-8 -*-

__author__ = 'fenrir_'

import xlrd
import numpy as np
import math
from pylab import *


def rad_to_deg(rad):
    return rad / np.pi * 180


if __name__ == "__main__":
    # Excelからデータを読み出す。Sheet1の一番左の行。
    filename = r"C:\Users\******\testdata.xlsx"
    book = xlrd.open_workbook(filename)
    sheet_1 = book.sheet_by_index(0)

    # PythonのListから、numpy.ndarrayに変換
    col_array_in_numpy = np.array(sheet_1.col_values(0))
 
    # 以後、FFTの処理
    start = 0
    N = 4096    # データ点数
    fs = 2000000

    # Hanning窓を掛ける
    hanning_window = np.hanning(N)
    windowed_data = hanning_window * col_array_in_numpy
    fft_result = np.fft.fft((windowed_data[start:start + N]))

    freq_list = np.fft.fftfreq(N, 1.0 / fs)

    # 振幅・位相のスペクトルを求める。
    amplitude_spectrum = [np.sqrt(c.real ** 2 + c.imag ** 2) for c in fft_result]
    phase_spectrum = [rad_to_deg(np.arctan2(int(c.imag), int(c.real))) for c in fft_result]

    # 元データの波形を表示する
    subplot(311)
    plot(range(start, start + N), col_array_in_numpy[start:start + N])
    axis([start, start + N, np.min(col_array_in_numpy), np.max(col_array_in_numpy)])
    xlabel("time [sample]")
    ylabel("amplitude")

    # 振幅スペクトルを表示する
    subplot(312)
    plot(freq_list, amplitude_spectrum, marker='o', linestyle='-')
    axis([0, fs / 2, np.min(amplitude_spectrum), np.max(amplitude_spectrum)])
    yscale("log")  # 縦軸は対数軸にする
    xlabel("frequency [Hz]")
    ylabel("amplitude spectrum")

    # 位相スペクトルを表示する
    subplot(313)
    plot(freq_list, phase_spectrum, marker='o', linestyle='-')
    axis([0, fs / 2, -180, 180])
    xlabel("frequency [Hz]")
    ylabel("phase spectrum")

    show()
結果、以下のようなグラフが生成されます。(振幅スペクトルは、まだ最大値を基準としたdB表示にはしていません)。 
窓関数なしの場合の、入力データ(上段)と、そのFFT結果
Hanning窓を適用させた場合の、入力データ(上段)と、そのFFT結果
わあ、めっちゃ便利ですね。

一部データを抜き出す処理を加える

さて、今回はデータ数Nは4096でしたが、これが異なる点数の時を考えます。この際、考慮すべきことが1つあります。それは、FFTに使うデータの個数です。というのも、FFTではNは2の累乗である(N = 2^n)ことが望ましいです。なので、N≠2^nであった場合、FFTに用いるデータ数は2^nとなるように、一部を抜き出さねばなりません。例えば、データ数N = kである場合、kよりも小さく、一番大きな2の累乗の数だけ、データを抜き出したくなります。
 実際には、log(2, k)以下の一番大きな整数Nを求めれば、log(2, k) > Nよりk > 2^Nとなります。よく対数の練習問題にあるやつですね。なので、このNを求める処理を加えます。
 Pythonで関数として実装すると、以下のようになりました。
def floor_2_pow(value):
  # 2**N < log2(value) < 2**N+1となるような整数を返す。つまり、valueに一番近い2**N
    log_2_value = np.log2(value)
    exponentiation = np.floor(log_2_value)
    # print "value : %10.24f exponentiation : %10.24f" % (log_2_value, exponentiation)
    return 2 ** exponentiation

 さて、これを書いて実行していたのですが、value = 4096の時、2^11 = 2048が返ってきてしまいました。デバッグして各変数を見てみると、log_2_valueは12.0(float64)ですが、exponentiationが11.0になっていました。floor(n)は、n以下の最大の整数を求める関数ですから、11.0ではなく12.0が入っているはずです。実際に、
    exponentiation = np.floor(np.float64(12.0))
とすれば、exponentiationは12.0になります。なので、問題はnp.floor()以前、np.log2()にありそうです。

 ここで、浮動小数点では丸め誤差などの誤差がよく問題になることを思い出しました。なので、np.log2(4096)の返り値の表示精度を上げてみました(コメントアウトされているprint文の、コメントを外した)。その結果は以下のようになりました。
log_2_value : 11.999999999999998223643161 exponentiation : 11.000000000000000000000000
 これを見ると、np.log2(4096)が実は12.0ではなく、11.999999…を返していたようです。デバッガのWatch窓は小数第一位までしか表示しないので、私が12.0だと勝手に勘違いしていたみたいです。問題はここにあったようです。

検証

さて、本当にnp.log2()が悪いんでしょうか。よく分からなかったので、幾つかの値でnp,log2()をチェックしてみました。以下のスクリプト(verification_log2.py)を用いました。
# -*- coding: utf-8 -*-
__author__ = 'fenrir_'

import math
import numpy as np

if __name__ == "__main__":
    # np.log2()が返す値を検証する。
    for num in range(1, 20):
        val = 2 ** num
        print "np.log2(%d)      : %10.24f  math.log(%d, 2.0)   : %10.24f" % (val, np.log2(val), val, math.log(val, 2.0))

    # np.log10()が返す値を検証する
    for num in range(0, 20):
        print "np.log10(%d)       : %10.24f" % (num, np.log10(10 ** num))
その結果、以下のような結果が得られました。
np.log2(2)      : 1.000000000000000000000000  math.log(2, 2.0)   : 1.000000000000000000000000
np.log2(4)      : 2.000000000000000000000000  math.log(4, 2.0)   : 2.000000000000000000000000
np.log2(8)      : 2.999999999999999555910790  math.log(8, 2.0)   : 3.000000000000000000000000
np.log2(16)      : 4.000000000000000000000000  math.log(16, 2.0)   : 4.000000000000000000000000
np.log2(32)      : 5.000000000000000000000000  math.log(32, 2.0)   : 5.000000000000000000000000
np.log2(64)      : 5.999999999999999111821580  math.log(64, 2.0)   : 6.000000000000000000000000
np.log2(128)      : 6.999999999999999111821580  math.log(128, 2.0)   : 7.000000000000000000000000
np.log2(256)      : 8.000000000000000000000000  math.log(256, 2.0)   : 8.000000000000000000000000
np.log2(512)      : 9.000000000000000000000000  math.log(512, 2.0)   : 9.000000000000000000000000
np.log2(1024)      : 10.000000000000000000000000  math.log(1024, 2.0)   : 10.000000000000000000000000
np.log2(2048)      : 11.000000000000000000000000  math.log(2048, 2.0)   : 11.000000000000000000000000
np.log2(4096)      : 11.999999999999998223643161  math.log(4096, 2.0)   : 12.000000000000000000000000
np.log2(8192)      : 12.999999999999998223643161  math.log(8192, 2.0)   : 13.000000000000000000000000
np.log2(16384)      : 13.999999999999998223643161  math.log(16384, 2.0)   : 14.000000000000000000000000
np.log2(32768)      : 15.000000000000000000000000  math.log(32768, 2.0)   : 15.000000000000000000000000
np.log2(65536)      : 16.000000000000000000000000  math.log(65536, 2.0)   : 16.000000000000000000000000
np.log2(131072)      : 17.000000000000000000000000  math.log(131072, 2.0)   : 17.000000000000000000000000
np.log2(262144)      : 18.000000000000000000000000  math.log(262144, 2.0)   : 18.000000000000000000000000
np.log2(524288)      : 19.000000000000000000000000  math.log(524288, 2.0)   : 19.000000000000000000000000

 うーん、np.log2()はところどころ期待した結果が得られてないようです。math.log()の方は、ちゃんと出てますね。ちなみに、np.log10()ではこのようなことは起きないようです。

 これについては、Stack Overflowでも話題になったみたいです。確かに、浮動小数点の精度は有限ですね。
Python NumPy log2 vs MATLAB - Stack Overflow http://stackoverflow.com/questions/17702065/python-numpy-log2-vs-matlab

対策

この誤差は、np.log2()の求解アルゴリズムに依るものなのでしょうか。丸め誤差でしょうか。今のところ、この原因はよく分からないです(私が無知なので、調べてもない)。とにかく、このままではnp.log2(2^n)≠nとなることがあるようなので、value = 2^nの時はnp.log2()を使わないようにしました。処理としては、「valueが2で割り切れるならば、valueを返す」です。最終的に、コードは以下のようになりました。
def is_dividable_by_2(value):
    while True:
        if value % 2 != 0:
            return False
        elif value == 2:
            return True
        else:
            value /= 2


def floor_2_pow(value):
    # 2**N < log2(value) < 2**N+1となるような整数を返す。つまり、valueに一番近い2**N
    if is_dividable_by_2(value):
        return value
    else:
        log_2_value = np.log2(value)
        exponentiation = np.floor(log_2_value)
        # print "value : %10.24f exponentiation : %10.24f" % (log_2_value, exponentiation)
        return 2 ** exponentiation

参考サイト

・高速フーリエ変換(FFT) - 人工知能に関する断創録 http://aidiary.hatenablog.com/entry/20110618/1308367728
 numpyでのFFTの仕方が載っています。このサイトは、Pythonでの機械学習やデータ処理など、色々有用な例が豊富にあって、とても勉強になります。

・numpy.floor — NumPy v1.8 Manual http://docs.scipy.org/doc/numpy/reference/generated/numpy.floor.html
 numpy.floor()のReferenceページです。numpy.floor(2.0)は2.0だって、例に書いてありますね。5回くらい「えっ本当だよね?」って見返してしまいました…。

・ライブラリ:unittest - Life with Python http://www.lifewithpython.com/2014/03/unittest.html
 Pythonでのテストの書き方が掲載されています。ちなみに、main.pyにある関数をtest.pyで使いたい場合は、from main import *と書けばいいみたいです。

・Creating Tests http://www.jetbrains.com/pycharm/webhelp/creating-tests.html
 私が使っているIDE「PyCharm」のHelpです。テストの作り方が載っています。Ctrl + Shift + Tを押すだけです。楽でいいですね…。

・高速フーリエ変換 - Wikipedia http://ja.wikipedia.org/wiki/%E9%AB%98%E9%80%9F%E3%83%95%E3%83%BC%E3%83%AA%E3%82%A8%E5%A4%89%E6%8F%9B 
 結局のところ、FFTのデータ数Nは、2の累乗「でなければいけない」のか、「であれば一番早い(他の値でもよし)」なのか、どっちなのか。こういう厳密なところを全然おさえられていないので、頭の悪さ、無知といったものが露呈してしまいますね…。
Read more →

2014/05/24

[プログラミング]Pythonの環境を整えた。

,
 Pythonを始めることにしました。なので、環境を整えました。ですが、32bit/64bitの非互換性のためか、チュートリアルサイト通りには進まず、何箇所かハマってしまいました。なので、詰まったところのメモとして、この記事を書きました。


pipのインストール方法について


 Pythonには、モジュールと呼ばれるライブラリ群が沢山あります。私達は、これらの恩恵によって、すぐにプログラムを書いて、したいことを実行できるわけですね。

 ところで、ライブラリと聞くと、その管理方法をどうするかが疑問になります。例えばVC++だと、NuGetなるものがありました。これはコマンド一つでプロジェクトに導入できたり、自動でいろいろやってくれるので、あまり管理方法に悩まずに済むいいツールです。
 で、「Pythonではライブラリ管理は何を使えばいいんだー」と調べた所、「pip」なるものを見つけました。なので、とりあえずはこれを入れてみることにしました。

 pipのインストール方法ですが、Googleで検索すると「easy_installで入れるべき」だと書いてあるページが沢山出てきます。しかし、この方法はちょっと古いみたいです。私が参考にした以下のサイトのように、今はPythonがインストールしてあれば、get_pip.pyを実行することでpipを導入できるようです。

pipの使い方 (2014/1バージョン) — そこはかとなく書くよん。


Pythonがインストールされているのに、モジュールのインストーラーがそれを認識しない。


 PyAudioというモジュールをインストーラーでインストールしていたときに、「Python version 2.7 is required, which was not found in the registry」というエラーが出てしまい、数日間ここで悩んでました(投げた、ともいう)。

 この解決策なのですが、レジストリを64bit版の設定にすることらしいです。以下のサイトに記された通りにレジストリを書き換えたら、正常に認識されるようになりました。
Yamada Program: Pythonのsetuptools を 64bitマシンにインストール


pipからだと、64bit版のモジュールを入れられない?


 以下のサイトで、非公式に64bit版のモジュールを配布しているようです。ここからダウンロードしてインストールします。
Python Extension Packages for Windows - Christoph Gohlke

参考 :
64bitのWindowsでPythonの環境づくり - 唯物是真
windows7 64bit環境でのpython, opencvセットアップ等 - kohta blog


ライブラリ同士の依存関係が複雑


 最初、scipy、numpyをpipで入れようとしていましたが、インストール時にエラーが出てしまっていました。メッセージを見ると、どうやら何かモジュールが足りないようでした(今考えると、32bit版を入れようとしていたのが原因の気もします)。
 インストーラー(GUI)によるインストールなら、依存先のライブラリも一緒に入れてくれるのでしょうか?この事に関しては、私はあまり理解していません…。
 とりあえず、以下のものを先に入れていました。(他サイトではgfortranを入れるべきだと書いているのですが、自分は入れませんでした。)

 nose、pyparsing、python-dateutil


ブログを書いてみて


 Pythonとは関係無い、おまけの話です。
 モジュールの導入は、この週の頭くらいから詰まっていた問題でした。そして、だいたい木曜日くらいに、これらの解決策が見つかり、やっと不自由なく動作する環境が手に入りました。
 それで、この記事を書いているのは土曜日なのですが、もう何があったのか記憶が薄れてきているみたいです。特にエラーメッセージなどはそうで、ちゃんとメモしておかないと「どんなエラーだったのか」すらあやふやになってしまいます。そもそも、大体詰まった時はエラーメッセージをそのまま検索してみたりします。なので、

・エラーメッセージは、どこかに記録しておく。(出来ればスクリーンショットも撮る)
・解決策を見つけたら、そのURLや書籍の出典も、後で分かるようにメモを取る

これらの事は最低限しておくべきだなあと思いました。


 それから、他のブログページと比べると、大抵「自分の書いた文章は長くて読みにくいなあ」と思ってしまいます。「必要な情報を、簡潔に、分かりやすくまとめておく」方が、見やすいし理解の助けにもなるということは知っているのですが、実践するのはなかなか難しいです…。冗長的になってしまうのは、癖なのかもしれません。直したいところです。
Read more →

2014/01/04

[プログラミング]通貨のレート計算

,
 3つめは"A.2 Currency Calculator"です。通貨のレート計算ですね。問題文を読めば分かりますが、仕様は
  • 引数に10進数と、2つのISO 4217準拠の通貨コードみたいなのを与えるので、もう一つの通貨でいくらになるか計算する
  • プログラムで扱えない通貨コードならエラーを吐く
  • 引数が無い場合は、てきとーな解説を吐く
です。

むずかしいところ - 通貨レートの取得

さて、これを実現するのには一つ問題があります。それは「変動する通貨レートを、どうやって取得するか」です。問題の出題者さんは「1$ = \100だよね!」みたいな感じで軽く出題したのかもしれませんが…

 幸いにして、ネット上で最新の通貨レートのデータを取得できるサイトがあります。

クジラ 外国為替 確認 API (為替 RSS) - http://api.aoikujira.com/kawase/

 ほんとはこのデータ元のXurrencyのAPIを使おうと思っていたのですが、Pricingページに"only 29,99 eruos per year"と書かれてたので諦めました。

 なんとかXML形式なら扱えるだろう、ということで、今回はこれを使うことにします。

コード

とりあえずコード貼っておきます。
using System;
using System.Linq;
using System.Xml.Linq;

namespace Rate1
{
    class Program
    {
        /// <summary>
        /// 通貨リストを作成する関数。といっても面倒なので、currencies.xmlを作る。
        /// </summary>
        static void MakeCurrencyList()
        {
            // Currency List(Compliant to ISO 4217) can be get from http://api.aoikujira.com/kawase/
            // (you should replace ", " to "\", \"")

            //var currencyList = new List<string> {"eur", "gbp", "aud", "brl", "cad", "chf", "cny", "dkk", "hkd", "inr", "jpy", "krw", "lkr", "myr", "nzd", "sgd", "twd", 
            //                                "zar", "thb", "sek", "nok", "mxn", "bgn", "czk", "huf", "ltl", "lvl", "pln", "ron", "isk", "hrk", "rub", "try", "php", 
            //                                "cop", "ars", "clp", "svc", "tnd", "pyg", "mad", "jmd", "sar", "qar", "hnl", "syp", "kwd", "bhd", "egp", "omr", "ngn", 
            //                                "pab", "pen", "ils", "uyu", "usd"};

            //var savexml = new XElement("Currencies");
            //savexml.Add(currencyList.Select(item => new XElement("Currency", item.ToUpper())));
            //savexml.Save(@"currencies.xml");
        }

        static void Main(string[] args)
        {
            // 使用できる通貨リストを、currencies.xmlから読み込む。
            var path = @"currencies.xml";
            var currencyxml = XElement.Load(path);
            var currencyList = from currency in currencyxml.Elements()
                             select currency.Value;
                
            // 引数のチェックをする
            if (3 > args.Length)
            {
                Console.WriteLine("args < 3");
                return;
            }

            // パラメータ(価格、元の通貨、相手先の通貨)
            var price = 0.0;
            try
            {
                price = double.Parse(args[0]);
            }
            catch (FormatException fe)
            {
                Console.WriteLine(fe.Message);
                return;
            }

            var srcCurrency = args[1].ToUpper();
            var dstCurrency = args[2].ToUpper();
            // 扱ってない通貨を指定すると、前の変換結果が返ってくる。
            // なので、仮にこういうエラーチェックしてる。
            if(!(currencyList.Contains(srcCurrency))){ 
                Console.WriteLine(srcCurrency + @"は扱えないです");
                return;
            }else if(!(currencyList.Contains(dstCurrency))){
                Console.WriteLine(dstCurrency + @"は扱えないです");
                return;
            }

            // レート表のxmlをDLする
            var url = @"http://api.aoikujira.com/kawase/xml/" + srcCurrency.ToLower();
            var elem = XElement.Load(url);

            // xmlのkawase/resultがokでなかったらエラー
            if (elem.Element("result").Value != "ok")
            {
                Console.WriteLine("XML result isn't ok");
            }
            else
            {
                // xmlからレートを取得する
                var rates = from p in elem.Elements()
                            where p.Name.LocalName == dstCurrency
                            select p.Value;

                foreach (var rate in rates)
                {
                    // 結果を表示する(結果は1つのはず…)
                    try
                    {
                        Console.WriteLine("{0} {1} == {2} {3}", price, srcCurrency, price * double.Parse(rate), dstCurrency);
                    }
                    catch (FormatException fe)
                    {
                        Console.WriteLine(fe.Message);
                        return;
                    }
                }
            }
        }
    }
}

コードは大きく分けてMakeCurrencyList()とMain()の2つです。前者は、「プログラムが対応している通貨レートを保存しておくため、対応通貨を書いたXMLを作成する」関数です。それもネットから取得してくればいーじゃんと思うかもですが、

  • そもそも上のAPIは、Invalidな通貨を指定すると、前に取得が成功したデータが返ってくる(キャッシュ?)(/kawase/iiiとかにすると、その前に指定した/kawase/jpyとかが来た) → よって、返ってきたデータで通貨対応してるか判断できない
  • 1つの通貨を指定すると、対応した他の(54種の)通貨全てとのレートが出るけど、これを用いて対応通貨を取得するとしても、最初の1つは指定しないといけない
  • そもそもXurrencyも上のサイトも、対応通貨を別表で配布したりはしてない(HPにはテキストで書いてある、けどHTMLをDOMしたりするのも大変そう)
  • じゃあ元からデータで持っておこう
という話でした。"JPY"で他通貨取得の方がいいのかなあ…

 XML作成ですが、面倒だからxmlns名前空間とかは特に指定してないのですが、多分したほうがいいのですね…

var savexml = new XElement("Currencies");
savexml.Add(currencyList.Select(item => new XElement("Currency", item.ToUpper())));
savexml.Save(@"currencies.xml");
ここでは、XElementを用いて、
<Currencies>
<Currency>EUR</Currency>
<Currency>GBP</Currency>
</Currencies>
のようなXMLファイルを作成しています。最初にルートのCurrencies要素を作って、その子としてcurrencyListの各要素を追加しています。

// パラメータ(価格、元の通貨、相手先の通貨)
var price = 0.0;
try
{
    price = double.Parse(args[0]);
}
catch (FormatException fe)
{
    Console.WriteLine(fe.Message);
    return;
}
ここはargs[0]( = 元の通貨での額)をdoubleにしてます。Parse()出来なかった時のためにtry~catchしてますが、「tryスコープ中で変数を宣言すると、tryのスコープが終わった瞬間に見えなくなる」という厄介な問題があるので、最初にわざわざ書いてます。もしかしたらTryParse()の方がいいのかもしれない…

コーディング規則

オンリーワンなコーディング規則、多分あると思うんですけど、天下のMicrosoftさんが「こう書け!」って言ってるみたいですので、今回はこれになるべく沿うように書きました。

C# のコーディング規則 (C# プログラミング ガイド) - http://msdn.microsoft.com/ja-jp/library/ff926074.aspx

でもコメントの//後に半角開けるのって気持ち悪い…
Read more →

2014/01/03

[プログラミング]ファイル読み込み

,
 100 Little Programming Exercises – go-left Softwareの"A.5 Count Words and Lines"です。と言ってもファイルの文字数と行数を数えるだけですが… 改行コードの猥雑さ(LF、CR、CRLF)を叩き込む、文字列処理の練習ということなんでしょうか?でも面倒だったので、Fileクラス使って済ませました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CountWordsandLines1
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Specify a file name");
            }
            else
            {
                var filename = args[0];
                var encoding = Encoding.UTF8;
                // ファイルを読みこむ
                //if(File.Exists(filename)) //ファイルが存在するかは、File.Exists()か読み込み時例外のどっち?
                try
                {
                    var lines = File.ReadLines(filename, encoding);
                    Console.WriteLine("File name : {0} contains {1} words in {2} lines", 
                        filename, lines.Select(line => line.Length).Sum(), lines.Count());
                }
                catch (FileNotFoundException e)
                {
                    Console.WriteLine(e.StackTrace + "\n" + e.Message);
                }
                catch (System.Security.SecurityException e)
                {
                    Console.WriteLine(e.StackTrace + "\n" + e.Message);
                }
            }
        }
    }
}

 File.ReadLines(string, Encoding)はどうやら.NET 4.0追加の新しい関数らしいです。私は最初ストリームを使うものだと思ってました。で、この関数は面白いことに、返り値がIEnumerable<string>です。

.NET Framework 4 の基本クラス ライブラリの新機能 - http://msdn.microsoft.com/ja-jp/magazine/ee428166.aspx

 なので今回はこれを使って、LINQ拡張メソッドで文字数を数えてます。.Select(line => line.Length)で行ごとの文字数を数え、それを.Sum()で合計してます。
 行数は、1行毎にIEnumerable<string>に突っ込まれているので、この要素数になります。
 一応幾つかファイルを読み込んでみた結果では、合ってるみたいです。

ぎもん

  • ファイルを読み込むときに、やはりファイルが存在するかは確かめると思うんだけど、これはFile.Exists()を使って事前に確かめるべきなのか?File.ReadLines()してみてFileNotFoundExceptionをcatchすることで把握するべきなのか?
  • Encodingの問題 - 未知のファイルは多分事前にエンコーディングはわからないだろうけど、どうするんだろう。
  • 容量の大きすぎるファイル問題 - GB級ファイルの問題とか、メモリをどうするかとか
Read more →

[プログラミング]引数逆順

,
 お正月休みですね。といっても、バイトもなく、実家暮らしで暇なので、プログラミングの練習問題を探してはC#をごにょごにょしてます。

100 Little Programming Exercises – go-left Software - https://go-left.com/blog/programming/100-little-programming-exercises/

これのReverse The Inputです。引数は自動的に、スペースで区切られた単語ごとに配列に入れられるので、その配列を逆順にすればいいだけですが、最後の要素"END"は逆順にしません。

とりあえず書いたのは以下のコード。
static void Main(string[] args)
{
    var list = args.ToList();
    if (list.Last() != "END")
    {
        Console.WriteLine("args.Last() != \"END\"");
    }
    else
    {
        //http://melma.com/backnumber_120830_4798410/
        //IEnumerable<T>.Reverse()は範囲の指定ができない。
        //List<T>.Reverse()は範囲指定が出来るが、返り値がvoid。
        list.RemoveAt(list.Count() - 1);
        list.Reverse<string>().ToList().ForEach(arg => Console.WriteLine(arg));
    }
}

 例外処理はしておらず、LINQ拡張メソッドではなくList<T>の関数を呼んでるので、行数が多めだと思われます。
 本来は多分配列の逆順を実装する練習なんでしょうけど、各要素を後ろから挿入するくらいしか思いつかなかった…

 最初は最後の要素以外のReverse()(List<T>.Reverse(Int32, Int32))しようと思ったんですけど、RemoveしてIEnumerable<T>.Reverse()のほうがいい気がした。
 IEnumerableだかにForEachが無いから、ListのForEachを使うためだけにToList()してる(どうやら副作用回避のため実装してないけれど、皆同様の拡張メソッド作ってしまうみたいだね)。

LINQ拡張メソッドのTake()を使う

結局、自分が困っているのは「どうやって最後の要素以外を逆順するか?」ということで、上の例だとRemoveAt()を用いてList<string>から要素を削除して、それからReverse()して対処してるわけです。
 
 んでも拡張メソッド一覧見てたら、「配列の一部だけを抜き出す」というTake()を見つけたので、じゃあそっちのほうが一行で済むよね?ということでこうしました。
static void Main(string[] args)
{
    var endKey = "END";
    if (args.Last() != endKey)
    {
        Console.WriteLine("args.Last() != " + endKey);
    }
    else
    {
        //http://melma.com/backnumber_120830_4798410/
        //IEnumerable<T>.Reverse()は範囲の指定ができない。
        //List<T>.Reverse()は範囲指定が出来るが、返り値がvoid。
        args.ToList()
            .Take(args.Length - 1)
            .Reverse<string>()
            .ToList().ForEach(arg => Console.WriteLine(arg));
    }
}
このコードの問題は、args.ToList()した後に、argsのListを幾つ抜き出す(Take()する)かの指定に、Listする前のargsの要素数を参考にしているところ…本当だったらList<T>.Countを使うべきなんだろうけど、どうしたものか…
 これは病院から帰ってきて思いついたもの。

ぎもん

自分はLINQ星人ではないけれど、「最後の要素が○○ならプログラムを終わらせる」みたいなのを、もうちょっとスマートに書けたりしないかな。
Read more →

2014/01/02

[プログラミング]暗号の国のアリスと循環アルファベット++

,
 とてもタイトルに困ったんですが…とりあえず大した内容ではないです
 
 『暗号の国のアリス』(著:結城浩)の中でシーザー式暗号というものが取り上げられています。これは、暗号化したい平文のアルファベットをそれぞれ、アルファベット順にn文字前後にずらすといったものです。例えば、"abc"なら3文字ずらしで"def"といった具合です。
 で、この暗号はBrute Force Attackで破ることが出来るのですが、練習問題を解くプログラムを書いてて「どうやって実装すればいいんだろう???」って思ったときのメモです。

実装法1 - テーブルを使う

 どうしようか迷い、最初はテーブルを使うものにしました。つまり、string配列→char配列にし、それぞれの文字のn文字先を、予め用意したテーブルから持ってきます。この場合のキモは、n文字先がテーブルのどの要素に成るかの計算です(と言っても簡単なクイズレベルですけど)。難点としては、コードが長くなるのと、for文の中にforeachが入り二重ループになりそうなことです。

参考: C# でのアルファベットの文字の配列を生成 - http://ja.softuses.com/73951

 この時、大文字(0x41~0x5A)と小文字(0x61~0x7A)を分けるのが面倒だったので、最初の文字列を.ToLower()し、小文字に揃えました(今回はこれでオッケー)。

実装法2 - 拡張メソッドを使う

 で、さっきのページでC#の拡張メソッドを見て、「なんだもっと簡単に書けるじゃん」ということで、結局次のようになりました。
static void Main(string[] args)
{
    var str = "PELCGBTENCUL";  //対象文字列
    foreach(var i in Enumerable.Range(0, 26).ToArray())
    {
        Console.WriteLine("{0} : {1}", i, 
        new string(str.ToLower().Select(c => (char)('a' + ((i + c - 'a') % 26))).ToArray()));
    }
}
 すると、以下のようになり、13文字後ろにずらす(or??文字前にずらす)と、元の平文と思われる"cryptography"が分かります。
総あたり(といっても26回)した時の例
拡張メソッドはお勉強中なのですが、実践できる機会があってよかったです。
 コードとしては、
  • str.ToLower()で、まず小文字に統一します。
  • .Select()で、変数str中の各要素(char)に対して、そのi文字先(ただし、'z' + iなどはASCIIの小文字の範囲を超えるので、i文字先(i + c)から、先頭の文字を引き(- 'a')、更に26で割ることでどのアルファベットか割り出し(% 26)、オフセット0x61のために'a'を足す。という面倒なことをしています。
  • .ToArray()でchar[]にしています。
  • new string(...)は、char[]→stringにするときの常套手段みたいです。でもnewした割りにはその変数すぐ消えるし、なんかちょっと気持ち悪いですね…
拡張メソッドのように、関数の戻り値から、また数珠つなぎのように関数を呼び出し、1行に収めるのはなかなか楽しいですね。

ぎもん

この本では、この後の使い捨て暗号(ワンタイムパッド)が解読不可な理由として、「全ての平文の候補が登場するため、どれが答えかわからない」と説明されています。これには、全てが同じ平文とかが登場するから、と例で示されています。でも、シーザー式暗号のn(0 < n < 26)文字復元をしても、結局どれが平文か分からないような気もする…?(もしかしたら平文は単語ではないのかもしれない)
 結局、元の平文が人間味のある∧既知の単語∧平文候補が1つを覗いて辞書に載ってる単語じゃないとだめなのでは…
 こればかりは、使い捨てパッドが解読不可能である数学的証明を知らないと、真実に辿り着けなさそうですね…

 それと、暗号はやはり戦争に於いても利用されたようですが(ナチスドイツのエニグマetc)、「どうやって鍵を秘密裏に運ぶか」ということに苦心したんですね。前線の兵に秘密鍵を渡す方法というのも気になってきました。
Read more →

2013/09/28

[電子工作]aitendoのDDSモジュールを動かす その2

,
0)前置き

 前にこんな記事を書いて以来、aitendoのDDSモジュールも放置していたのですが、そろそろケースに入れたいのでまた触り始めました。
 取り敢えず、SPIでいい加減動かさないと…ということでSPIから制御する話です。

 使ったハードはArduino Duemilanoveなので(今となっては化石…)、Master側のSPI設定に困ることはないです。


1)ハード的な話

 AD9850は、どうも2ピンがLow、3・4ピンがHighならばデフォルトでシリアル通信モードみたいです。aitendoにある回路図を見ると、2ピンに繋がっているJP1をジャンパしておけばGNDに落ちるみたいなので、パラレル→シリアルへの変換コマンドを送る必要はないみたいです。

 出力ですが、IOUTが200ΩでGNDに落ちているので、そのまま出力インピーダンスも200Ωなのでしょうか…?評価キットの回路図では、IOUTBは25ΩでGNDに、IOUTは50ΩでGNDに落ちてますが…

2)ソフト的な話

 AD9850のデータシートに書かれている、SPI通信のタイミングをみます。
AD9850 Data Sheet 英語 Rev H(http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf)より転載
で、AD9850のシリアル信号線の名前は、SPIによくあるSS、MOSIなどとは違ってますが、結局役割は同じみたいです。

FQUP(FreQuency UPdate) → SS(10)
WCLK → SCLK(13)
D7 → MOSI(11)


 SPI通信をするときには、幾つか設定しないといけない事がありますが、AD9850のデータシートを見る限りでは、以下の様になってるみたいです。

・動作モード
 CLKの立ち上がりでデータをサンプリングするみたいです。又、通信していない時のCLKはLowなので、SPI_MODE0になります。

・データの送り方(BitOrder)
 BitOrderは、データの送る順番がw0(LSB)から順にw1、w2、…、w31(MSB)なので、LSBFIRSTです。

・SS(FQUP)の制御
 SPI通信では、先ず最初にSSを下げてから通信をしますが、どうも通信終わりのSS↑↓は自分でやらないといけないみたいです。

・AD9850のRESET信号
 RESETを最初に落とすことになりますが、このRESETは正論理(!)なので、Resetしたい時に0にします。最初負論理だとてっきり思っていて、動かずに困りました…

・送るデータですが、EF_AD9850に依れば、
//Calculate the frequency of the HEX value
x=4294967295/125;//Suitable for 125M Crystal
frequence=frequence/1000000;
frequence=frequence*x;
y=frequence;
らしいです。例えば40kHzであれば、0b00000000, 0b00010100, 0b11111000, 0b10110101になります。

最後のw32~39のControl Registerには、
bit[1:0] Control Serialならば00
bit2 Power Shutdown 0
bit[7:3] Phase
を書き込みます。


 ということで、ArduinoのSPIを使うように改造しました。一応クラスにしてますが、8bitレジスタ5個分を送るだけの簡単なプログラムです。
class AD9850{
 //7 reset
 //10 fqup
 //11 d7
 //13 wclk 

private:

public:
 AD9850(){};
 ~AD9850(){};

 //Initialize SPI & DDS(AD9850)
 void Init(){
  //set
  pinMode(7, OUTPUT);

  //Send Reset signal
  digitalWrite(7, 0);
  digitalWrite(7, 1);
  digitalWrite(7, 0);

  //Init SPI
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);  //Idle : clk low, rising edge. 
  SPI.setBitOrder(LSBFIRST);
 };

 //freq : Frequency[Hz]
 //w0 : Control Register Value
 void SetFrequency(const unsigned char w0, const double freq){
  const double xtal_freq = 125;  //125MHz Xtal
  const double x = (4294967295 / xtal_freq)  / 1000000;
  long int y = (long int)(freq * x);
  
  SPI.transfer(y & 0xFF);
  SPI.transfer((y >> 8) & 0xFF);
  SPI.transfer((y >> 16) & 0xFF);
  SPI.transfer((y >> 24) & 0xFF);
  SPI.transfer(w0);  //Control Register
  //FQUP
  digitalWrite(10, 1);
  digitalWrite(10, 0);
 };
};
このプログラムを実機上で動かして、ロジック・アナライザで通信データを見てみたところ、下の写真のようになりました。
ロジアナで見たSPI通信

 SPI.transfer()を1byteごとに呼んで送信しているため、1byteごとに何も通信していない部分(A2のMOSIが1になっているところ)がありますが、CLKもLowのままでサンプリングされてないので問題無いと思っています。

 DigitalWrite()でSS(FQUP)を制御しているので、SPIの通信が終わってからSSが上がるまでに結構時間がかかっているみたいですが、まあ動いてるので大丈夫でしょう…

 使い方は、前のライブラリ(EF_AD9850)とほぼ一緒。
Read more →

2013/09/10

[電子工作]LPC800 Mini-Kitもどきの製作 その1

,
※書きかけ、やりかけ

 LPC800 Mini-Kitが探しても本当に見当たらないので、業を煮やしてそれっぽいのを作ることにしました?
 初心者がやるとこーなるのかーーーーみたいなことを分からされててとてもつらいなあ。

ピンの設定
ISPモードに入るためGNDに落とすスイッチが5ピン。
ISP書き込み用のUARTが

部品の選定
 

 電源周り、自動切り替えするICは使わずに、原始的にダイオードで済ます。一応VBUSを外に出して使うことも考えてはいるので、バイパス用のジャンパパターンを付けておく。
 ショットキーダイオードは思考停止でライブラリにあるSOD523を選んだ。でも実際ボードいじってると、これじゃあ小さすぎて無理だと思わされるってワケ。一応Digikeyにはこのパッケで使えそうなのがある(http://www.digikey.com/product-detail/en/PMEG3005EB,115/568-7397-1-ND/2697417)。けどねえ…
 
 スイッチング電源はコイルを選ぶ才能がないと気付いたので、LDOにした(楽)。選択基準とか候補が次のようになってる。
3.3V LDO Vdrop < 1.0V、Iout > 0.3A

ノイズ入るとヤバイとも思えないんだけど、なんか面倒くさいしLDOにしておく。
BA33BC0FP-E2 \107 1A TO252-3 Vin < 16V Vdropは0.3V@200mA? Rohm
http://www.digikey.jp/product-detail/ja/BA33BC0FP-E2/BA33BC0FP-E2CT-ND/3663735
KA78RM33RTF \72 0.5A TO252-3 Vin < 20V Vdropは0.6V@500mA、200mAだと180mV程度。 Fairchild
http://www.digikey.jp/product-detail/ja/KA78RM33RTF/KA78RM33RTFFSCT-ND/3478379
MCP1802T-3302I/OT \69 SOT23-5 0.3A Vin < 10V Vdropは0.2V@100mA
http://www.digikey.jp/product-detail/ja/MCP1802T-3302I%2FOT/MCP1802T-3302I%2FOTCT-ND/1979781
AP7215-33YG-13 \58 SOT89-3 0.6A Vin < 5.5V Vdropは0.1V@100mA 本当はこれを使いたかったが、外部9Vも無くはなさそうだから断念…
http://www.digikey.jp/product-detail/ja/AP7215-33YG-13/AP7215-33YG-13DICT-ND/2179740
TA48S033AF(T6L1,Q) \128 5HSIP(TO-252-6) 1A Vin < 16V Vropは0.69V@1A
http://www.digikey.jp/product-detail/ja/TA48S033AF(T6L1,Q)/TA48S033AF(T6L1Q)CT-ND/2273210

 とりあえずKA78RM33RTFを使う方針で行きます。AdafruitのライブラリにTO252があります、78XXのライブラリもあります。なので唯のコピペ。
 コンデンサは入力側は0.1μFだけど、出力側が33μFなので、チップ積層セラミックコンデンサならこれ
C3216X5R0J336M130AC TDK 3216 ±20% \95(高い…)
http://www.digikey.jp/product-detail/ja/C3216X5R0J336M130AC/445-4060-1-ND/1965707
ちな、秋月には33μFは売ってない。


回路図(暫定、ほぼ決定)
かいろず
配置が下手、読みにくさヤバイ。特に部品の番号とかの重なりがひでえ。

パターン
 ICの近くに、ICと同じ配置したピンを出しておく。
 VBUS、Vccもパッドで外に出しておく。
Top
FT232RL周りが本当にきつくて、全然配線できない。やばい。Arduino Duemilanoveとかどうしてるんだろ…
 とりあえず、5cm * 5cmにはどうやっても収まりそうにないから、5*10か10*10になるんだろうなあ…
Read more →

2013/09/08

[電子工作]LPC11U37でのXorshiftのメモリ消費量とかを調べた

,
 ちょっと気になったので、Xorshiftのコードサイズがどれくらいになるのか確かめてみました。あと最初うまくいかなかった時に何をしたのかの、ログです。

ぜんたいのこうせい
1)前置き
2)ソース
3)実行結果
4)まとめ
5)おまけ

使った環境
MPU : LPC11U37/401 Cortex-M0
Toolchain : Keil MDK-ARM Version:4.72.1.0

1)前置き - なんでXorshiftか
 乱数が欲しい時は何を使えばいいんでしょうか。標準的な関数rand()は、今では精度の悪い(≒ランダム性が高くない)、使わない方がいい乱数生成器の代表になってしまいましたが、シード含めて2つの関数を呼べば乱数が出てくるので、気軽に使える乱数生成器の1つです。この気軽に使えるというのが重要で、例えばC++の標準になった乱数生成クラスだと、何回かクラスのインスタンスをゴニョゴニョして、んでもってやっと乱数が出てくる(精度はいい、らしい)感じです。これだと、PC上で実行する分にはいいけれど、メモリが少なく速度が遅いマイコンだと、本当に使えるのかと疑問になってしまいます(まあそもそもC++のこれにまだ対応していないのが多いような気もする、<random>インクルードしたらエラーになったし)。
 ということで、今回はある程度精度があって、とても高速だと言われるXorshiftというものを使ってみます。
 解説とサンプルコードは、Wikipedia(http://ja.wikipedia.org/wiki/Xorshift)にあります。
 
2)ソース

 こんなかんじのコード(main.cpp)になりました。他に、startup_LPC11U**.s、system_LPC11Uxx.c、core_cm0.c、core_cm0.h、サンプルプログラム集にあるuart.c、uart.h、type.hが必要だと思われます。

#ifdef __cplusplus
extern "C" {
#endif
 
#include "LPC11Uxx.h"
#include "uart.h"
#include 
 
void SysTick_Handler(void);
 
#ifdef __cplusplus
}
#endif

//xorshift
uint32_t xor128(){
 static uint32_t x = 123456789;
 static uint32_t y = 362436069;
 static uint32_t z = 521288629;
 static uint32_t w = 88675123;
 
 uint32_t t = x ^ (x << 11);
 x = y;
 y = z;
 z = w;
 return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
}

void uart_init(uint32_t baudrate)
{
 UARTInit(baudrate);
}

//delay functions
volatile static uint32_t msTicks = 0;
void SysTick_Handler(void) { 
 msTicks++;
}

inline void delay(uint32_t delayTicks) {
 uint32_t curTicks = msTicks;
 while((msTicks - curTicks) < delayTicks);
}

//entry point
int main()
{
 SystemCoreClockUpdate();

 uart_init(9600);
 
 if(SysTick_Config(SystemCoreClock / 1000)){
  //handle error
 }
 
 while(1){
  char str[24] = {0};
  const uint32_t length = (uint32_t)(sizeof(str) / sizeof(str[0]));
  //convert uint32_t to char array.
  if(EOF != sprintf(str, "%u\r\n", xor128())){
   //send to console(UART serial).
   UARTSend((uint8_t*)str, length);
  }
  
  delay(1000);
 }
 
 return 0;
}

 このプロジェクトをまとめたものを.zip形式で上げておこうと思ったんですが、Bloggerがファイルのアップロードに対応していないらしいので、考え中です…この際意識高くgitデビューしちゃうか。

3)実行結果

 他に書くこと無いので、メモリ消費量と逆アセンブルを書いておきます。なお、これはXorshiftを使っているけれど、上のコードとは少し違うものをコンパイルした時のものです。
 途中に出てくる-O0、-O1、-O2、-O3というのは最適化の度合いで、数字が大きくなるほど最適化が書けられていきます。いらないものはバッサバッサ切り落とされちゃいます。これを設定するには、Keil μVision4なら、[Project]→[Options for Target '<project name>'...]で出てくるダイアログの、[C/C++]タブ、[Language/Code Generation]のOptimizationで設定可能です。

 また、ここに書いてある逆アセンブルはデバッグ時に表示されるものをコピー&ペーストしています。困ったことに、無償版だとこうしないと表示してくれないみたいです(参考:http://mbed.org/users/MACRUM/notebook/offline_compiler_2/)。


-O0です。
xorshift書く前
Program Size: Code=4724 RO-data=224 RW-data=172 ZI-data=676

xorshift書いた
Program Size: Code=4792 RO-data=224 RW-data=188 ZI-data=676

xorshiftをボーレートにした(強制的に使う)
Program Size: Code=4796 RO-data=224 RW-data=188 ZI-data=676


逆アセンブル
                 __rt_exit_exit:
0x00000160 BC03      POP      {r0-r1}
0x00000162 F001F8F3  BL.W     _sys_exit (0x0000134C)
0x00000166 0000      MOVS     r0,r0
    19:         uint32_t t = x ^ (x << 11); 
0x00000168 484E      LDR      r0,[pc,#312]  ; @0x000002A4
0x0000016A 6800      LDR      r0,[r0,#0x00]
0x0000016C 02C0      LSLS     r0,r0,#11
0x0000016E 4A4D      LDR      r2,[pc,#308]  ; @0x000002A4
0x00000170 6812      LDR      r2,[r2,#0x00]
0x00000172 4050      EORS     r0,r0,r2
0x00000174 4601      MOV      r1,r0
    20:         x = y; 
0x00000176 484C      LDR      r0,[pc,#304]  ; @0x000002A8
0x00000178 6800      LDR      r0,[r0,#0x00]
0x0000017A 4A4A      LDR      r2,[pc,#296]  ; @0x000002A4
0x0000017C 6010      STR      r0,[r2,#0x00]
    21:         y = z; 
0x0000017E 484B      LDR      r0,[pc,#300]  ; @0x000002AC
0x00000180 6800      LDR      r0,[r0,#0x00]
0x00000182 4A49      LDR      r2,[pc,#292]  ; @0x000002A8
0x00000184 6010      STR      r0,[r2,#0x00]
    22:         z = w; 
0x00000186 484A      LDR      r0,[pc,#296]  ; @0x000002B0
0x00000188 6800      LDR      r0,[r0,#0x00]
0x0000018A 4A48      LDR      r2,[pc,#288]  ; @0x000002AC
0x0000018C 6010      STR      r0,[r2,#0x00]
    23:         return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 
0x0000018E 4848      LDR      r0,[pc,#288]  ; @0x000002B0
0x00000190 6800      LDR      r0,[r0,#0x00]
0x00000192 0CC0      LSRS     r0,r0,#19
0x00000194 4A46      LDR      r2,[pc,#280]  ; @0x000002B0
0x00000196 6812      LDR      r2,[r2,#0x00]
0x00000198 4050      EORS     r0,r0,r2
0x0000019A 0A0A      LSRS     r2,r1,#8
0x0000019C 404A      EORS     r2,r2,r1
0x0000019E 4050      EORS     r0,r0,r2
0x000001A0 4A43      LDR      r2,[pc,#268]  ; @0x000002B0
0x000001A2 6010      STR      r0,[r2,#0x00]
    24: } 

-O3です。
xorshiftのxor128()をコメントアウトしている
Program Size: Code=3236 RO-data=236 RW-data=172 ZI-data=676

xorshiftをボーレートにしてない。
Program Size: Code=3280 RO-data=224 RW-data=188 ZI-data=676

xorshiftをボーレートにした
Program Size: Code=3280 RO-data=224 RW-data=188 ZI-data=676


逆アセンブル
    19:         uint32_t t = x ^ (x << 11); 
0x00000168 492A      LDR      r1,[pc,#168]  ; @0x00000214
0x0000016A 6848      LDR      r0,[r1,#0x04]
0x0000016C 02C2      LSLS     r2,r0,#11
0x0000016E 4042      EORS     r2,r2,r0
    20:         x = y; 
0x00000170 6888      LDR      r0,[r1,#0x08]
    21:         y = z; 
0x00000172 6048      STR      r0,[r1,#0x04]
0x00000174 68C8      LDR      r0,[r1,#0x0C]
    22:         z = w; 
0x00000176 6088      STR      r0,[r1,#0x08]
0x00000178 690B      LDR      r3,[r1,#0x10]
    23:         return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 
0x0000017A 60CB      STR      r3,[r1,#0x0C]
0x0000017C 0CD8      LSRS     r0,r3,#19
0x0000017E 4058      EORS     r0,r0,r3
0x00000180 0A13      LSRS     r3,r2,#8
0x00000182 4053      EORS     r3,r3,r2
0x00000184 4058      EORS     r0,r0,r3
0x00000186 6108      STR      r0,[r1,#0x10]
    24: } 
    25:  
    
0x00000214 0000      DCW      0x0000}#0x10]                            /* Function successful */ SysTick Timer */ 

ちなみに、Global Symbolsによれば(<project name>.map。プロジェクトをダブルクリックすると表示される。)

Symbol Name                              Value     Ov Type        Size  Object(Section)
-O3 xor128()                                 0x00000169   Thumb Code    34  main.o(.text)
-O2  xor128()                                 0x00000169   Thumb Code    34  main.o(.text)
-O1  xor128()                                 0x00000169   Thumb Code    34  main.o(.text)
-O0 xor128()                                 0x00000169   Thumb Code    62  main.o(.text)

また、static変数は
Image Symbol Table

    Local Symbols

    Symbol Name                              Value     Ov Type        Size  Object(Section)
    x                                        0x10000004   Data           4  main.o(.data)
    y                                        0x10000008   Data           4  main.o(.data)
    z                                        0x1000000c   Data           4  main.o(.data)
    w                                       0x10000010   Data           4  main.o(.data)
のようになっている。uint32_tだから4Byteなのは妥当。だけどどこで初期化されているんだろう?

 UARTがなんか動かないので、まだxor128()がちゃんと乱数を生成しているかまでは検証していない。UARTでXorshiftが動いてるのを確認しました。
Xorshift

4)まとめ

 50Bytesとかだったら、余裕でプログラムに組み込んでいける。
 1clk = 48MHzとして、1clk1命令だとする(どうなんだろう)。すると、約21nsで1命令。16命令あるから、21*16=0.336μsが理想の動作速度?

 問題はx、y、zとかのシード値をどうするかで、C++11の<random>なら、ハードウェアエントロピーリソースを用いた予測不可能な乱数std::random_deviceが使えますが、ああいったものを考えないと毎回同じ数値が出てきてしまいます。1000回ごとにEEPROMに保存して、次回はそれをシードにするとかでしょうか…


------------------------------------------------------------------------------------------------------
5)おまけ! - extern "C"を忘れて、SysTick_Handlerが無限ループしてた話

 上のコードは、最初は次のようなコードでした。しかしこれだと、UARTに1~2文字ほど表示されるだけで動作が止まってしまい、ちゃんとxor128()が動いているのかわかりませんでした。
#ifdef __cplusplus
extern "C" {
#endif
 
#include "LPC11Uxx.h"
#include "uart.h"
#include < stdio.h >
 
 
#ifdef __cplusplus
}
#endif

//xorshift
uint32_t xor128(){
 static uint32_t x = 123456789;
 static uint32_t y = 362436069;
 static uint32_t z = 521288629;
 static uint32_t w = 88675123;
 
 uint32_t t = x ^ (x << 11);
 x = y;
 y = z;
 z = w;
 return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
}

void uart_init(uint32_t baudrate)
{
 UARTInit(baudrate);
}

//delay functions
volatile static uint32_t msTicks = 0;
void SysTick_Handler(void) { 
 msTicks++;
}

inline void delay(uint32_t delayTicks) {
 uint32_t curTicks = msTicks;
 while((msTicks - curTicks) < delayTicks);
}

//entry point
int main()
{
 SystemCoreClockUpdate();

 uart_init(9600);
 
 if(SysTick_Config(SystemCoreClock / 1000)){
  //handle error
 }
 
 while(1){
  char str[24] = {0};
  const uint32_t length = (uint32_t)(sizeof(str) / sizeof(str[0]));
  //convert uint32_t to char array.
  if(EOF != sprintf(str, "%u\r\n", xor128())){
   //send to console(UART serial).
   UARTSend((uint8_t*)str, length);
  }
  
  delay(1000);
 }
 
 return 0;
}
この方(http://mibc.blog.fc2.com/blog-entry-82.html)と似たような症状なのかな。

5-1)問題を切り分けたい…

 とりあえず、デバッガを起動して実行してみます。すると、デバッガのステップ実行ではうまく文字が送信されるのに、リリース(FlashへのDownload書き込み)では動かない事がわかりました。
 どこに問題があるんでしょうか…。とりあえずブレークポイントを全て消し、プログラムをしばらく走らせ、徐ろにStopさせます。すると、SysTick_Handler()のB命令でずっと止まっているのがわかりました。本来ならばmsTicks++;が実行されるだけなので、なんか変です…

 しょうが無いのでプログラムで問題がありそうなところをコメントアウトしていきます。すると、次の部分をコメントアウトしたら動きました。
if(SysTick_Config(SystemCoreClock / 1000)){
 //handle error
}
ということは、SysTick_Config()が原因みたいです。SysTick_Config()はSysTickタイマーを設定・開始させる関数なので、もしかしたら割り込みが悪い影響を与えているのかもしれません。例えば、UART送信中に割り込みが来たりしたら動かなくなるとか?(違った)
 ということで、UARTSendのところに
__disable_irq();
UARTSend((uint8_t*)str, length);
__enable_irq(); //割り込みを再設定しないといけない?
としてみました。すると、最初の1回だけは正常に動きます。どうやら、問題はUARTSendではないようです(ライブラリからコピーしてきたので動いて当たり前か…)。

 で、SysTick_Handler()が無限ループになることも考えると、UART送信中にSysTickタイマの割り込みが発生し、SysTick_Handler()に飛んで無限ループに陥るために、送信できないのかもしれないと考えます。

5-2)void SysTick_Handler(void)はどこなのか

 startup_LPC11Uxx.sではSysTick_Handler()は
SysTick_Handler PROC
                EXPORT  SysTick_Handler           [WEAK]
                B       .
                ENDP
となっています。EXPORT SysTick_Handlerを右クリックし、[Go To Definition of 'SysTick_Handler'...]を選んでみても、ちゃんとmain.cppにあるSysTick_Handlerに飛びます。
その割には、逆アセンブルを見ると、
   151:                 EXPORT  PendSV_Handler            [WEAK] 
0x0000030A E7FE      B        SVC_Handler (0x0000030A)
   152:                 B       . 
   153:                 ENDP 
   154: SysTick_Handler PROC 
   155:                 EXPORT  SysTick_Handler           [WEAK] 
0x0000030C E7FE      B        PendSV_Handler (0x0000030C)
   156:                 B       . 
   157:                 ENDP 
   158: Reserved_IRQHandler PROC 
   159:                 EXPORT  Reserved_IRQHandler       [WEAK] 
0x0000030E E7FE      B        SysTick_Handler (0x0000030E)
   160:                 B       . 
0x00000310 E7FE      B        Reserved_IRQHandler (0x00000310)
となっています。これは、main.cppで定義したSysTick_Handler()ではなく、無限ループなのでは?

 アプリケーションノートのp.424~425とp.426の"Figure 23?80 Vector table"には、SysTickの割り込みベクタは0x0000003Cらしいですが、このアドレスを見る限りだと
0x0000003C 030F      DCW      0x030F
0x0000003E 0000      DCW      0x0000
となっているので、
    32: //delay functions 
    33: volatile static uint32_t msTicks = 0; 
    34: void SysTick_Handler(void) {  
0x000001C0 BD10      POP      {r4,pc}
    35:         msTicks++; 
0x000001C2 4844      LDR      r0,[pc,#272]  ; @0x000002D4
0x000001C4 6800      LDR      r0,[r0,#0x00]
0x000001C6 1C40      ADDS     r0,r0,#1
0x000001C8 4942      LDR      r1,[pc,#264]  ; @0x000002D4
0x000001CA 6008      STR      r0,[r1,#0x00]
    36: } 
にはどう考えても飛ばなさそうです。

5-3)顛末

 ここで、SysTick_Handler()の設定の仕方に何か問題があるのではと調べたら、これが出てきました。この通りにextern "C"したらちゃんと動きました。そうだった、前も同じミスをやったんでした。完全にバカですね。

 実際に動かない場合の.mapファイルを見てみると、関数は
    xor128()                                 0x00000179   Thumb Code    62  main.o(.text)
    uart_init(unsigned)                      0x000001b7   Thumb Code    12  main.o(.text)
    SysTick_Handler()                        0x000001c3   Thumb Code    12  main.o(.text)
    main                                     0x000001cf   Thumb Code   132  main.o(.text)
    __use_two_region_memory                  0x000002f1   Thumb Code     2  heapauxi.o(.text)
    __rt_heap_escrow$2region                 0x000002f3   Thumb Code     2  heapauxi.o(.text)
    __rt_heap_expand$2region                 0x000002f5   Thumb Code     2  heapauxi.o(.text)
    __I$use$semihosting                      0x000002f7   Thumb Code     0  use_no_semi.o(.text)
    __use_no_semihosting_swi                 0x000002f7   Thumb Code     2  use_no_semi.o(.text)
    __semihosting_library_function           0x000002f9   Thumb Code     0  indicate_semi.o(.text)
    Reset_Handler                            0x00000301   Thumb Code     8  startup_lpc11uxx.o(.text)
    HardFault_Handler                        0x00000309   Thumb Code     2  startup_lpc11uxx.o(.text)
    SVC_Handler                              0x0000030b   Thumb Code     2  startup_lpc11uxx.o(.text)
    PendSV_Handler                           0x0000030d   Thumb Code     2  startup_lpc11uxx.o(.text)
    SysTick_Handler                          0x0000030f   Thumb Code     2  startup_lpc11uxx.o(.text)
となっていて、SysTick_HandlerとSysTick_Handler()が別々にあるのがわかります。
Read more →

2013/08/21

[FPGA][MicroBlaze]MicroBlaze開発中に出てきたエラーと、その解決

,
 今日は待ちに待った2CELLOSのCD発売日なんですが、ヨドバシ秋葉マルチメディアには置いてなかったので激おこです。しょうがないので、これ書いたらプール行って帰ろうと思います。

 で、MicroBlaze人口が少ないからなんでしょうか、エラーメッセージで検索すると日本語のページが全然出てこなくて大変です。全く、失敗とその解決法も、動作法に劣らない程度に重要だと思うんですよね、時間を使って得た経験、ノウハウとも言えるでしょうし…
 いくつか、自分が嵌められた?エラーとその解決法についてメモしておきます。

1)MicroBlazeにあったdebug_moduleを消して、手動でまた追加する場合
 別のプロジェクト開いたら、xmdでfpga -f ***.bitはいけるのにconnect mb mdmでMDMがねえよって怒られたので…(このときのエラーメッセージはコピペしてメモしてないんだ、ごめん)

 まず、microblaze_0をダブルクリックし、Configuration WizardでEnable Debugにチェックを入れる。
 次に、IP CatalogからDebug→MicroBlaze Debug Module (MDM)を選択し、追加する。
IPコアの追加(Debugの一番下のやつね)
で、これだといろいろと配線が足りないと思うので、Bus Interfaceから
・debug_moduleのMBDEBUG_0を、microblaze_0_debugに
・debug_moduleのS_AXI(AXIバスを使っている場合)を、なんかそれっぽいのに(てきとう)
・Portsタブから、debug_moduleのDebug_SYS_Rstを右クリックして[Make External]、リセットのところの(名前がまちまちだろうけど、例としてproc_sys_reset_0)のMB_Debug_Sys_Rstにそれぞれ繋ぎます。
要は、debug_module⇔microblaze、debug_moduleのバス接続、Reset接続です(もしかしたらバスは要らないのかもしれない。Resetはないとエラーが出る)。
microblaze_0とdebug_moduleを繋げる。Bus Interfacesから。
Reset信号のモジュール同士の接続は、右クリック→[Make External]からだ。
でいいような気がする。変に直接ポート名をタイプすると、エラーだらけになって手に負えなくなるのでやらない。
MicroBlaze Debug Module 
で、最後にAddressesタブを開き、AXIバス上での(かなあ)アドレスを指定してあげます(すでにアドレスが割り当ててあるなら、この手順は別にいらないかもですね)。これには、モジュールを選択して右上にあるGenerate Addressボタンを押すだけで終わります。
Addressを割り当ててもらう。
参考:Xilinx エンベデッド システム開発 v12.3 8-14 アドレスの割り当て
//ハウツーマニュアルが、セミナーに行かないと貰えないのってちょっとどうかと思うんです…いくら商売とは言え…

2)逆に、debug_module(MDM)を2つ以上つないじゃった場合
 多分Run DRCsのところで SIGNAL: Ext_BRK - multiple drivers found: のようなエラーが出て通らないと思います。しかし困ったことに、これでもGenerate Netlist、Generate BitStreamは一応選択できるんですね…
 普通にBus Interfacesのところを右クリックして、Delete Instanceすればオッケーです(禍根を残さないようにAll Connectionをdeleteしてもらう)。で、自分は通った。
debug_moduleとmdm_0って名前が違うから、最初重複してることに全く気付かなかったのがそもそもの原因…

参考:http://forums.xilinx.com/t5/Embedded-Processors-and/Multiple-debug-modules-instantiation/td-p/273698

 ちなみにこの時はExport DesignからSDKが起動できなくて嘆いてた。その時のログは以下のようなもの。これだけだと通ってるように見えるから困る。
********************************************************************************
At Local date and time: Wed Aug 21 15:38:48 2013
 make -f system.make exporttosdk started...
IF NOT EXIST "SDK\SDK_Export\hw" @mkdir "SDK\SDK_Export\hw"
psf2Edward -inp system.xmp -exit_on_error -dont_add_loginfo -make_inst_lower -edwver 1.2 -xml SDK\SDK_Export\hw/system.xml 
Release 14.5 - psf2Edward EDK_P.58f (nt64)
Copyright (c) 1995-2012 Xilinx, Inc.  All rights reserved.

Checking platform configuration ...
IPNAME: plb_v46, INSTANCE: mb_plb - 2 master(s) : 9 slave(s) 
IPNAME: lmb_v10, INSTANCE: ilmb - 1 master(s) : 1 slave(s) 
IPNAME: lmb_v10, INSTANCE: dlmb - 1 master(s) : 1 slave(s) 

Checking port drivers...
Done!

3)Generate BitStreamに反応がない場合
 例として、というか自分の場合Run DRCs、Generate Netlistまでは通っているのに、Generate BitStreamを押しても以下のようなメッセージがコンソールに吐きだされて、Generateされない。
********************************************************************************
At Local date and time: Wed Aug 21 18:01:16 2013
 make -f cpu.make bits started...
"This project has been instantiated in Xilinx ISE Project Navigator. Please use ProjNav to generate the bitstream. "
Done!
もう知らん。そもそもXPSからProjectを開いたときに、以下のようなダイアログが出てくる時点でイミフだって
どゆこと…
(2013/09/08)これは、ISPのProject Navigatorで作られたプロジェクトは、ISEから開くべき、みたいなやつだったような気がする…

4)Linuxのコンパイルについて

 多分自分のことだからどうせまた記事を書いちゃうんでしょうけど、Linuxのコンパイル時にはXilinx Wikiに書いてあるやり方だと怒られてビルド通りません。
 どうやらCROSS_COMPILEオプションを指定してあげた方がいいみたい?で、mb-かmicroblazeel-xilinx-gnu-だったかを書いてあげたら通りました。(要検証)

0)各ファイルの場所
最初のモジュール名がsystemなら、system\implementation\にsystem.bitとかができる。
最終的に生成されるdownload.bitは\software\cpu_hw_platform\以下。\software\は多分SDKで聞かれるworkspaceの場所。
\software\uboot_bsp_0\microblaze_0\libsrc\uboot_v4_00_c\にconfig.mkとxparameters.h

余談
http://blog.cronus-embedded.com/article/57563399.html
なるほどねーって思ってたら、1年前の記事だった。
Read more →

2013/08/20

[FPGA][MicroBlaze]MicroBlaze用u-bootのコンパイルと実行

,
 Xilinxの組み込みIPコアの一つであるCPU、MicroBlaze上でu-bootを実行するお話です。悲しいかなネットにある大量の情報の海に溺れかけ、ここまで2日かかりました。かなりいろいろとややこしいのでメモしておきます。Linuxが起動できればいいなあ(というかその前座)。

 まず、開発環境をメモしておきます。
・Xilinx ISE 14.5
・Xilinx Platform Studio 14.5
・Xilinx Software Development Kit Release Version: Release 14.5 Build SDK_P.58f
・ターゲットボードは、Xilinxの評価ボードSP605
・後UbuntuとTeraTermが必要。


 基本的な開発の流れは、XilinxのWikiであるところのこのページに従います。

MicroBlazeのハードウェア層を、XPSで設計する(ISE使わずともいけるっぽい)。

SDKの方で、u-bootの設定をする(u-bootのコンパイルに必要な、config.mkとxparameters.hを生成する)。

SDKで、.bmmと.bit(と後.elf?)を統合し、download.bitを生成しておく。

Ubuntuで、u-bootのコンパイルをする(上記2ファイルが必要。これでu-bootファイル(拡張子なし)を生成する)

必要なファイル(download.bit、u-boot)がそろったので書き込みする


1)とりあえず、最初に、Xilinxのページから、SP605用MicroBlazeのサンプルプロジェクトをダウンロードします。

2)旧バージョンのファイルを新しくする。

 展開したらsp605_bistというフォルダが出来たと思います(多分)。そしたら、XilinxのSDKの方を立ち上げ、[File]→[Open File]でsp605_bist/system.xmpを開きます。すると、XPSが立ち上がり「旧バージョンのファイルを新しくするよ」的なダイアログが出るので、Nextをペペペと押していきます。
※system.xmpをXPSで開いても、ただ中身を表示するだけで、ダイアログが出ない故のSDKからの起動であります。

3)MicroBlazeにMMUを載せる

 まずやることは、MMUをのっけることです。なぜならば、u-bootにはMMUが必要ですが、リファレンスデザインのデフォルト設定のまま書き込んでxmdからconnect mb mdmすると、No-MMUと見えたので…
(2013/09/06)MMU無しでも普通に動くらしいです。動きました。

 XPS上で、Bus Interfaceタブにあるmicroblaze_0をダブルクリックします。

 Configuration Wizardが立ち上がりますので、左のSelect configuration:からLinux with MMUを選びます。(Low-end Linux with MMUでも大丈夫なのかもしれない。uCLinux向け?)
 [Next]ボタンを押して設定を一通り見てもいいですが、別に見なくてもよかったような気がします。[OK]ボタンを押して、Wizardを閉じます。

4)MicroBlazeのBitStreamを生成し、XPSでやることを終わらせる。

 左のNavigatorから、Run DRCs、Generate Netlist、Generate BitStreamを順にやっていきます。多分エラーは出ません、出てもちょっとどうにもならないです…(無い場合は[View]→[Toolbars]→[Navigator]?)。ちなみにこの作業は結構時間がかかります。までもNavigatorがやることをわかりやすくリストアップしてくれてるので、めっちゃいいですねこれ。

5)SDKへの開発に移る

 Export Designを押し、ダイアログが出たら[Export & Launch SDK]をクリックします。すると、SDKのWindowが表示されるかと思います。ここからは、Eclipseの操作に慣れた方ならすいすい行くと思うんですが、とりあえずWelcome的なウィンドウを閉じます。

 えっと、ここで追加のプラグインみたいなものが必要になります。u-boot-xlnxです。Xilinxのgitにあるので、Ubuntuマシンの方でgit cloneしてDLしておきます。
※Eclipseへの追加から先は、このページによくまとめられています。めっちゃ参考にしました
 このページからuboot_bsp.tar.gzをDLします。どうにかして展開します。したらXilinx\14.5\ISE_DS\EDK\sw\lib\bsp以下に放り込みます。結果として、
Xilinx\14.5\ISE_DS\EDK\sw\lib\bsp\uboot_v4_00_c\data\uboot_v2_1_0.mld
Xilinx\14.5\ISE_DS\EDK\sw\lib\bsp\uboot_v4_00_c\data\uboot_v2_1_0.tcl
の様になってればオッケーっぽいです。

したら、SDKから[File]→[New]→[Board Support Package]を選択します。図のようなダイアログが出ると思うので、左下をubootに変えて[Finish]を押します。
ubootのためにconfigファイルを作ります。
次に、よくわからないセッティングをします。下のようなウィンドウが出ると思うので(このSSはすべて設定し終えたときのもの)、Valueのところをデフォルトのnoneから繋ぎたいものに変えていきます。
セッティング
 終わったらOKを押すと、自動的にmakeが始まります。ここで結構エラー出ること多いです。MicroBlazeのピン接続が少しでも違うとダメっぽいです(Enable Debugにしたままデバッグポート繋げないとかするとエラーでた)。
 Can't find variable 'flash_memory_bank'みたいなエラーもでます。よくわからないので、出てきた解決策の通りに0固定にします。(これやるとu-bootのビルド時にgpioあたりでundefined referenceで落ちる?)
バンクってなんだよ…

ログ:
17:58:59 **** Build of project uboot_bsp_0 ****
make -k all 
libgen -hw ../sp605_bist_hw_platform/system.xml\
      \
      -pe microblaze_0 \
      -log libgen.log \
      system.mss
libgen
Xilinx EDK 14.5 Build EDK_P.58f
Copyright (c) 1995-2012 Xilinx, Inc.  All rights reserved.

Command Line: libgen -hw ../sp605_bist_hw_platform/system.xml -pe microblaze_0
-log libgen.log system.mss 


Staging source files.
Running DRCs.
#--------------------------------------

# uboot BSP DRC...

#--------------------------------------

Running generate.
#--------------------------------------

# uboot BSP generate...

#--------------------------------------

/* Main Memory is ddr3_sdram */

INFO automatic U-BOOT position = 0x57c00000

Running post_generate.
Running execs_generate.
'Finished building libraries'

17:59:03 Build Finished (took 3s.431ms)

 もしコンパイルがうまくいったならば、projectのuboot_bsp_0\microblaze_0\libsrc\uboot_v4_00_cあたりにconfig.mkとxparameters.hが出来てると思います。これをつかってu-bootをビルドします。

7)
 最後に、[Xilinx Tools]→[Program FPGA]を選択し、[OK]を押します。ここら辺の参考

6)書き込む

 ファイルが出来たら、いよいよ書き込みです。わけのわからない難解なジャンパの設定をしたら、電源を入れます。
 書き込みには、iMPACTではなくxmdというツールをCUIで使います。多分スタートメニューの[Xilinx Design Tools]→[ISE Design Suite 14.5]→[Accessories]に"ISE Design Tools 64bit Command Prompt"みたいなものがあります。それを選択します。
 コマンドラインが表示されたら、まずはdownload.bitとu-bootのあるフォルダに移動します。そうしたら
C:\***\***\***\>xmd
と打ちます。Xilinx Microprocessor Debugger (XMD) Engineだなどと表示されたかと思います。続いて、
XMD% fpga -f download.bit
XMD% connect mb mdm
XMD% dow u-boot
XMD% run
します。
xmdを使ってFPGAに書き込みする。
もしTeraTermなどのシリアルモニタを繋いでいれば(ボーレート115200)、u-bootが起動したメッセージが流れてきます。
u-bootの動作確認
ここら辺はこのページを参考にしました。

 ここまでXilinxのWiki通りに進めてきたわけですが、Xilinxに買収されたらしいPetaLinuxの話は一度も出てきませんでした。ネットにある僅かな情報によれば、それなりにコンパイルから書き込みまでが楽に行えるらしいのですが、一体どこに行ったんでしょう…

 後、実際に使うにあたってはFSBL(First Step Boot Loader)が無いと、起動してもu-bootすら読み込んでくれなくなりそうなんですが、どうすればいいんでしょう。SDKでApplication Projectを作成しようとすると、ExampleにSREC Bootloaderなるものが出てくるのですが、これなんでしょうかね…
SREC Bootloaderとは
ちなみになんでこんなことをやっているかというと、仕事でやることになったからです…Xilinx触ったことなかったんですが、仕事なのでそうも言ってられず。かといって初心者なので情報を端から端までいちいち眺めるわけです。とても覚えてられたもんじゃないので、Blogにまとめざるをえない…ですよねえ…

MicroBlazeを作りこむところからは、このpdfも参考になる。

おまけ:Xilinxの書き込みツールだけど、これで低価格品だと銘打っちゃうんだから、庶民には手の届かない世界のままなんだなあ…
Read more →

2013/07/08

[C#]SQL ServerのLocalDBを、Entity Data Modelで設計し生成する。

,
 C#でデータベースを使うお話です。私は初心者で、尚且つ情報をあまり上手に集められない人間なので、とても試行錯誤(或いは途方に暮れて迷うとも言う)しました。実際、ここに書くだけの内容を知るのにも、かなりの時間がかかりました。その覚書みたいなものです。

 まあデータベースと一口に言っても、実際には何種類も有りまして、例えばフリーライブラリのSqliteとか、MicrosoftのSQL Serverとかです。私のような情弱初心者だと、そもそもどれを使っていいかわからない…ということになってしまいました。
こんなにある…
上の写真は、VS2012で[サーバーエクスプローラー]ウィンドウで、[データ接続]を右クリック→[接続の追加]を選択した時のダイアログです。色んなデータベースの種類があります。例えば、
・SQL Server Compact 4.0とは、携帯向けのデータベースです。確か、データベースの容量や、使用出来るデータ型に制限があったような気がします。
・SQL Serverはその名の通りです。
・Access データベース ファイルとは、Microsoft Accessで用いるもの…なのでしょうか…?

 私の場合、データベースが必要といっても、アプリケーションを動かす場所は固定ではなく、ちょっとデータを保存したり読み込んだりできればいいので、保存場所はどちらかというとローカルの方が良いのです。なので、SQL Serverのうち、LocalDBと言うのを使うことにします。
//最初はLocalDBを知らなかったので、SQL Server Compact 4.0の方を使おうと頑張ってた… 
 LocalDBを作るには、[ソリューション エクスプローラー]でプロジェクト名を右クリック→[追加]→[新しい項目]で、[サービスベースのデータベース]を選択します。


 すると、このようなダイアログが出ると思います。ここでは、Entity Data Modelという機能を使いたいので、右の[Entity Data Model]を選択します。


 最初の段階から設計するので、ここでは[空のモデル]を選択。


 次に、Entity Data Modelでデータベースの設計をします。こんな画面が出てくると思います。ここでは、GUIツールを用いてぽちぽちとコンポーネントを配置することで、データベースのテーブルなどの設計図(のようなもの)を作っていきます。
 

 まず、右クリック→[新規追加]→[エンティティ]で、エンティティ(テーブルと同じようなもの?)を一つ配置します。ダイアログが出ると思います。ここは主キー(一意に識別するためのIDみたいなもの)と、エンティティに関する設定をします。多分そのままOKでいいような気がします…

 
 Entityへの要素(データ項目)の追加は、Entity上で右クリック→[新規追加]→[スカラー プロパティ]です。追加するとデフォルトでデータ型がStringになっているので、[プロパティ]ウィンドウの[全般]→[型]で適宜変更します。


 テーブル間のアソシエーション(SQLでのJOINのようなもの?)は、[ツールボックス]ウィンドウから[アソシエーション]を選択し、マウスでテーブル間をドラッグ&ドロップで繋ぐとできます。

 設計し終わったら、この論理モデル(≒設計図)から、実際にデータベース上に生成するための、SQL文を自動生成してもらいます。Entity Data Modelを設計するところの何も無いところを右クリック→[モデルからデータベースを生成]を選択します。
 

 最初のほうでプロジェクトに追加した、ローカルベースのデータベースファイルに接続することを確認します。そうしたら、次に行きます。



 DDLというタブに、論理モデルをデータベース上に生成するSQL文が書かれています。[完了]ボタンを押します。
 

 このSQL文を実行するには、左上の▷(右向き三角)マークの[実行]ボタンを押せば大丈夫です。しかし、接続文字列の問題からか、私の場合"database [Database1] does not exist. Make sure that the name is entered correctly."のようなエラーが出てしまい、実行出来ませんでした。どうやらSQL文の先頭の方にある、USE文でデータベースが選択できずにコケているようです。で、

ここで、このようなダイアログが出ることがあります。

 ローカルベースの場合(LocalDB)、この接続文字列には"(LocalDB)\11.0"などという文字列が入るものと思われます(参考:MSDN)。 の場面で右下のプロパティ欄に出ている接続文字列が、多分正しいものと思われますが、これをそのまま貼り付けると、文字数超過のようなエラーが出て接続出来ません。
 
 しょうが無いので、[サーバー エクスプローラー]→[*******.mdf]を右クリック→[新しいクエリ]を選択し、自動生成されたSQL文をそのままコピペして実行すると、同じようなエラーが出ますがなぜかテーブルは生成されています(なんでだ)。とりあえずできたので良しとはします…
コピペすると…
エラーが返ってくるが、なぜかテーブルは出来る…
これで一件落着、次はデータベースを操作することにします。長くなったので多分別の記事にします。

//書きかけ
Read more →