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

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

Railsで簡易ブログを作る その2

前回やったこと

Railsアプリケーションとデータベースを新規作成し、
ローカル環境上で動くことを確認。
truthfullscore.hatenablog.com

今回やること

  • モデルのテーブルをデータベースに追加
  • モデルのアソシエーション

テーブルの定義

今回はユーザーと投稿の情報を持つテーブルを作ります。
ログイン機能とユーザー情報はRailsのgemであるdeviseを使用し、
ブログの投稿情報はシンプルにタイトルと本文だけを持つものとします。

usersテーブル

オプション
id int8 null: false, unique: true
nickname varchar null: false, index: true
encrypted_password varchar null: false
... 以下略 ...

postsテーブル

オプション
id int8 null: false, unique: true
title varchar null: false, index: true
article text null: false
user_id int8 null: false, foreign_key: true

ユーザー(user)と投稿(post)は1:Nの関係
この情報をrailsアプリケーションに登録していきましょう。

deviseを導入しuserモデルの作成とデータベース作成

Gemfileを開き末尾に追加

gem "devise"

以下のコマンドでgemファイルをインストール

$ bundle install

以下のコマンドでdevice用のファイルとuserモデルを作成

$ rails g devise:install
$ rails g devise user

この時に作られたマイグレーションファイル(db\migrate内)を開き、
ユーザー名(nickname)のカラム情報を追加。

create_table :users do |t|
    ## Database authenticatable
    t.string :nickname,           null: false, index: true

マイグレーションを実行してデータベース上にusersテーブルを作成。

$ rake db:migrate

postモデルの作成とデータベース作成

以下のコマンドでpostモデルを作成

$ rails g devise post

この時に作られたマイグレーションファイル(db\migrate内)を開き、
postsテーブルの各カラム情報を追加

def change
  create_table :posts do |t|
    t.string      :title, null: false
    t.text        :article, null: false
    t.references  :user, null: false, foreign_key: true
    t.timestamps
  end
end

マイグレーションを実行してデータベース上にpostsテーブルを作成。

$ rake db:migrate

これでブログアプリケーションに必要なテーブルがデータベースに追加されました。

アソシエーションの追加

テーブルの定義でも書きましたが、
ユーザー(user)と投稿(post)は1:Nの関係
となるため、その関係性をそれぞれのモデルクラスに記述します。

user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :posts
end

post.rb

class Post < ApplicationRecord
  belongs_to :user
end

これでブログを扱うためのデータが完成しました。
次回はアプリケーションの画面を作っていこうと思います。

Railsで簡易ブログを作る その1

めちゃくちゃ久しぶりにブログ書きます。
令和になってからWeb技術(主にRuby on RailsJavascript)を学び始めたので、
アウトプット兼・忘備録としてこれから記事書いてこうと思います。

何を作るか?

勉強課題でブログを作ってみましょうとあったので、
今回はシンプルなプログアプリケーションを作ろうと思います。
以下の機能を実現できるように進めていきます。

  • ユーザー管理機能(ログイン・ログアウト・サインインができる)
  • ユーザー投稿
  • 投稿後の編集
  • 投稿の削除
  • ユーザー毎にブログが閲覧できる

これ以外の機能は必要に応じて適宜拡張します。

開発環境

デプロイ先

  • Heroku

アプリケーションの新規作成

Railsの開発環境導入手順もまとめておきたいですが、
今回は既に開発環境がインストールされている前提でやります。

まずはこれでPostgreSQLを使うRailsアプリケーションを新規作成。
ついでにデータベースも作っておきましょう。

rails new blog_app -d postgresql
rails db:create

この状態でローカルでサーバーを起動し、

rails s

http://localhost:3000

にアクセスすれば

f:id:s_sikisya:20190716230131j:plain
楽しげな起動成功画面出ました!

とりあえずローカル環境ではこれで開発を進められるので、
次の記事から徐々に開発を進めていきたいと思います。

C#でボックスミューラー法による正規分布に従う乱数生成

.NET Frameworkでは一様乱数を生成するクラスとして
System.Randomクラスが提供されています。
Random クラス (System)

データにノイズを加えるときに一様乱数よりも、
正規分布に従うノイズ(ホワイトガウス)を加えたいというケースがありますが、
.NET Frameworkではそこまでは提供してくれていないので、
ボックスミューラー法による正規分布に従う乱数生成クラスを作ってみました。

ボックスミューラー法

