モールス信号decoder(まとめ)
前回、ちらりと書いた、アメリカ製トランシーバー、性懲りもなく、迂回輸入したら 幾らになるかと、識者に聞いてみました。気になる点は、現地の消費税と関税です。
税については、CAの消費税は各市で若干異なります。SFOですと9.5%です。 関税については、以前の経験では5%でした。 課税は、(商品価格*0.6+送料)*0.05と記憶しています。
運び屋さんへの謝礼も含めるとして、大雑把に考えると、商品価格 X 1.15 ぐらいで 考えておけばいいんだな。
10W完成品が、$1600。ATU $330, USB I/F $40, Crystal Filters $125 each っつう事で ざっくり、$2100 かあ。
後は、運び屋をどうするかだな。裏社会知らないから、これが一番の難関そう。 壊れると、米国へ送って修理になる。このターンアラウンドが、1.5月ぐらいだ そうだから、ちと辛い。
decoderの最終試験
オプションのスピード追従も出来たので、これを組み込んで、最終試験をやってみる。 信号発生器は、A1A Breaker 。 被試験品は、FreeBSD on VMWARE だ。NotePCの内部 スピーカーで音を聞くと、キークリックっぽく聞こえるので、ヘッドフォンとマイク を、手ぬぐいで包んで、音響結合した。
まず、コールサイン集があったので、これを試験信号とした。cwdを立ち上げ、受信 状態にしておき、信号発生、1フレーズが終わると、信号発生器のスピードを変え (追従性能を見る為)て、連続受信した。
DG8KNF XK0DGE NQ2AJJ LO5DVS AL7FH PV0GHF DO2OGJ AL0JBM/HR0 <-- ref DGLT TMNF XK0DGE NQ2AJJ LO5DVS AL7FH PV0GHF DO2OGJ AL0JBM/HR0 <-- 60chr/min SR6DIF XK0DGE N<??>2AJ<??> LW5DVS AL7FH PV0GHF DO2OGJ AL0<??>BM<??>HR0 <- 120 DG6<??>F XKEE<??>2A<??>L<HH>DVS AL7FH PV0GHF DO2G<??>I<??><??>HR0 <- 130 DG8KNF XK0DGE NQ2AJJ LO5DVS AL7FH PV0GHF DO2OGJ AL0JBM/HR0 <- 90
うーん、スピードが速くなると、付いていけないなあ。それじゃ、平文はどうかな?
Alice was beginning to get very tired of sitting by her sister on the <- ref ALICE A T TT D BEGINNING TO GET VERY TIRED OF SITTING BY HER SISTER <- 60 <??>ICE WAS BEGINNING TO GET VERY TIRED OF SITTING BY HER SISTER ON <- 120 ALICE WAS BEGINNING TO GET VERY TIRED OF SITTING BY HER SISTER ON THE <- 130 A L I CE EMAS BEGINNING TO GET VERY TIRED OF SITTING BY HER SISTER ON <- 90
まあまあか。
次は、平文で限界スピード(と、思われる 150文字/分)を、調べてみた。
ALICE WAS BEGINNING TO GET VERY TIRED OF SITTING BY HER SISTER ON THE BANK, AND OF HAVING NOTHING TO DO: ONCE OR TWICE SHE HAD PEEPED INTO THE BOOK HER SISTER WAS READING, BUT IT HAD NO PICTURES OR CONVERSATIONS IN IT, AND WHAT IS THE USE OF A BOOK,'
解読してる時、どのぐらいのロードアベレージになるか、確認しておきたかったんだ けど、on VMWAEW上 でのロードアベレージは信用ならんのでやめた。このあたりは リグによって良く振れる(振れない)Sメーターみたいなもんで、耳Sの方が信用なるか。
さすがに、裏JOBでコンパイル等を始めちゃうと、解析をミスるようになる。nice値を 上げると良くなるのかな。まあ、この辺は、シングルスレッドの限界だろう。
プログラム修正点
上記の実験で使った、ソース一式を巻末に付けておきます。 最終試験を行うに当たり、ちと修正した部分があるので、メモしておきます。
自動追従させる為の初期値を当初は、{6, ... 18, ...} としてたけど、{ 12 } に改めた。この方が、初回起動時の追従性が良かった。(@ tr.c)
自動追従中に符号データ数が10を超える場合があり、本体が強制終了しちゃう 事が有った。超えた場合は、符号データをリセットするようにした。(@ cwd.c)
早過ぎるオプティマイズは悪と言う掟が有ったので、今まで気にはなっていたけど 何もしてなかった。今回でこのシリーズも終わりにしようと思っているので、プチ オプティマイズをやってみた。FFTの返り値を、全周波数領域に渡って計算して たり、使いもしない周波数ドメインの値を配列に入れていた。 これは、今回に限っては無駄なので、必要なもののみにした。(それに伴い、 fftの引数が変わった)
Makefileを整理して、ちとスマートにした。これって DRY の実践になるのかな。
Linuxへの移植について
Linuxはメインで使っていないので、音関係はどうなっているか良く知りません。 伝え聞く所によると、音を扱う流儀は2種類あるとの事。
一つは、ALSA系。名前の一部にLinuxと入っているように、Linux専用の方言だ。 こちらは、意識してドライバーを追加しないといけないらしい。
もう一つの系統はOSS。Linuxを入れると、つられて入ってくるのかな。良く知らない や。
で、FreeBSDで使った、/dev/dspW は、OSS系だとか。よって、Linuxで使うなら OSS系の方が親和性が高いと思われます。(注: AlSAでもOSS系のふりをする事が 出来るとか。)
論より証拠とばかり、Debian/lenny へ持っていって、そのままコンパイル・起動 してみたら、エラーにもならずにモールス信号待ちになったよ。(残念ながら マイクジャックの口径が合わず、信号は入れられなかった。)
細かい事は分からないけど、openした時の、デバイスのデフォルト設定さえ確認 して、8000Hz,16Bit,mono ぐらいに設定出来れば、動くんではなかろうか。 (後で調べた限りでは、/dev/dspWの初期値は、FreeBSDのそれと一緒だったよ)
ちなみに、使ったlennyはこんなのでした。
sakae@debian:~/CW$ cat /dev/sndstat Sound Driver:3.8.1a-980706 (ALSA v1.0.16 emulation code) Kernel: Linux debian 2.6.26-2-686 #1 SMP Fri Aug 14 01:27:18 UTC 2009 i686 Config options: 0 Installed drivers: Type 10: ALSA emulation Card config: ESS ES1938 (Solo-1) rev 0, irq 5 Audio devices: 0: ESS Solo-1 (DUPLEX) Synth devices: NOT ENABLED IN CONFIG Midi devices: 0: ESS ES1938 (Solo-1) MIDI Timers: 7: system timer Mixers: 0: ESS Solo-1
ソース開陳
Makefile
# Makefile for CW decoder OBJS = cwd.o lookup.o tr.o a.out: $(OBJS) cc -lm -o $@ $(OBJS) .c.o: cc -g -c $< #cwd.o: cwd.h
tr.c
// auto speed tracking #include <stdio.h> #include <stdlib.h> #include <strings.h> #define KEEP 16 // mcnt keep count int rp = KEEP; // ring pointer for kp int kp[KEEP] = { 12 }; // history (init value) int int_cmp(const int *a, const int *b){ if (*a == *b){ return 0; } if (*a < *b){ return -1; } else { return 1; } } int tr(int v){ int i, work[KEEP]; int dot = 0; // sum of dot int dash = 0; // sum of dash if (rp == KEEP){ rp = 0; } kp[rp++] = v; // Update history bcopy(&kp, &work, sizeof(int) * KEEP); // copy to work qsort(work, KEEP, sizeof(int), (int(*)(const void*, const void*))int_cmp); for(i = 0; i < KEEP; i++){ // printf(" %d", work[i] ); // debug if ( i < KEEP/2 ) { dot += work[i]; } else { dash += work[i]; } } // printf(" -> %d %d ", dot / (KEEP/2), dash / (KEEP/2)); // debug // if (dash > dot * 2){ printf("OK\n"); } else { printf("ng\n"); } // debug if (dash > dot * 2){ return dot / (KEEP/2); } else { return 0; } } /******** for debug *********************** main(){ int dot_dash; // simulate mcnt while(1){ printf("Next dot/dash ==> "); scanf("%i", &dot_dash); tr(dot_dash); } } *******************************************/
lookup.c
// lookup morse code to char #include <stdio.h> struct kv { long key; char val[8]; }; struct kv tbl[] = { { 13, "A" }, { 3111, "B" }, { 3131, "C" }, { 311, "D" }, { 1, "E" }, { 1131, "F" }, { 331, "G" }, { 1111, "H" }, { 11, "I" }, { 1333, "J" }, { 313, "K" }, { 1311, "L" }, { 33, "M" }, { 31, "N" }, { 333, "O" }, { 1331, "P" }, { 3313, "Q" }, { 131, "R" }, { 111, "S" }, { 3, "T" }, { 113, "U" }, { 1113, "V" }, { 133, "W" }, { 3113, "X" }, { 3133, "Y" }, { 3311, "Z" }, { 33333, "0" }, { 13333, "1" }, { 11333, "2" }, { 11133, "3" }, { 11113, "4" }, { 11111, "5" }, { 31111, "6" }, { 33111, "7" }, { 33311, "8" }, { 33331, "9" }, { 131313, "." }, { 331133, "," }, { 113311, "\?"}, { 31113, "=" }, { 311113, "-" }, { 333111, ":" }, { 133331, "'" }, { 31131, "/" }, { 133131, "@" }, {11111111, "<HH>"}, { 13131, "<AR>" }, { 0, "<\?\?>" } // case not found. (don't delete this line) }; char* lookup(long k){ int i = 0; while(1){ if (tbl[i].key == 0){ return tbl[i].val; } if (tbl[i].key == k){ return tbl[i].val; } i++; } } /******* for test ************************** main(){ printf("%s\n", lookup(atol("3111"))); printf("%s\n", lookup(atol("33331113"))); } ********************************************/
cwd.c
// Morse code decoder #include <stdio.h> #include <stdlib.h> #include <math.h> #include <fcntl.h> #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> #define SOURCE "/dev/dspW" // Morse tone from audio device //#define SOURCE "sound.au" // Morse tone from raw file #define DATAN 128 // FFT point # #define MAXELE 10 // max dot dash element (par char) #define DUMP(fmt,val) do{ if (argc == 2 ){ printf(fmt, val); } } while(0) double Wave[DATAN]; // wave buffer double fft(double *tW, int fp) { int N = DATAN; int N_2 = N/2; int i,j,k,kp,m,h,P; double mdd; double w1, w2; double t1,t2,s1,s2; double tri[DATAN]; double fReal[DATAN], fImag[DATAN]; // Get P, where N = 2 ** P i = N; P = 0; while (i != 1) { i = i / 2; P++; } // copy real wave into buffer for (i = 0; i < N; i++) { fReal[i] = tW[i]; fImag[i] = 0.0; } // make sin,cos table for ( i = 0; i < N_2; i++ ) { tri[i] = cos( 2 * i * M_PI / N ); tri[i + N_2] = (-1.0) * sin( 2 * i * M_PI / N ); } // sort j = 0; for ( i = 0; i <= N - 2; i++ ) { if (i < j) { t1 = fReal[j]; fReal[j] = fReal[i]; fReal[i] = t1; t2 = fImag[j]; fImag[j] = fImag[i]; fImag[i] = t2; } k = N_2; while (k <= j) { j = j - k; k = k/2; } j = j + k; } // do butterfly for ( i = 1; i <= P; i++ ) { mdd = pow(2.0, (double)i); m = (int)mdd; h = m/2; for ( j = 0; j < h; j++ ) { w1 = tri[j*(N/m)]; w2 = tri[j*(N/m) + N_2]; for( k = j; k < N; k+=m ) { kp = k + h; s1 = fReal[kp] * w1 - fImag[kp] * w2; s2 = fReal[kp] * w2 + fImag[kp] * w1; t1 = fReal[k] + s1; fReal[kp] = fReal[k] - s1; fReal[k] = t1; t2 = fImag[k] + s2; fImag[kp] = fImag[k] - s2; fImag[k] = t2; } } } return sqrt(fReal[fp] * fReal[fp] + fImag[fp] * fImag[fp])/DATAN; } load_wave(double *tW, int fd){ int i; signed short buf[DATAN]; read(fd, &buf, sizeof(signed short) * DATAN); for (i = 0; i < DATAN; i++){ tW[i] = (double)buf[i]; } } main(int argc, char* argv[]){ int count = 491520 / DATAN; // samples @ 8000/sec * 60sec int fd; double th = 800.0; // thereshold for 1/0 int dot = 6; // dot length int onoff; // Signal on(1) or off(0) int ms = 0; // mark(1) or space(0) int mcnt = 0; // mark counter int scnt = 0; // space counter char val[MAXELE]; // dot(1),dash(3) int x = 0; // index for val int ws= 1; // word-space print enable int yes; // for auto speed tracking fd = open(SOURCE, O_RDONLY); while(1){ load_wave(Wave, fd); onoff = fft(Wave, 10) > th ? 1 : 0; // 10 is 625Hz amplitude DUMP("%d",onoff); if (onoff != ms ){ // mark->space or space->mark ms = onoff; if (ms) { mcnt = scnt = 0; ws = 1; } else { scnt = 0; } } if (ms) { mcnt++; } else { scnt++; if ( mcnt && (scnt > 1) ){ // in char val[x++] = mcnt > dot * 2 ? '3' : '1'; yes = tr(mcnt); if (yes) { dot = yes; } // auto speed tracking mcnt = 0; if (x == MAXELE){ x = 0; } // fatal, reset char } if ( x && (scnt > dot * 2)){ // detect char val[x] = '\0'; printf("%s", lookup(atol(val))); fflush(stdout); DUMP("%s", "\n"); x = 0; } if ( ws && (scnt > dot * 5) ){ // detect word printf("%s", " "); ws = 0; DUMP("%s", "_\n"); } } } close(fd); printf("%s", "\n"); exit(0); }