HOME

送信機のPCMモードのトレーナー信号のデコード

2008/11/10


PPMでパルス幅が測定できたので、同じ回路で送信機のモードをPCMにして トレーナー信号のデータを採ってみました。送信機はJR製PCM9Xです。
これをデコードして、サーボ駆動パルスを作ってみようと思います。
参考にしたのは、SmartPropoPlusのソースコードです。
SmartPropoPlusのページはこちらです。

ここにはJRのPCM信号デコードして、8チャネル分の10bitデータを 取り出すアルゴリズムが記載されています。


SmartPropoPlusのソースコードによると、 JRのPCM信号は166usecという基本幅の整数倍のパルスで 構成されていると書かれています。 ただ、syncパルスだけは166[usec]の2.5倍の415[usec]のパルス幅と 規定されています。 PPM信号と同様にデータを採ってみると、syncパルスを見分けることができました。
送信機のトレーナー信号から取り出したデータを、時間単位に変換するために 16を掛け算します。 (PICは16[usec]クロックのタイマーでパルスの時間を測定しているため。) この値が415[usec]に近いところがsyncパルスになります。
pcm-data.gif

次は、デコードの第一段階です。
JRのPCMのRFの基本単位は166[usec]なので、パルス時間を 166で割って四捨五入しておきます。これがG列になります。 基本単位である166[usec]がいくつ分あるかを示します。
PIC用のコードは処理時間を短くするため、計算式を少し変えています。
オリジナルのコードは以下のように計算しています。
#define PW_JR 7.340
width = (int)floor((double)width / PW_JR + 0.5);
PCのオーディオI/Fのサンプリング周期44kHzが約22.7usecなので、 166usecを22.7usecで割ると、だいたい7.3となっています。

PIC用には、以下のようにしました。
tmp = pulse_width + 4;
width = tmp / 10;

PICの場合はpulse_widthにTMR0(16usecクロック)で数えた数値が入っています。 従ってこの時点では16usecがいくつあるかという時間情報になっています。 やっとTMR0のクロックを16usecに設定した意味がでてきました。
やりたいことは、以下の式を簡単にすることです。
int(pulse_width x 16usec / 166usec)

166で割るところを160という近似値を使うと、
int(pulse_width x 16usec / 160usec) = int(pulse_width / 10) となります。
四捨五入するには1/10にするまえに、割られる数に5を足しておけばよいです。 今回は割る数を166から160にしていますので、若干補正をするために4を足すように しました。

ここからが少し面倒です。
第二段階は、第一段階で得た数値をビットストリームに変換します。 SmartPropoPlusのソースコードを日本語にしてみると、以下のようになります。

1)まず、初期値として変数を用意し、0を入れておきます。
2)この変数を1ビット左シフトし(0を1ビット左シフトするので、結果は0)
3)最下位ビットに1を設定します。
4)第一段階で得た数値の分だけ、この変数を左シフトします。
5)変数を1ビット左シフトします。
6) 3)に戻ります。

式で書くと以下のように一行なのですが。
bitstream = ((bitstream << 1) + 1) << (width - 1);

第三段階は、ビットストリームから有効データへの変換です。 これは、第二段階と平行して行ないます。 JRの送信機はエンコード時に5bitのデータを -> 8bit に変換しているので、 デコードではこの逆の変換を行ないます。

大まかな流れです。
ビットストリームが8ビット以上たまったら、最上位から8ビットを 取り出し、8bit -> 5bitの変換テーブルで 5bitのデータに変換します。 使った8bitを除いた残りのビットストリームに対して、 第二段階のビットストリーム生成を繰り返し、 再度8bit以上たまったらビットストリームの最上位 ビットから8bitを取り出し、8bit -> 5bit変換を繰り返します。

これらを図にすると、以下のようになります。
bitstream.gif

smartpropoplusのコードによると、取り出した5bitデータの中に 各チャネルデータが以下のように埋め込まれています。 従って、0から1023までの値を取ることが可能です。
Position[2] = 1023 - ((data[1] << 5) | data[2]);
Position[0] = 1023 - ((data[4] << 5) | data[5]);
Position[5] = 1023 - ((data[9] << 5) | data[10]);
Position[7] = 1023 - ((data[12] << 5) | data[13]);
Position[3] = 1023 - ((data[16] << 5) | data[17]);
Position[1] = 1023 - ((data[19] << 5) | data[20]);
Position[4] = 1023 - ((data[24] << 5) | data[25]);
Position[6] = 1023 - ((data[27] << 5) | data[28]);


PCで取り込んだデータをperlでデコードした結果です。 perlスクリプト
pcm-decoded.gif

こちらがExcelファイルになります。 1チャンネルだけ動作するように送信機の操作を行ない、 そのときのトレーナー信号をデコードして、表にしました。 Excelファイル

この表のデータを採ったときは、エルロンの操作をしていました。 その様子がデータで確認することができます。

ここまでできたので、今度はデコード結果を使ってサーボを動かしてみます。
エルロンチャンネルのデコードして得られた値を、サーボ駆動信号に変換します。 デコード結果は、0-1023が理論上の最小最大になりますが、 実験結果では最小値が166、最大値は860でした。 PICのTMR1を2usecクロックで動かしてサーボ駆動信号のパルス幅を制御するようにします。 サーボ駆動信号は、センターの時に約1.5msec、最大角時に約2msec、 最小角時に約1msecとします。 デコード結果を2で割ってから500を足すと、だいたい良い感じになりそうです。 RB4にHIGH出力させるときにTMR1に値を設定し、オーバーフロー時の割り込みで RB4をLOW出力とします。

実験風景
s-jikken.gif


うまく動きました。PCMモードの送信機のスティックに合わせて サーボが動作しています。 MediaFireというサーバーにファイルを置きました。 mp4形式で、約4MByteあります。

動画ファイル(pcm-decode.mp4)
MediaFireのページに飛ぶと、 ”Click here to start download..”と左側に表示されますので、ここを クリックしてファイルをダウンロードしてください。




HOME