ボックスミューラー法のアルゴリズムは単純で、
(0~1]の範囲を取る一様乱数を2つ用意し(R1,R2)、
以下の式に当てはめるだけで、
N(0.0,1.0)の正規分布に従う乱数が1組生成できます。

{ \displaystyle
Norm_1 = \sqrt{-2 \log R_1} \cos{2 \pi R_2}
}
{ \displaystyle
Norm_2 = \sqrt{-2 \log R_1} \sin{2 \pi R_2}
}

また平均値μ、分散σ^2に従う正規分布N(μ,σ^2)での乱数は、
得られた値にσを乗算して、平均値を加えることで求まります。

{ \displaystyle
Norm_1 = \sigma \sqrt{-2 \log R_1} \cos{2 \pi R_2} + \mu
}
{ \displaystyle
Norm_2 =  \sigma \sqrt{-2 \log R_1} \sin{2 \pi R_2} + \mu
}

参考
http://ja.wikipedia.org/wiki/乱数列
http://ja.wikipedia.org/wiki/ボックス=ミュラー法

プログラムソース

RandomBoxMuller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RandomBoxMuller
{
  public class RandomBoxMuller
  {
    private Random random;

    public RandomBoxMuller()
    {
      random = new Random(Environment.TickCount);
    }

    public RandomBoxMuller(int seed)
    {
      random = new Random(seed);
    }

    public double next(double mu = 0.0, double sigma = 1.0, bool getCos = true)
    {
      if (getCos)
      {
        double rand = 0.0;
        while ((rand = random.NextDouble()) == 0.0) ;
        double rand2 = random.NextDouble();
        double normrand = Math.Sqrt(-2.0 * Math.Log(rand)) * Math.Cos(2.0 * Math.PI * rand2);
        normrand = normrand * sigma + mu;
        return normrand;
      }
      else
      {
        double rand;
        while ((rand = random.NextDouble()) == 0.0) ;
        double rand2 = random.NextDouble();
        double normrand = Math.Sqrt(-2.0 * Math.Log(rand)) * Math.Sin(2.0 * Math.PI * rand2);
        normrand = normrand * sigma + mu;
        return normrand;
      }
    }

    public double[] nextPair(double mu = 0.0, double sigma = 1.0)
    {
      double[] normrand = new double[2];
      double rand = 0.0;
      while ((rand = random.NextDouble()) == 0.0) ;
      double rand2 = random.NextDouble();
      normrand[0] = Math.Sqrt(-2.0 * Math.Log(rand)) * Math.Cos(2.0 * Math.PI * rand2);
      normrand[0] = normrand[0] * sigma + mu;
      normrand[1] = Math.Sqrt(-2.0 * Math.Log(rand)) * Math.Sin(2.0 * Math.PI * rand2);
      normrand[1] = normrand[1] * sigma + mu;
      return normrand;
    }
  }
}

実行例

作成したクラスはRandom関数と同様に、
コンストラクタにシード値を指定させてオブジェクトを初期化し、
(未指定の場合はシステムの起動時刻をシード値として利用)
next関数の引数に正規分布のμとσを指定することで乱数が求まります。

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RandomBoxMuller
{
  class Program
  {
    static void Main(string[] args)
    {
      var random = new RandomBoxMuller();

      using (var sr = new System.IO.StreamWriter("output.csv"))
      {
        for (int i = 0; i < 10000; i++)
        {
          sr.WriteLine(random.next());
        }
      }
    }
  }
}


上記プログラムではN(0.0,1.0)の乱数を1万個生成しています。
得られたデータのヒストグラムは以下のようになりました。

f:id:s_sikisya:20140603132934p:plain

今回-1.0~1.0のデータは6816個得られており、
Z=1.0のときの正規分布データ点数は6826個(0.3413)
なのでほぼ近い値が得られていることが確認できました。

参考
http://www.koka.ac.jp/morigiwa/sjs/standard_normal_distribution.htm

MPU-9150をArduinoに接続して9軸モーションデータを取得する

MPU-9150

MPU-9150はGoogle Grassにも使用されている(らしい)
9軸(加速度・角速度・地磁気)モーションセンサー。
ストロベリーリナックス等でモジュールが購入できます。
https://strawberry-linux.com/catalog/items?code=12150

Arduinoに繋いでセンサーと仲良くなるためのメモ。

Arduinoを使用したデータ取得

単純に9軸データを取得するだけなら、
SparkfunがGitHubで公開している以下のソースを使うと楽。
https://github.com/sparkfun/MPU-9150_Breakout

MPU-9150_Breakoutを使った9軸データ取得

