OTO - Ohshima Tamashima Observatory- - ProgramAutoGuide Diff
- Added parts are displayed like this.
- Deleted parts are displayed
like this.
!Arduinoによる望遠鏡のリアルタイム制御プログラミング
OTO30cmフリクション赤道儀改良で、私が実際に行ったファームウェア開発について記す。
ここでいうリアルタイム制御とは、手動(自動)ガイドや目的天体導入時の操作に要求されるような0.1秒以下(実際には1msecから100msecの間)の応答速度で、信号を処理しモータを制御することを意味している。(2018.08.28)
!!制御はすべてソフトウェア(ファームウェア)で処理
望遠鏡の手動による天体導入やガイド、ガイドカメラによる自動ガイドを行う場合、(手動かオートガイダーかはともかく)この接点がONになったら、このモータをこの速さでこの方向に回すという仕組みが望遠鏡のシステムには必要である。
古典的な望遠鏡では、この仕組は、すべて結線により、スイッチ、リレー、モーター、発振器などを接続して行ってきた。近代的な望遠鏡では、ハードウェア配線でなく、これらの制御はマイコンとそのソフトウェア(ファームウェア)で行う。これは、使われるモーターに精密さが要求され、望遠鏡制御にはもっぱらステッピングモーターやサーボモーターが使われるようになったためであり、それらの制御にはすべてパルスが使われ、マイコンとの組み合わせるのが相性がとても良いためである。
!!実際のstatus信号
望遠鏡のハンドセットには、私の望遠鏡の場合、速度モード選択スイッチが3速種(早回し速slew、設定設定速set、ガイド速guide、)モード選択スイッチとと東西南北EWSNの4つの押しボタンスイッチがある。また、緊急停止ボタンの1ビットもある。小型CCD(CMOS)カメラを使ったオートガイダーもEWNSの4接点の信号が取り出される。合計112ビットの信号をここでは制御することを考える。この内、手動ボタンのEWNS信号は、選択された速度モード信号と必ず組み合わせて使われるが、ガイダーのEWNS信号は常にGuide速で使われる。
[[signal|http://otobs.org/tech/30cmtelescope/AGsgn.png]]
!!信号の変化と求められる動作
今、南北方向の押しボタンが押された場合の2ビットの信号を考える(手動ボタンでもオートガイダーでも同じ。また、東西方向の2ビット信号でも同様である)。
[[signal Change|http://otobs.org/tech/30cmtelescope/AGstateChange.png]]
図に従って、ボタンの状態とその信号変化をまとめてみる状態(2進数表示)とその信号変化(10進数表示)を列挙する。
*ボタンが押されていない時は2ビット信号は2進数表示で"00"である。
*Sボタンが押されると信号は"01"になり、今の信号−前の信号=+1という変化が起こる。→モータをS方向へ回転される回転させる。
*次に、押すのをやめると信号は"00"に戻り、信号の変化は-1である。→回転していたモータを止める。
*Nボタンが押されると信号は"10"になり、信号の変化は+2である。→モーターをN方向へ回転させる。
*Nボタンから手を離すと、信号"10"が"00"に変わり、その信号の変化は-2である。→モータの回転を停止する。
*めったに手動操作ではまず起こらないが、オートガイダーではありうる例で、Sボタンが押された状態から突然Nボタンが押された状態に変化する場合は、信号は"01"から"10"へ変わり、その変化は+1である。→S方向に回っていたモータを止める→次にモータをN方向に回転させる。
*この逆の場合は、信号変化は-1である。→N方向に回っていたモータを止める→次にモータをS方向に回転させる。
*NとSのボタンの同時押しは同時押し(”11”)は、意味のない信号なので無視する。
こうして、信号の変化をどう処理するのかは処理するのか(反応動作)は、元の信号状態と、信号が変化した量の2つがわかれば、判断できることがわかる。
以上の信号処理をC言語の関数にした一方、反応動作の種類は、上記一覧からまとめると、(1)モーターの回転を停止(*1)する、(2)モータを正転させる、(3)モータを逆転(*2)させる、(4)モータを一旦停止させた後に正転する、(5)モータを一旦停止させた後に逆転するの5種類があることがわかる。しかし(2)と(3)は、一旦停止を最初に行うことで、それぞれ(4)と(5)と同じ動作になる(*3)。
こうして、反応動作の種類を3種類に抑えることができる。
(*1)RAでは恒星時追尾
(*2)RA方向では、E方向のガイド速が恒星時追尾速の50%の場合は、恒星時の50%速で正転
(*3)一旦停止を入れることでわずかに動作開始が遅れるが、その処理時間は1ミリ秒以下なので、その遅れは実用上無視できる。
以上の信号処理をArduinoのC言語の関数にした。
<<<
byte isNext(byte pre, char diff){
byte nxdo;
if (pre == B00) { //pre is nothing
if (diff == 1) {nxdo = 1;} // w inRA
if (diff == 2) {nxdo = 2;} // e inRA
}
if (pre == B01) { //pre is +
if (diff ==-1){nxdo = 0;} // sd inRA
if (diff == 1){nxdo = 2;} // e inRA
}
if (pre == B10){ //pre is -
if (diff == -2){nxdo = 0;} // sd inRA
if (diff == -1){nxdo = 1;} // w inRA
}
return(nxdo);
}
>>>
引数は、preが元の信号状態で diffが信号の変化を変化(=現在の状態−以前の状態)を表す。
"//"のコメント部は赤経方向の場合の例である。
次に行う動作を0,1,2で表し、戻し値にしている。
この戻し値を受け取ったら取ったプログラムは、次のプログラミはその戻し値によって次のような処理(反応動作)を行う。
0は、NS方法ならば方向では「モーター停止であるが停止」であるが、EW方向では「恒星時追尾でW方向へ回す」、である。
1は、正転開始、2は逆転開始であるが、これはこれはそのマシンでの定義によっては逆の場合もある。
また、1も2もその動作の前にかならず「一旦モーターを停止させる」動作を入れてある。それは
!!リアルタイム制御関数の実際
以下のプログラム(関数)は、2つの反応動作を1つの処理で済ますように簡略化したハンドセット信号とオートガイダー信号を受け取り、どのように処理するかを決めるもので、この中で上に示したisNext()関数を使っている。つまり
関数の引数、(1)正転(逆転)開始と(2)動作停止→正転(逆転)開始の2つをspeedは、緊急停止"E"、早回し速"L"、設定速"T"、ガイド速"G"のどれかを与える(1ただし、オートがダー信号の処理ではspeedは無視され常にGuide速になる)の方にも一旦停止を加えている。そのためわずかに動作開始が遅れるがdirは、1ミリ秒以下の処理なのでこの記事の最初に示した図の下位8ビット信号である。
なお、その時間は実用上無視できるからである途中の多数のコメントアウトされたSerial.print()などは、デバッグのためのシリアル出力であり、実用時には機能させない。
<<<
void moveDir(char speed, byte dir){
static byte prevState=0; //initialized at only the first exection of
//the main function. not initialized at every this function call.
if (speed == 'E'){
stopEM(); siderealOFF();return;
}
byte nowState = dir; //上位4bitsがAG信号、下位4bitsがHSet信号
//for AutoGuider
byte nowAg = (nowState & B11110000) >> 4;
byte prAg = (prevState & B11110000) >> 4;
nowAg = agc[nowAg]; //bit順が不揃いのAg接点信号を正規の順序に変換する配列
prAg = agc[prAg]; // 同上
byte prRA = (prAg & B11); // 上位2bitsがDec信号、下位2bitsがRA信号
byte prDec = (prAg & B1100) >> 2;
char diffDec = (char)(nowAg >> 2)-(char)(prAg >> 2);
char diffRA = (char)(nowAg & B0011)-(char)(prAg & 0b0011);
byte nxdoRA = isNext(prRA, diffRA);
byte nxdoDec = isNext(prDec, diffDec);
if (nowAg ^ prAg){
if (diffRA){ //RA入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("AgRA state change: ");Serial.print(prRA,BIN);
// Serial.print(" ");Serial.println(diffRA,BIN);
DebugPrint("Debug:");
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startGuide('w'); break;
case 2: startGuide('e'); break;
default: Serial.print("isNext_error !! ");
Serial.println(nxdoRA);break;
}
}
if (diffDec){ //Dec入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("AgDec state change:");Serial.print(prDec,BIN);
// Serial.print(" ");Serial.println(diffDec,BIN);
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startGuide('n'); break;
case 2: startGuide('s'); break;
default: Serial.print("isNext_error !! ");
Serial.println(nxdoDec); break;
}
}
}
//for HandSet
byte nowHd =(nowState & B00001111) ;
byte prHd =(prevState & B00001111);
if (nowHd ^ prHd){
diffDec = (char)(nowHd >>2)-(char)(prHd >>2);
diffRA = (char)(nowHd & B0011)-(char)(prHd & 0b0011);
prDec = (prHd >> 2);
prRA = (prHd & B11);
if (diffRA ){ //RA入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("HdRA state change:");Serial.print(prRA,BIN);
// Serial.print(" ");Serial.println(diffRA,BIN);
// DebugPrint("Debug:");
nxdoRA = isNext(prRA, diffRA);
Serial.print("nedoDec=");Serial.println(nxdoRA);
if (speed == 'G'){
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startGuide('w'); break;
case 2: startGuide('e'); break;
default: Serial.print("isNext_error !!");
Serial.print(nxdoRA); break;
}
}
else if (speed == 'T'){
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startSet('w'); break;
case 2: startSet('e'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoRA); break;
}
}
else if (speed == 'L') {
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startSlew('w'); break;
case 2: startSlew('e'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoRA); break;
}
}
}
if (diffDec){ //Dec入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("HdDec state change:");Serial.print(prDec,BIN);
// Serial.print(" ");Serial.println(diffDec,BIN);
nxdoDec = isNext(prDec, diffDec);
Serial.print("nedoDec=");Serial.println(nxdoDec);
if (speed == 'G'){
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startGuide('n'); break;
case 2: startGuide('s'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoDec); break;
}
}
else if (speed == 'T'){
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startSet('n'); break;
case 2: startSet('s'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoDec); break;
}
}
else if (speed == 'L') {
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startSlew('n'); break;
case 2: startSlew('s'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoDec); break;
}
}
}
}
prevState = nowState;
}
>>>
OTO30cmフリクション赤道儀改良で、私が実際に行ったファームウェア開発について記す。
ここでいうリアルタイム制御とは、手動(自動)ガイドや目的天体導入時の操作に要求されるような0.1秒以下(実際には1msecから100msecの間)の応答速度で、信号を処理しモータを制御することを意味している。(2018.08.28)
!!制御はすべてソフトウェア(ファームウェア)で処理
望遠鏡の手動による天体導入やガイド、ガイドカメラによる自動ガイドを行う場合、(手動かオートガイダーかはともかく)この接点がONになったら、このモータをこの速さでこの方向に回すという仕組みが望遠鏡のシステムには必要である。
古典的な望遠鏡では、この仕組は、すべて結線により、スイッチ、リレー、モーター、発振器などを接続して行ってきた。近代的な望遠鏡では、ハードウェア配線でなく、これらの制御はマイコンとそのソフトウェア(ファームウェア)で行う。これは、使われるモーターに精密さが要求され、望遠鏡制御にはもっぱらステッピングモーターやサーボモーターが使われるようになったためであり、それらの制御にはすべてパルスが使われ、マイコンとの組み合わせるのが相性がとても良いためである。
!!実際のstatus信号
望遠鏡のハンドセットには、私の望遠鏡の場合、速度モード選択スイッチが3
[[signal|http://otobs.org/tech/30cmtelescope/AGsgn.png]]
!!信号の変化と求められる動作
今、南北方向の押しボタンが押された場合の2ビットの信号を考える(手動ボタンでもオートガイダーでも同じ。また、東西方向の2ビット信号でも同様である)。
[[signal Change|http://otobs.org/tech/30cmtelescope/AGstateChange.png]]
図に従って、ボタンの
*ボタンが押されていない時は2ビット信号は2進数表示で"00"である。
*Sボタンが押されると信号は"01"になり、今の信号−前の信号=+1という変化が起こる。→モータをS方向へ
*次に、押すのをやめると信号は"00"に戻り、信号の変化は-1である。→回転していたモータを止める。
*Nボタンが押されると信号は"10"になり、信号の変化は+2である。→モーターをN方向へ回転させる。
*Nボタンから手を離すと、信号"10"が"00"に変わり、その信号の変化は-2である。→モータの回転を停止する。
*
*この逆の場合は、信号変化は-1である。→N方向に回っていたモータを止める→次にモータをS方向に回転させる。
*NとSのボタンの
こうして、信号の変化をどう
こうして、反応動作の種類を3種類に抑えることができる。
(*1)RAでは恒星時追尾
(*2)RA方向では、E方向のガイド速が恒星時追尾速の50%の場合は、恒星時の50%速で正転
(*3)一旦停止を入れることでわずかに動作開始が遅れるが、その処理時間は1ミリ秒以下なので、その遅れは実用上無視できる。
以上の信号処理をArduinoのC言語の関数にした。
<<<
byte isNext(byte pre, char diff){
byte nxdo;
if (pre == B00) { //pre is nothing
if (diff == 1) {nxdo = 1;} // w inRA
if (diff == 2) {nxdo = 2;} // e inRA
}
if (pre == B01) { //pre is +
if (diff ==-1){nxdo = 0;} // sd inRA
if (diff == 1){nxdo = 2;} // e inRA
}
if (pre == B10){ //pre is -
if (diff == -2){nxdo = 0;} // sd inRA
if (diff == -1){nxdo = 1;} // w inRA
}
return(nxdo);
}
>>>
引数は、preが元の信号状態で diffが信号の
"//"のコメント部は赤経方向の場合の例である。
次に行う動作を0,1,2で表し、戻し値にしている。
この戻し値を受け
0は、NS
1は、正転開始、2は逆転開始であるが、
また、1も2もその動作の前にかならず「一旦モーターを停止させる」動作を入れてある。
!!リアルタイム制御関数の実際
以下のプログラム(関数)は、
関数の引数、
なお、
<<<
void moveDir(char speed, byte dir){
static byte prevState=0; //initialized at only the first exection of
//the main function. not initialized at every this function call.
if (speed == 'E'){
stopEM(); siderealOFF();return;
}
byte nowState = dir; //上位4bitsがAG信号、下位4bitsがHSet信号
//for AutoGuider
byte nowAg = (nowState & B11110000) >> 4;
byte prAg = (prevState & B11110000) >> 4;
nowAg = agc[nowAg]; //bit順が不揃いのAg接点信号を正規の順序に変換する配列
prAg = agc[prAg]; // 同上
byte prRA = (prAg & B11); // 上位2bitsがDec信号、下位2bitsがRA信号
byte prDec = (prAg & B1100) >> 2;
char diffDec = (char)(nowAg >> 2)-(char)(prAg >> 2);
char diffRA = (char)(nowAg & B0011)-(char)(prAg & 0b0011);
byte nxdoRA = isNext(prRA, diffRA);
byte nxdoDec = isNext(prDec, diffDec);
if (nowAg ^ prAg){
if (diffRA){ //RA入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("AgRA state change: ");Serial.print(prRA,BIN);
// Serial.print(" ");Serial.println(diffRA,BIN);
DebugPrint("Debug:");
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startGuide('w'); break;
case 2: startGuide('e'); break;
default: Serial.print("isNext_error !! ");
Serial.println(nxdoRA);break;
}
}
if (diffDec){ //Dec入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("AgDec state change:");Serial.print(prDec,BIN);
// Serial.print(" ");Serial.println(diffDec,BIN);
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startGuide('n'); break;
case 2: startGuide('s'); break;
default: Serial.print("isNext_error !! ");
Serial.println(nxdoDec); break;
}
}
}
//for HandSet
byte nowHd =(nowState & B00001111) ;
byte prHd =(prevState & B00001111);
if (nowHd ^ prHd){
diffDec = (char)(nowHd >>2)-(char)(prHd >>2);
diffRA = (char)(nowHd & B0011)-(char)(prHd & 0b0011);
prDec = (prHd >> 2);
prRA = (prHd & B11);
if (diffRA ){ //RA入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("HdRA state change:");Serial.print(prRA,BIN);
// Serial.print(" ");Serial.println(diffRA,BIN);
// DebugPrint("Debug:");
nxdoRA = isNext(prRA, diffRA);
Serial.print("nedoDec=");Serial.println(nxdoRA);
if (speed == 'G'){
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startGuide('w'); break;
case 2: startGuide('e'); break;
default: Serial.print("isNext_error !!");
Serial.print(nxdoRA); break;
}
}
else if (speed == 'T'){
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startSet('w'); break;
case 2: startSet('e'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoRA); break;
}
}
else if (speed == 'L') {
switch( nxdoRA ){
case 0: stopRA(); break;
case 1: startSlew('w'); break;
case 2: startSlew('e'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoRA); break;
}
}
}
if (diffDec){ //Dec入力信号に変化があれば以下の処理を行う
// Serial.print("pre=");Serial.print(prevState, BIN);
// Serial.print(" now=");Serial.println(nowState, BIN);
// Serial.print("HdDec state change:");Serial.print(prDec,BIN);
// Serial.print(" ");Serial.println(diffDec,BIN);
nxdoDec = isNext(prDec, diffDec);
Serial.print("nedoDec=");Serial.println(nxdoDec);
if (speed == 'G'){
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startGuide('n'); break;
case 2: startGuide('s'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoDec); break;
}
}
else if (speed == 'T'){
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startSet('n'); break;
case 2: startSet('s'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoDec); break;
}
}
else if (speed == 'L') {
switch( nxdoDec ){
case 0: stopDec(); break;
case 1: startSlew('n'); break;
case 2: startSlew('s'); break;
default: Serial.println("isNext_error !!");
Serial.print(nxdoDec); break;
}
}
}
}
prevState = nowState;
}
>>>