ゴルフ感覚で

rubyで式量計算
私は、データ処理の大部分にrubyを使っている。物質を扱っていると、式量を計算する必要が出てくることがあるが、これもrubyで計算している。具体的には、formula.datに元素記号と原子量のデータを入れておいて、化学式の中に含まれる、元素記号と数字を読み取って、原子量と数をかけて足している。かなり前に書いたスクリプトがこれである。

def fmlwt(fml)
  awt={}
  IO::foreach("formula.dat") do |l|
    awt[$1]=$2.to_f if l=~/([A-Z][a-z]?)\s+([\d\.]*)/
  end
  fw=0
  while fml.size>0
    break unless m=/([A-Z][a-z]?)([\d\.]*)/.match(fml)
    aw=awt[m[1]]
    an=(m[2]=="")?1:m[2].to_f
    fw+=an*aw
    fml=m.post_match
  end
  fw=1 if fw==0
  return fw
end

原子量のデータを一行ずつ読んで、hashに記録して、その後で化学式を一元素ごとに正規表現でマッチして処理している。しかし、かなり長い気がする。そこで、久しぶりに書き直してみた。まず、書いたのがこれ。

def fmlwt2(fml)
  awt=Hash[*IO.readlines("formula.dat").map{|l| l.strip.split(/\s+/)}.flatten]
  fml.split(/(?=[A-Z])/).map{|e|
    awt[$1].to_f*(($2=="")?1:$2.to_f) if e=~/([A-Z][a-z]?)([\d\.]*)/
    }.inject{|s,x| s+x}
end

原子量のデータは一気に読んで行列にして、一行の前後の空白を除去してから空白で区切って、それをhashに変換している。ここで、コードの節約のために、原子量はまだ文字列のままである。その後で、各元素ごとに区切ってから、正規表現を使って処理して、それを最後にinjectで足している。だんだんゴルフ感覚になってきた。もう少し短くできた。

def fmlwt3(fml)
  awt=Hash[*IO.read("formula.dat").strip.split(/\s+/m)]
  fml.split(/(?=[A-Z])/).grep(/([A-Z][a-z]?)([\d\.]*)/){
    awt[$1].to_f*(($2=="")?1:$2.to_f)
    }.inject{|s,x| s+x}
end

ファイルを行をくっつけて読み込んで、それをそのまま配列にしてからhashにしている。後半はgrepを使って正規表現部分をすっきりとさせた。昔書いたコードを見ると、美しく感じられないことが多い。徐々にスクリプトを書く流儀が変化しているからなのだろうが、まだまだ未熟な気がする。しかし、短く書けば良いというものでも無い。後半の処理は良くなったと思うのだが、原子量のhashを作る部分は、最初のやつの方が何をしているのかはっきりしているようにも感じられる。化学式に関して言えば、本当は括弧などにも対応したいのだが、再帰を使えば出来そうな気がしているが、まだ手をつけていない。しかし、こういったプログラムを使うときには、単純な入力ミスが、誤ったデータ処理につながるので、逐一ミスが無いことをチェックすることが重要である。

2010/8/15追記 分かりやすさと短さを考えて、こんなのはどうだろうか。

def fmlwt4(fml)
  awt=Hash[*IO.read("formula.dat").strip.split(/\s+/m)]
  r=0
  fml.scan(/([A-Z][a-z]?)([\d\.]*)/){|s|
    s[1]=1 if s[1]==""
    r+=awt[s[0]].to_f*s[1].to_f
    }
  r
end

2010/10/11追記 scanを{|a,n|}で受ければ、s[0]とか書かないでも良くなることに気がついた。

Read more...

参照と値

rubyにおける参照と値
普段はあまり意識していないのだが、rubyでの参照と値の違いを考えてみた。まず、簡単な例を実行してみた。

a=1; b=a; b=2; p a #1

まあ、これは当然な結果だ。一方、配列を使うと少し違う結果が得られる。

a=[1]; b=a; b[0]=2; p a #[2]

配列の代入では、参照が代入されていることが分かる。しかし、別の配列を代入すれば、元と同じような結果が得られる。

a=[1]; b=a; b=[2]; p a #[1]

つまり、参照が代入されているので、破壊的メソッドを使うと、元の変数も変化するが、新しく別の値を代入しても、元の変数は変化しないのだ。配列は簡単な計算をするときによく使うので、注意が必要だ。配列の値自身を代入したいときには、