1.GitHubからリポジトリのマスターダウンロード

ページ内のDownload.Zipボタンをクリックして、
MPU-9150_Breakout-master.zip」をダウンロード。

2.ダウンロードしたファイルを解凍しライブラリに追加

MPU-9150_Breakout-master.zip」を解凍し解凍したフォルダから、

firmware\MPU6050
firmware\I2Cdev

の2つをArduino IDEのライブラリフォルダ「Arduino\libraries」にコピー。

3.Arduino IDEでスケッチを送信

MPU-9150_Breakout-master.zip」を解凍し解凍したフォルダから、

firmware\MPU6050\Examples\MPU9150_raw\MPU9150_raw.ino

Arduinoスケッチを書き込めば、
シリアル通信で9軸のデータ値が取得できます。

4.物理量として利用するには

このサンプルでは全てのセンサー値が符号付き16ビットで格納されていますが、
値磁気センサーのみ分解能が13ビットなので注意が必要です。
デフォルトでは以下のようにセンサー情報が表されています。

  • 加速度:±2G
  • 角速度:±250(degree/sec)
  • 地磁気:±1200(μT)

また物理量に変換するなら次の式を利用します。

加速度:{ \displaystyle
Acc = 2 * SensorValue / 32768
}
角速度:{ \displaystyle
{\omega} = 250 * SensorValue  / 32768
}
地磁気{ \displaystyle
Mag = 1200 * SensorValue / 4096
}

ちなみに地磁気は周囲の影響を受けまくっているので、
ぐりぐりと8の字に動かしてこの値からオフセットを求めて
補正することが必要になります。

5.もうちょっとできる子なんですが

実はこのセンサー実はもうちょっとできる子です。
センサー公式に概要が書かれていますがセンサーの中に、
Digital Motion Processing™ (DMP™) エンジンが搭載されており、
センサーフュージョンによる姿勢計算をして出力してくれます。
MEMS Gyro-Accel | Gyroscope | Accelerometer | Processing - MPU-9150 9軸(ジャイロ+加速度+コンパス)MEMS MotionTracking™ デバイス

MPU-9150_Breakout-master内の「MPU6050_DMP6.ino」は
DMPエンジンを利用したサンプルスケッチなんですが、
値磁気センサーが組み込まれていない下位モデルのMPU-6050用で、
このままだと6軸のみで地磁気のデータが取れてこない。

そこでまた色々と試行錯誤したわけですがそれは書く機会があれば。

Android Developer Toolkit が 22.6.1 にアップデートできないときの対処

Android SDKマネージャでAndroid SDK Toolsのアップデートを行ったら、
Android Developer Toolkitも更新するように表示されたので、
更新の確認からEclipseプラグインの更新を行おうとしたところ

No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.ddms,22.6.1.v201403111859-1066720

とエラーが発生し、更新できない&Androidプロジェクト全てがビルドエラー状態に。
サーバー側にデータが置かれてないのか、パスのスペルミスなのかは不明ですが、
新規にAndroid Developer Toolkitをインストールさせることで解決できた。

手順

  1. [ヘルプ]→[新規ソフトウェアのインストール]をクリック
  2. [追加]ボタンをクリック
  3. ロケーションに「https://dl-ssl.google.com/android/eclipse/」を入力し[OK]をクリック
  4. リスト内に表示された項目にチェックを入れ、[次へ]をクリック
  5. インストールダイアログの指示に従いインストール

ReadOnlyの時にチカチカしないNumericUpDownを作る

NumericUpDown

GUIで数値を上下に変化させて指定することができる、
NumericUpDownというFormコントロールがあります。
NumericUpDown クラス (System.Windows.Forms)

アプリケーションでユーザーに数値を入力してもらう際に、
ボタンを押せば一定間隔で指定ができるので、
開発側としても変な入力がされにくい便利なコントロール。

ReadOnly時のチカチカがいらないよね

f:id:s_sikisya:20140310185141p:plain
ユーザが直接値を指定して変更させないために、
ReadOnlyのプロパティが用意されていますが、
ReadOnly時でもフォーカスに入るとカーソルがチカチカと点滅。
これじゃユーザが入力できると勘違いする恐れがあります。

チカチカを消してしまうには

調べてみるとこのチカチカするヤツ、
caret(キャレット)って名前らしく、
これを消し去るにはWindows APIが必要とのこと。

参考
[C#] 独自に作成したウィンドウコントロールのコンポーネントにキャレットを表示する

チカチカしないNumericUpDownを作る

今回はSystem.Windows.Forms.NumericUpDownクラスを継承して、
ReadOnly時にキャレットを表示させないコントロールを実装してみました。

NumericUpDown継承クラスの作成

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

NumericUpDownNoBlinking.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;

namespace NumericUpDownNoBlinking
{
  public partial class NumericUpDownNoBlinking : NumericUpDown
  {

    [DllImport("user32.dll")]
    static extern bool HideCaret(IntPtr hWnd);

    //キャレット表示の有無を指定
    private bool caretVisible = true;

    //ReadOnlyメソッドを上書き
    public new bool ReadOnly
    {
      get
      {
        return base.ReadOnly;
      }
      set
      {
        base.ReadOnly = value;

        //キャレットの有無をReadOnly属性に応じて変更
        caretVisible = !value;

        //ReadOnly有効時、NumericUpDownのTextBoxコントロールにカーソルを変化させない。
        foreach (Control cntr in this.Controls)
        {
          if (cntr is TextBox)
          {
            if (caretVisible)
            {
              cntr.Cursor = Cursors.IBeam;
            }
            else
            {
              cntr.Cursor = Cursors.Arrow;
            }
          }
        }
      }
    }

    public NumericUpDownNoBlinking()
    {
      InitializeComponent();
    }

    public NumericUpDownNoBlinking(IContainer container)
    {
      container.Add(this);

      InitializeComponent();
    }

    //フォーカス取得時にキャレットを消去
    protected override void OnGotFocus(EventArgs e)
    {
      base.OnGotFocus(e);

      if (!caretVisible)
      {
        //NumericUpDownのTextBoxコントロールのキャレットを非表示にする
        foreach (Control cntr in this.Controls)
        {
          if (cntr is TextBox)
          {
            HideCaret(cntr.Handle);
          }
        }
      }
    }
  }
}

解説

Windwos APIのHideCaret関数を使えるように、
宣言しておきます。

[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);

コントロールのOnGotFocusメソッドをオーバーライドし、
フォーカス取得時にControlsプロパティに格納されている
TextBoxのHandleをHideCaret関数の引数に渡せばOK。

protected override void OnGotFocus(EventArgs e)
{
  base.OnGotFocus(e);

  if (!caretVisible)
  {
    //NumericUpDownのTextBoxコントロールのキャレットを非表示にする
    foreach (Control cntr in this.Controls)
    {
      if (cntr is TextBox)
      {
        HideCaret(cntr.Handle);
      }
    }
  }
}

NumericUpDownのHandleではなくTextBoxを指定することがポイント!

また上記プログラムでは、
テキストボックス上にマウスカーソルが移動しても、
マウスポインタの形状を矢印のままにしています。
こちらもControlsプロパティに格納されているTextBoxに対して、
プロパティを変更することで対処しています。

参考
ネコキス: NumericUpDown のキャレットを消す
[C#] 独自に作成したウィンドウコントロールのコンポーネントにキャレットを表示する

C# Fromで動画再生するには

WPFやWinRTならXAMLベースのMediaElementクラスを使うことが、
MSDNでも書かれているように鉄板だと思います。
http://msdn.microsoft.com/ja-jp/library/aa970915(v=vs.110).aspx

しかし現在自分が今行っている案件はWindows Form開発。
Formアプリケーションではどうするのって事になったので、
調べてみた。

WindowsMediaPlayerコントロールを使う

一番簡単に実装するならコレが手っ取り早い模様。
再生、停止、スクロール等のUI操作は、
全部このコントロールにおまかせできます。

コントロール追加方法
  1. VisualStudioから[ツール]→[ツールボックス アイテムの選択]をクリック。
  2. [COMコンポーネント]のタブを選択
  3. [Windows Media Player]にチェックを入れOKをクリック
  4. ツールボックスに追加された[Windows Media Player]をFormに貼り付け

f:id:s_sikisya:20140303095357p:plain
f:id:s_sikisya:20140303095857p:plain

これだけでプレイヤーが画面に追加できます。
再生時にはコントロールのURLプロパティに、
ファイルパスを指定するだけでOK。

axWindowsMediaPlayer1.URL = "moviefile.wmv";

ちょっと気になるところ
  • コマ送り再生ができない
  • WindowsMediaPlayerの外観使ってます感バリバリ
  • UI部分のカスタマイズができない
  • 一時停止中にシークしてもサムネイルが移動しない

これらの点が気に食わないので、
楽せずにやる方法で現在は実装中。
具体的にはC#でDirectShowを使ってます。
DirectShowについては今後まとめていく予定。