a=[1]; b=a.clone; b[0]=2; p a #[1]

とすれば良い。しかし、これも完全ではなく、

a=[[1]]; b=a.clone; b[0][0]=2; p a #[[2]]

となってしまう。行列などでは、cloneでは値の複製はできないのだ。ちなみに、rubyのメソッドはすべて値渡しらしい。もっとも、オブジェクトに破壊的なメソッドを使うと、元のオブジェクトも変化してしまうが。何だかややこしい。rubyの変数はすべて参照で、メソッドではその参照が値渡しされるということだろうか。

Read more...

AVR tiny261を使って

マイコン制御サイリスタ

ゼロクロスのサイリスタを使っているが、電流計でモニタしてみると、電流のON/OFFの頻度がそれほど高くないようで、電流計の読みがぶれる。基本的には、電源が50Hzだとすると半波を数えると100Hzとなり、出力の大きさにもよるが、ON/OFFのタイミングは、少なくとも10Hz程度はあるはずで、電流計のぶれもそれほど大きくはならないように感じる。オシロで見てみると、一秒間に数回しか切り替えていないように見える。電流が大きくなると、サイリスタはノイズを発生するので、ノイズの少ないゼロクロス方式を使おうと考えているのだが、電流計のぶれが気になる。

気に入らなければ作るしかないかということで、以前計画したAVRを用いてサイリスタユニットを作る計画を実行に移すことにした。石としては、AD変換を使いたいので、tiny261を使うことにする。基本的な回路は以前書いた通りだが、マイコンはトランスで電気的に浮いているので、入力は直接抵抗に流して、その電圧を読むことにした。

電源電圧がゼロになるところは、トランスで電圧を落として、整流して片側だけにした電圧をツェナーダイオードで頭を落としたものを、INT0/ADC9とINT1/ADC2に入力する。そして、falling edgeを割り込みで検出して、そのあとAD変換して、約1V以下になるのを待っている。回路の特性なのか、1V以下になったあとも、なかなか0Vにはならず、だらだらとdecayするので、例えば0.5Vになるのを待っていると、電源電圧のゼロを検出できないようだ。アナログ比較器を使おうかとも思ったが、配線をやり直すのが面倒なので止めにした。

あとは、読み取った出力に応じて、パルスを出す場合と出さない場合を判断するだけだ。しかし、出力がちょうど50%のときには、交流の電圧が正または負の片側だけしか使わないことになってしまうのだが、これは問題ないのだろうか。なんだか気持ち悪い。まあ、そんな偶然はあまり無いから良いか。

とりあえず、汚いけどソースはこんな感じ。

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 1000000
#include <util/delay.h>
#define setbit(PORT,BIT) PORT|=_BV(BIT)
#define clearbit(PORT,BIT) PORT&=~_BV(BIT)
#define checkbit(PORT,BIT) (PORT&_BV(BIT))
#define POWER_ZERO 45 // 220ohm*4mA
#define POWER_MAX 180 // 220ohm*(20-4)mA
static volatile unsigned int power=0;
// AD converter
void adc_init(){
  clearbit(ADMUX,REFS1);  // Vcc
  clearbit(ADMUX,REFS0); // Vcc
  setbit(ADMUX,ADLAR); // bit3-9
  clearbit(ADCSRB,MUX5); // ADC setting
  clearbit(ADCSRA,ADPS2); // CK/4 
  setbit(ADCSRA,ADPS1); // CK/4
  clearbit(ADCSRA,ADPS0); // CK/4
}
int adc_read(){
  unsigned char c;
  setbit(ADCSRA,ADSC); // adc start
  _delay_us(16);
  while(checkbit(ADCSRA,ADIF)==0);
  c=ADCH;
  setbit(ADCSRA,ADIF);
  return c;
}
void pulse(unsigned char c){
  setbit(ADCSRA,ADEN); // enable adc
  while(adc_read()>0x40);
  setbit(PORTB,c);
  _delay_ms(1);
  clearbit(PORTB,c);
  clearbit(ADCSRA,ADEN); // disable adc
}
void read_power(){
  unsigned int c;
  setbit(ADCSRA,ADEN); // enable adc
  ADMUX&=0xf0;
  ADMUX|=0x00; //ADC0
  adc_read();
  c=adc_read();
  c+=adc_read();
  c/=2; //average
  if(c>POWER_ZERO+POWER_MAX){c=POWER_ZERO+POWER_MAX;}
  if(c>POWER_ZERO){power+=c-POWER_ZERO;}
  clearbit(ADCSRA,ADEN); // disable adc
}
void pin_init(){
  clearbit(GIMSK,INT0); /* disable INT0 */
  clearbit(GIMSK,INT1); /* disable INT1 */
  setbit(MCUCR,ISC01); /* interrupt falling */
  clearbit(MCUCR,ISC00); /* 00:L, 10:fall, 11:raise */
  setbit(GIMSK,INT0); /* enable INT0 */
  setbit(GIMSK,INT1); /* enable INT1 */
}
ISR(INT0_vect){
  cli();
  ADMUX&=0xf0;
  ADMUX|=0x09; //ADC9
  if(power>=POWER_MAX){power-=POWER_MAX;pulse(0);}
  read_power();
  sei();
}
ISR(INT1_vect){
  cli();
  ADMUX&=0xf0;
  ADMUX|=0x02; //ADC2
  if(power>=POWER_MAX){power-=POWER_MAX;pulse(1);}
  read_power();
  sei();
}
int main()
{
  cli(); /* disable interuption */
  PORTA=0x00;
  PORTB=0x00;
  DDRA=0x00;
  DDRB=0x03;
  pin_init();
  adc_init();
  _delay_ms(100);
  sei();
  for(;;);
}
あとは、パルス出力をフォトサイリスタで受けて、その出力をメインのサイリスタに入れるだけのはずだ。しかし、極性などを間違え無いように気を使わないといけない。
Read more...

小数の計算

AVRの浮動小数点
AVRで電圧を読んで、それを計算していたが、その時に浮動小数点を使ってみた。普通にコンパイルは通るのだが、いざAVRに書き込もうとすると、エラーが出る。いろいろと調べてみると、AVRの浮動小数点は、サイズも大きくなるし速度も遅くて、あまり良くないので、固定小数点を使うべきらしい。固定小数点というと難しそうだが、要するに何倍かして、基本的には整数で計算するということだろう。エラーの原因が分かって納得がいった。

Read more...

微妙なgain

tiny261のADコンバーター AVRのADコンバーターの使い方も少しずつ分かってきて、いろいろと試している。0.3Vぐらいの電圧を精度良く測定しようとしているのだが、意外に難しい。まずは、ADコンバーターを初期化する。 void ad_init(){ setbit(ADCSRB,REFS2); // 2.56V setbit(ADMUX,REFS1); // 2.56V clearbit(ADMUX,REFS0); //2.56V clearbit(ADMUX,ADLAR); // bit0-9 setbit(ADCSRB,MUX5); // ADC0-1 clearbit(ADMUX,MUX4); // ADC0-1 clearbit(ADMUX,MUX3); // ADC0-1 clearbit(ADMUX,MUX2); // ADC0-1 clearbit(ADMUX,MUX1); // ADC0-1 setbit(ADMUX,MUX0); // ADC0-1 setbit(ADCSRB,GSEL); // gain 8 setbit(ADCSRA,ADPS2); // CK/128 setbit(ADCSRA,ADPS1); // CK/128 setbit(ADCSRA,ADPS0); // CK/128 setbit(ADCSRA,ADEN); // enable setbit(ADCSRA,ADIE); // interupt enable } ちなみに、ヘッダーでは #define setbit(PORT,BIT) PORT|=_BV(BIT) #define clearbit(PORT,BIT) PORT&=~_BV(BIT) #define checkbit(PORT,BIT) (PORT&_BV(BIT)) と定義しておく。そして、割り込みで結果を受け取る。 ISR(ADC_vect){ unsigned int c; c=ADCL; c|=(unsigned int)ADCH<<8; } 最初は1.
Read more...

液晶ディスプレイの故障

LEDバックライト
本日、メンバーの液晶ディスプレイが故障した。微妙に表示はしているのだが、色が変になって、チラチラするようになった。最近、ディスプレイの故障が多い気がする。暑いからなのかも知れない。ディスプレイが映らないと仕事にならないということで、近くの店で買ってきた。IODATAの23型だが、バックライトがLEDである。まず感じたのは、非常に薄くて軽いということである。これまでの液晶では、バックライトの陰極管のための高電圧発生回路が必要だし、二つの陰極管の光を、面状に分散させるための板なども必要だった。LEDバックライトはどのような仕組みになっているのかはまだ知らないが、面状に発光するために、薄くできるのかも知れない。いつかLEDバックライトの液晶ディスプレイが故障した時に、仕組みを調べてみよう。

Read more...

3日目のrails

railsのモデル
ruby on railsは、MVCという概念を採用している。これは、model, view, conrtollerに分けて設計するというものである。viewは、いわゆる見た目を定義する部分で、この意味合いは理解しやすい。残りのmodelとcontrollerが具体的に何を意味しているのかは、私には漠然として理解できていない。 modelの使用法の一つとして、入力されたデータを検証するというものがある。app/modelsの下にあるファイルに記述すると、データが適切でない場合に警告が出るようになる。その記述するコマンドにどのようなものがあるかを調べたところ、以下のようなものがあることが分かった。

validates_acceptance_of
validates_associated
validates_confirmation_of
validates_each
validates_exclusion_of
validates_format_of
validates_inclusion_of
validates_length_of
validates_numericality_of
validates_presence_of
validates_size_of
validates_uniqueness_of

使い方が明らかなものもあるが、複雑そうなものもある。徐々に使い方を覚えていきたい。

Read more...

自作サイリスタ

サイリスタユニットの設計
AVRを使って、サイリスタユニットを作れる気がしたので、いろいろと考えてみた。いくつかの解決すべき問題がある。 まずは、信号の入力をどうするかである。電気的に絶縁した状態で、4-20mAの信号を読み取る必要があるのだが、アナログフォトカプラというものがあるようで、電流の違いを抵抗の変化として検出できるようだ。 次の問題は、AC電源の位相をどのように検出するかである。いくつかの方法があるようだが、トランスで降圧した電圧をブリッジダイオードで正にした電圧を、ツェナーダイオードを通すことによって、ゼロ付近の電圧になったのをデジタル信号としてとらえることができる。ADを持つAVRならば、それを電圧として読み取ることもできる。 最後の問題は、AC電源の正と負をどのようにして判別するかである。トライアックを用いる場合には、これは問題にならないが、サイリスタの場合には正と負のそれぞれで信号を出さなければいけない。センター端子のあるトランスでそれをGNDにして、ダイオードで整流することにすれば、正と負のそれぞれの電圧を別々の信号として受けることができる。 マイコンでこれらの信号を受けて、適切なタイミングでフォトトライアックを介して、トライアックを制御すれば、サイリスタユニットが完成するはずである。 今度、試しに作ってみよう。

Read more...

サイリスタユニット
電力の制御が必要な時には、ソリッドステートリレーまたはサイリスタを使用する。後者としては、これまでサイリスタユニットを購入して利用していた。これは信号の入力にしたがって、出力を制御するユニットです。箱に組み込もうとしたのだが、放熱器は箱の外に出ていた方が良いだろうということで、分解して放熱器以外を箱の中に入れ、放熱器が外にでるようにした。その時にサイリスタユニットの構造をざっと見たのだが、基本的には、制御回路がサイリスタモジュールに適切なタイミングでトリが信号を送るようになっているようだ。 折角なので、サイリスタモジュールの制御の仕方を調べてみた。昔からよく使われている回路では、ダイアックを使ってトリガを作って、これでトライアックなどのサイリスタモジュールを制御する場合が多いようです。最近の回路では、マイコンを使ってトリがを生成する場合もあるらしい。フォトカプラで適切に絶縁しないといけないが、余分な回路が不要になるので、単純化できる気がする。いつかAVRでサイリスタユニットを作ってみようかな。

Read more...

2日目のrails

railsで二つのデータ
人の名前を選べるようにしてみた。まず、

rails use
cd use
ruby script/generate scaffold member name:string affiliation:string
rake db:migrate
ruby script/generate scaffold use from:time to:time member_id:integer
rake db:migrate

として、人のデータベースと、時間のデータベースを作る。そして、app/models/use.rbにbelongs_to :memberと追記して、 app/views/uses/index.html.erbとshow.html.erbのuse.member_idをuse.member.nameに変える。そして、app/views/uses/new.html.erbとedit.html.erbのf.text_field :member_idを

select("use","member_id",Member.find(:all).collect{|i| [i.name,i.id]})

に変更する。これで、member_idをリストから選べるようになった。

Read more...