/* * SANWA Propotional Radio Controler PCM type * PCM wave file to DATA converter * Mon,22 Jul,2002 - Tue,23 Jul,2002 * Sat,07 Aug,2004 - Wed,18 Aug,2004 * Wed,03 Nov,2004 * Copyright(C)2002,2004 G-HAL * * * ●能書き * * SANWA の送信機(プロポ) SRD-6127TS SPACE PCM COMPUTER の、 * PCM モードの送信データを復調します。 * * FUTABA PCM1024 と JR S-PCM のプロポの PCM モードに関しては、 * Tsutomu SEKI 氏により解析されており、 * 同氏の SmartPropo The RC to PC Audio Interface にて * ソースコードの形で公開されています。 * → http://www.sekiriki.jp/smartpropo/ * * # あとは KO の解析がされれば、日本のプロポメーカー4強全て揃うって? * # 誰がその物資と労力を提供してくれるんだい?(笑) * * * ●使い方 * * 入力データはサウンドボードか何かを使って、 * wav 形式で録音して取り込みます。 * SRD-6127TS にはトレーナー端子が無いので、 * 分解しててきとーに取り出して下さい。 * * データ形式は、無圧縮 PCM のみ対応です。 * * 符号化ビット数は 8bit のみ対応です。 * * ソースの最高周波数は 3kHz なので、 * 録音周波数は 10kHz 前後がギリギリの最低ラインだと思います。 * それより低いと、復調できないと思います。 * 20kHz 前後あれば大丈夫でしょう。 * * 録音チャンネル数は、モノラルだろうがステレオだろうが、 * 5.1ch だろうが構いませんが、一番最初のチャンネルだけしか * 復調しません。2番目以降のチャンネルは無視します。 * * * ●備考 * * 本ソースは ISO/IEC 9899:1999、通称 C99、に * 準拠したコンパイラにて、コンパイル出来る事を確認しています。 * 別の言い方をすると、gcc ではコンパイル通るのを確認したけれど、 * Microsoft Visual C++ は C99 に準拠していないので、 * 多分コンパイルすら通らないんじゃないかと。 * * * ●権利条項 * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * 要約すると、 * 著作権者の権利条項は、必ず表示しなければなりません。 * 権利条項を変えたり消したりしちゃ駄目よ。 * 利用は自己責任で。保証は一切無し。当然補償も無し。 * 権利条項を守れば、てきとーに(適切に)使っていいよ。 * と言う感じです。詳細は * http://opensource.jp/licenses/ * の BSD license の項を参照。 * * * ●参考資料 * * Masanao Izumo 氏作の soundscope-3.0a18 と mmlp-1.2 の、 * usffd.* の wav parser まわりのソースを利用させて頂きました。 * → http://www.onicos.com/staff/iz/ */ /* * ●SANWA SRD-6127TS SPACE PCM COMPUTER のマニュアルから、覚え書き。 * 1ch スロットル L/H * 2ch エルロン R/L * 3ch エレベーター U/D * 4ch ラダー R/L * 5ch ギヤ/ジャイロ 1/2 * 6ch ピッチ D/N * 右スティック = エルロン・スロットル スティック * 上下: スロットル(ALT) * 左右: エルロン * 左スティック = ラダー・エレベーター スティック * 上下: エレベーター * 左右: ラダー * 対になる受信機は SRD-6108RS。 */ /* * ●ハード解析結果、覚え書き。 * LongTrailer 4.000ms? failsafe の登録時 * NormalTrailer 2.120ms 通常時 * データ部、長パルス 600us * データ部、短パルス 300us * High level Trailer → Low level Trailer 20.4ms * Low level Trailer → High level Trailer 20.4ms * 半周期 22.5ms 通常時 * 1周期 45ms 通常時 * * データ部 * FSK(MSK?,位相連続FSK?) * データレート 1667bps * サブキャリア 1250Hz±417Hz (833/1667Hz) * * RF 基板コネクタ * 3P No.1 Open * No.2 Vcc IN : From Battery + * No.3 Meter OUT * 4P No.1 RF-SW IN : 0V to Transmitter OFF * No.2 GND IN : From Battery - * No.3 Vcc OUT : +10.5V * No.4(RED) SIGNAL IN : 制御基板側では OC でドライブしている気がする。 */ /* * ●論理解析結果、覚え書き。 40kHz サンプル時のデータ長: Time Threshold: BADTRAIL[182:220], TRAIL[91:110], LONG[24:33], SHORT[9:19], GLITCH[2] 全サンプル平均のビット出現確率: 時間方向 → ← LSB MSB → H 62425787 25626765 77256978 87882267 07 L #_0A0AAA _#213332 33774273 2223843A 07 H ******** ******** ******** ******** ** L 10****** 01****** ******** ******** ** H 63434777 36536774 77357988 88882267 07 L #_0A0AAA _#212222 22874172 2222833A 07 H ******** ******** ******** ******** ** L 10****** 01****** ******** ******** ** ^^^^^^^^=^P^^^^^^=^^^P ^^=^P ^^^^^^^^=^P ^^^^=^^^^^P Right DU Right DU CheckDigit ↑ Gear Right RL ch.1/6 ↑ ch.1/6 ↑ 偶数パリティ ch.5 ↑ ch.2 ↑ 偶数パリティ 偶数パリティ 偶数パリティ 偶数パリティ ^^^^=^^^^^P ^^^^^^=^^^P ^^=^P Left RL Left DU CheckDigit ↑ ch.4 ↑ ch.3 ↑ 偶数パリティ 偶数パリティ 偶数パリティ  各ch 10bit、データ 9bit LSB-First で偶数パリティが付く。  CheckDigit は、その前 3ch の上位 3bit だけ取り出して総和を取った値を 2bit 右シフト(上図なら左だけど)した値となっている。  各スティックの値は、右/下/ギヤダウン方向が小、 中立/ギヤアップが中間値、左/上方向が大、となる。  Right DU 2つは、連動して動いている為、 どちらがスロットルでどちらがピッチだか判らない。  そもそも、なんでこんなに ch 番号がばらばらに並ぶんだ?。 */ //#define DEBUG #if defined(DEBUG) # define pr_debug(fmt,arg...) fprintf(stderr, fmt,##arg) #else # define pr_debug(fmt,arg...) #endif #define pr_info(fmt,arg...) fprintf(stderr, fmt,##arg) #if defined(DEBUG) # if (3)<=(DEBUG) # define pr_debug3(fmt,arg...) pr_debug(fmt,##arg) # else # define pr_debug3(fmt,arg...) # endif # if (2)<=(DEBUG) # define pr_debug2(fmt,arg...) pr_debug(fmt,##arg) # else # define pr_debug2(fmt,arg...) # endif # if (1)<=(DEBUG) # define pr_debug1(fmt,arg...) pr_debug(fmt,##arg) # else # define pr_debug1(fmt,arg...) # endif #endif #ifdef DEBUG #ifdef sun int fprintf(FILE *, const char *, ...); #endif #endif #include #include #include #include #include #include #include // 機種依存? typedef unsigned int U32; /* unsigned 32 bit integer */ typedef unsigned short U16; /* unsigned 16 bit integer */ typedef unsigned char U8; /* unsigned 8 bit integer */ typedef int S32; /* signed 32 bit integer */ typedef short S16; /* signed 16 bit integer */ typedef char S8; /* signed 8 bit integer */ typedef enum{ HIGH = 1, LOW = 0, X = -1, }SIG_LV; typedef enum{ HAZARD = -1, TRAIL2 = 4, TRAIL = 3, LONG = 2, SHORT = 1, UNK = 0, }DAT; /* RAW decode */ void raw_decode1( int tim, DAT dat, SIG_LV level ) { static int datalen = 0; if( TRAIL == dat ){ if( LOW == level ) printf("L" ); else printf("\nH" ); datalen = 0; }else if( LONG == dat ){ if( LOW == level ) printf("00" ); else printf("11" ); datalen += 2; }else if( SHORT == dat ){ if( LOW == level ) printf("0" ); else printf("1" ); datalen += 1; }else{ printf("X" ); } return; } /* RAW decode, HEX output */ void raw_decode2( int tim, DAT dat, SIG_LV level ) { static int datalen = 0; if( TRAIL == dat ){ if( LOW == level ) printf("(%d) L+", datalen ); else printf("(%d)\nH+", datalen ); datalen = 0; }else if( LONG == dat ){ if( LOW == level ) printf("0-0+" ); else printf("1-1+" ); datalen += 2; }else if( SHORT == dat ){ if( LOW == level ) printf("0+" ); else printf("1+" ); datalen += 1; }else{ printf("X*" ); } return; } /* PPM decode */ void ppm_decode1( int tim, DAT dat, SIG_LV level ) { static int datalen = 0; if( TRAIL == dat ){ if( LOW == level ){ if( 0 < datalen ){ printf(" " ); } printf(" L "); }else{ printf("\nH "); } datalen = 0; }else if( LONG == dat ){ printf("1" ); datalen += 1; if( 8 <= datalen ){ printf(" " ); datalen = 0; } }else if( SHORT == dat ){ printf("0" ); datalen += 1; if( 8 <= datalen ){ printf(" " ); datalen = 0; } }else{ printf("*\n" ); } return; } /* PPM decode, HEX output */ void ppm_decode2( int tim, DAT dat, SIG_LV level ) { static U8 data = 0; static int datalen = 0; if( TRAIL == dat ){ if( 0 < datalen ){ printf(",%02X", data ); datalen = 0; data = 0; } if( LOW == level ){ printf(", L"); }else{ printf("\nH"); } }else if( LONG == dat ){ data <<= 1; data |= 1; datalen += 1; if( 8 <= datalen ){ printf(",%02X", data ); datalen = 0; data = 0; } }else if( SHORT == dat ){ data <<= 1; data |= 0; datalen += 1; if( 8 <= datalen ){ printf(",%02X", data ); datalen = 0; data = 0; } }else{ printf(",%02X *\n", data ); datalen = 0; data = 0; } return; } /* PE decode (マンチェスタ符号) */ void pe_decode1( int tim, DAT dat, SIG_LV level ) { static int len = -1, bitlen = 0; static SIG_LV hist = X; if( TRAIL2 == dat ){ if( LOW == level ) printf(" L* " ); else printf("\nH* " ); bitlen = 0; len = -1; hist = X; }else if( TRAIL == dat ){ if( LOW == level ) printf(" L " ); else printf("\nH " ); bitlen = 0; len = -1; hist = X; }else if( LONG == dat ){ if( 0 <= len ){ if( 1 == (len & 1) ){ if( (LOW == hist) && (HIGH == level) ) printf("0"); else if( (HIGH == hist) && (LOW == level) ) printf("1"); else if( (LOW == hist) && (LOW == level) ) printf("c"); else if( (HIGH == hist) && (HIGH == level) ) printf("d"); else if( (X == hist) && (LOW == level) ) printf("o"); else if( (X == hist) && (HIGH == level) ) printf("p"); else printf("s"); bitlen++; }else{ if( LOW == level ) printf("a"); else printf("b"); // 禁止項。 // このステートが出たら、PE だと言う前提が // 間違っている。 bitlen++; } if( 8 == bitlen ){ printf(" "); bitlen = 0; } } len += 2; hist = level; }else if( SHORT == dat ){ if( 0 <= len ){ if( 1 == (len & 1) ){ if( (LOW == hist) && (HIGH == level) ) printf("0"); else if( (HIGH == hist) && (LOW == level) ) printf("1"); else if( (LOW == hist) && (LOW == level) ) printf("e"); else if( (HIGH == hist) && (HIGH == level) ) printf("f"); else if( (X == hist) && (LOW == level) ) printf("q"); else if( (X == hist) && (HIGH == level) ) printf("r"); else printf("t"); // まぁ、バグでもなければ c/d/e/f/s/t の項へは行かないけれど。 // o/p/q/r は、ノイズにやられた時などに出る。 bitlen++; } if( 8 == bitlen ){ printf(" "); bitlen = 0; } } len += 1; hist = level; }else{ if( LOW == level ) printf(" X(L:%d) ", tim ); else printf("\nX(H:%d) ", tim ); bitlen = 0; len = -1; hist = X; } return; } /* PE decode (マンチェスタ符号), HEX output */ void pe_decode2( int tim, DAT dat, SIG_LV level ) { static int len = -1, bitlen = 0; static SIG_LV hist = X; static char str[90] = ""; if( TRAIL2 == dat ){ if( LOW == level ){ strlcat( str, " L*", sizeof(str) ); }else{ if( 'H' == str[0] ) puts( str ); strlcpy( str, "H*", sizeof(str) ); } bitlen = 0; len = -1; hist = X; }else if( TRAIL == dat ){ if( LOW == level ){ strlcat( str, " L ", sizeof(str) ); }else{ if( 'H' == str[0] ) puts( str ); strlcpy( str, "H ", sizeof(str) ); } bitlen = 0; len = -1; hist = X; }else if( LONG == dat ){ if( 0 <= len ){ if( 9 == bitlen ){ strlcat( str, " ", sizeof(str) ); bitlen++; } if( 11 == bitlen ){ strlcat( str, " ", sizeof(str) ); bitlen = 0; } if( 1 == (len & 1) ){ if( (LOW == hist) && (HIGH == level) ) strlcat( str, "0", sizeof(str) ); else if( (HIGH == hist) && (LOW == level) ) strlcat( str, "1", sizeof(str) ); else if( (LOW == hist) && (LOW == level) ) strlcat( str, "c", sizeof(str) ); else if( (HIGH == hist) && (HIGH == level) ) strlcat( str, "d", sizeof(str) ); else if( (X == hist) && (LOW == level) ) strlcat( str, "o", sizeof(str) ); else if( (X == hist) && (HIGH == level) ) strlcat( str, "p", sizeof(str) ); else strlcat( str, "s", sizeof(str) ); bitlen++; }else{ if( LOW == level ) strlcat( str, "a", sizeof(str) ); else strlcat( str, "b", sizeof(str) ); // 禁止項。 // このステートが出たら、PE だと言う前提が // 間違っている。 bitlen++; } } len += 2; hist = level; }else if( SHORT == dat ){ if( 0 <= len ){ if( 9 == bitlen ){ strlcat( str, " ", sizeof(str) ); bitlen++; } if( 11 == bitlen ){ strlcat( str, " ", sizeof(str) ); bitlen = 0; } if( 1 == (len & 1) ){ if( (LOW == hist) && (HIGH == level) ) strlcat( str, "0", sizeof(str) ); else if( (HIGH == hist) && (LOW == level) ) strlcat( str, "1", sizeof(str) ); else if( (LOW == hist) && (LOW == level) ) strlcat( str, "e", sizeof(str) ); else if( (HIGH == hist) && (HIGH == level) ) strlcat( str, "f", sizeof(str) ); else if( (X == hist) && (LOW == level) ) strlcat( str, "q", sizeof(str) ); else if( (X == hist) && (HIGH == level) ) strlcat( str, "r", sizeof(str) ); else strlcat( str, "t", sizeof(str) ); // まぁ、バグでもなければ c/d/e/f/s/t の項へは行かないけれど。 // o/p/q/r は、ノイズにやられた時などに出る。 bitlen++; } } len += 1; hist = level; }else{ if( LOW == level ){ snprintf( &(str[strlen(str)]), sizeof(str)-strlen(str), " X(L:%d) ", tim ); }else{ if( 'H' == str[0] ) puts( str ); snprintf( str, sizeof(str), "X(H:%d) ", tim ); } bitlen = 0; len = -1; hist = X; } return; } /* FSK decode */ void fsk_decode1( int tim, DAT dat, SIG_LV level ) { static int len = 0, bitlen = 0; static DAT hist = UNK; if( TRAIL2 == dat ){ if( LOW == level ) printf(" L* " ); else printf("\nH* " ); bitlen = 0; len = 0; }else if( TRAIL == dat ){ if( LOW == level ) printf(" L " ); else printf("\nH " ); bitlen = 0; len = 0; }else if( LONG == dat ){ if( 0 == (len & 1) ){ printf("0"); }else{ // 同期外れ len--; printf("+"); } bitlen++; len += 2; }else if( SHORT == dat ){ if( 1 == (len & 1) ){ if( (SHORT == hist) ){ printf("1"); }else{ len--; printf("+"); } bitlen++; } len += 1; }else{ if( LOW == level ) printf(" X(L:%d) ", tim ); else printf("\nX(H:%d) ", tim ); bitlen = 0; len = 0; } if( 8 == bitlen ){ printf(" "); bitlen = 0; } hist = dat; return; } /* FSK decode, HEX output */ void fsk_decode2( int tim, DAT dat, SIG_LV level ) { static bool err = true; static bool failsafemode = false; static DAT hist = UNK; static int len = 0, bitlen = 0, wordlen = 0; static int decode = 0, parity = 0, checksum = 0; static int data[6]; if( err && !((HIGH == level) && ((TRAIL2 == dat) || (TRAIL == dat))) ){ return; } if( (TRAIL2 == dat) || (TRAIL == dat) ){ if( !err ){ if( !(4 == bitlen) ){ printf("ERROR: CheckDigit Word Overrun\n"); err = true; return; } if( !(3 == wordlen) && !(wordlen == 6) ){ printf("ERROR: WordCount Overrun\n"); err = true; return; } if( 1 & parity ){ printf("ERROR: CheckDigit Parity Mismatch\n"); err = true; return; } if( (decode & 0x07) != (checksum >> 8) ){ printf("ERROR: Checksum Mismatch(%X,%X)\n", decode, checksum ); err = true; return; } pr_debug3("Checksum(%X,%X)\n", decode, checksum ); } if( HIGH == level ){ if( !err ){ printf("%s: %+4d, %+4d, %+4d, %+4d, %+4d, %+4d\n", failsafemode ? "DataFailsafe" : "Data", data[0], data[1], data[2], data[3], data[4], data[5] ); } err = false; len = 0; wordlen = 0; if( TRAIL == dat ){ failsafemode = false; }else{ failsafemode = true; } } checksum = 0; bitlen = 0; decode = 0; parity = 0; }else if( LONG == dat ){ if( 1 == (len & 1) ){ printf("ERROR: SynchronisationLoss\n"); err = true; return; } decode |= (0 << bitlen); bitlen++; len += 2; }else if( SHORT == dat ){ if( 1 == (len & 1) ){ if( !(SHORT == hist) ){ printf("ERROR: SynchronisationLoss\n"); err = true; return; } decode |= (1 << bitlen); parity++; bitlen++; } len += 1; }else{ printf("ERROR: Unknown PulseWidth\n"); err = true; return; } if( 10 == bitlen ){ if( 1 & parity ){ printf("ERROR: Data Parity Mismatch\n"); err = true; return; } if( 6 <= wordlen ){ printf("ERROR: WordCount Overrun\n"); err = true; return; } data[wordlen] = (0x1FF & decode) - 0x100; checksum += (0x1C0 & decode); wordlen++; bitlen = 0; decode = 0; parity = 0; } hist = dat; return; } void (*decode)( int tim, DAT dat, SIG_LV level ) = fsk_decode2; #define MAXCHANNELS 16 /* Windows WAVE File Encoding Tags */ #define WAVE_FORMAT_PCM 0x0001 #define WAVE_FORMAT_ADPCM 0x0002 /* Not suppoted */ #define WAVE_FORMAT_ALAW 0x0006 #define WAVE_FORMAT_MULAW 0x0007 #define WAVE_FORMAT_OKI_ADPCM 0x0010 /* Not suppoted */ #define WAVE_FORMAT_DIGISTD 0x0015 /* Not suppoted */ #define WAVE_FORMAT_DIGIFIX 0x0016 /* Not suppoted */ #define IBM_FORMAT_MULAW 0x0101 /* Not suppoted */ #define IBM_FORMAT_ALAW 0x0102 /* Not suppoted */ #define IBM_FORMAT_ADPCM 0x0103 /* Not suppoted */ enum usffd_data_encoding { USFFD_UNDEFINED = 0, USFFD_SIGBYTE, /* signed byte */ USFFD_UNSIGBYTE, /* unsigned byte */ USFFD_G711_ULAW, /* G.711 U-Law 8-bit */ USFFD_G711_ALAW, /* G.711 A-Law 8-bit */ USFFD_SIGWORDB, /* signed word big-endian */ USFFD_UNSIGWORDB, /* unsigned word big-endian */ USFFD_SIGWORDL, /* signed word little-endian */ USFFD_UNSIGWORDL, /* unsigned word little-endian */ USFFD_NENCODINGS /* number of encodints */ }; enum usffd_error_code { USFFD_NO_ERROR = 0, USFFD_SYS_ERROR, USFFD_NOT_REGULAR_FILE, USFFD_INVALID_FILE_HEADER, USFFD_UNRECOGNIZED_FILE_HEADER, USFFD_HAZARDNOWN_FILE_ENCODING }; typedef struct _usffd { /* setting in usffd_open() */ int fd; char* filename; long file_size; /* setting in usffd_set_header() */ long hdr_size; long data_size; /* nsamples * block_align */ long data_encoding; long sample_rate; long nchannels; }USFFD; int usffd_errno = USFFD_NO_ERROR; static U32 read_ulong_bigend(int fd); static U32 read_ulong_littleend(int fd); static U16 read_ushort_bigend(int fd); static U16 read_ushort_littleend(int fd); static bool usffd_read_wav_header(USFFD* usffd); bool usffd_open(const char* filename, USFFD* usffd); static U32 read_ulong_bigend(int fd) { #ifdef BIGENDIAN U32 val; if(read(fd, &val, 4) != 4) return 0; return val; #else unsigned char c[4]; if(read(fd, c, 4) != 4) return 0; return ((U32)c[3]) | (((U32)c[2]) << 8) | (((U32)c[1]) << 16) | (((U32)c[0]) << 24); #endif } static U32 read_ulong_littleend(int fd) { #ifdef BIGENDIAN unsigned char c[4]; if(read(fd, c, 4) != 4) return 0; return ((U32)c[0]) | (((U32)c[1]) << 8) | (((U32)c[2]) << 16) | (((U32)c[3]) << 24); #else U32 val; if(read(fd, &val, 4) != 4) return 0; return val; #endif } static U16 read_ushort_littleend(int fd) { #ifdef BIGENDIAN unsigned char c[2]; if(read(fd, c, 2) != 2) return 0; return ((U16)c[0]) | (((U16)c[1]) << 8); #else U16 val; if(read(fd, &val, 2) != 2) return 0; return val; #endif /* BIGENDIAN */ } static U16 read_ushort_bigend(int fd) { #ifdef BIGENDIAN U16 val; if(read(fd, &val, 2) != 2) return 0; return val; #else unsigned char c[2]; if(read(fd, c, 2) != 2) return 0; return ((U16)c[1]) | (((U16)c[0]) << 8); #endif /* BIGENDIAN */ } static U8 read_uchar_bigend(int fd) { U8 val; if(read(fd, &val, 1) != 1) return 0; return val; } #define read_uchar_littleend(fd) read_uchar_bigend(fd) static bool usffd_read_wav_header(USFFD* usffd) { char buff[8]; U32 bits_per_sample, size, chHAZARD_size, len; U16 format; read(usffd->fd, buff, 4); /* expect "RIFF" */ if(strncmp(buff, "RIFF", 4) != 0) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_INVALID_FILE_HEADER; return false; } size = read_ulong_littleend(usffd->fd); read(usffd->fd, buff, 4); /* expect "WAVE" */ if(strncmp(buff, "WAVE", 4) != 0) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_INVALID_FILE_HEADER; return false; } /* Skip to the next "fmt " or end of file */ while(1) { if(read(usffd->fd, buff, 4) != 4) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_HAZARDNOWN_FILE_ENCODING; return false; } if(!strncmp("fmt ", buff, 4)) break; len = read_ulong_littleend(usffd->fd); while(len > 0 && read(usffd->fd, buff, 1) > 0) len--; } chHAZARD_size = read_ulong_littleend(usffd->fd); format = read_ushort_littleend(usffd->fd); usffd->nchannels = read_ushort_littleend(usffd->fd); if(usffd->nchannels == 0) { #ifdef DEBUG puts("usffd->nchannels == 0"); #endif usffd_errno = USFFD_INVALID_FILE_HEADER; return false; } usffd->sample_rate = read_ulong_littleend(usffd->fd); /* avg_bytes_per_sec */ read_ulong_littleend(usffd->fd); /* block_align */ read_ushort_littleend(usffd->fd); bits_per_sample = read_ushort_littleend(usffd->fd); if(bits_per_sample != 8 && bits_per_sample != 16) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_HAZARDNOWN_FILE_ENCODING; return false; } chHAZARD_size -= 16; while(chHAZARD_size > 0 && read(usffd->fd, buff, 1) > 0) chHAZARD_size--; /* Now look for the wave data chHAZARD */ for (;;) { if(read(usffd->fd, buff, 4) != 4) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_HAZARDNOWN_FILE_ENCODING; return false; } len = read_ulong_littleend(usffd->fd); if(strncmp("data", buff, 4) == 0) break; /* Found the data chHAZARD */ while(len > 0 && read(usffd->fd, buff, 1) > 0) len--; } switch(format) { case WAVE_FORMAT_PCM: if(bits_per_sample == 8) usffd->data_encoding = USFFD_UNSIGBYTE; else usffd->data_encoding = USFFD_SIGWORDL; break; case WAVE_FORMAT_ALAW: if(bits_per_sample != 8) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_INVALID_FILE_HEADER; return false; } usffd->data_encoding = USFFD_G711_ALAW; break; case WAVE_FORMAT_MULAW: if(bits_per_sample != 8) { lseek(usffd->fd, 0, SEEK_SET); usffd_errno = USFFD_INVALID_FILE_HEADER; return false; } usffd->data_encoding = USFFD_G711_ULAW; break; default: usffd->data_encoding = USFFD_G711_ULAW; usffd_errno = USFFD_HAZARDNOWN_FILE_ENCODING; #ifdef DEBUG printf("format = %x\n", format); #endif break; } usffd->hdr_size = lseek(usffd->fd, 0, SEEK_CUR); usffd->data_size = len; return true; } bool usffd_open(const char* filename, USFFD* usffd) { struct stat buf; usffd_errno = USFFD_NO_ERROR; usffd->fd = -1; usffd->filename = NULL; usffd->file_size = 0; usffd->hdr_size = 0; usffd->data_size = 0; usffd->data_encoding = USFFD_UNDEFINED; usffd->sample_rate = USFFD_UNDEFINED; usffd->nchannels = 1; if((usffd->filename = strdup(filename)) == NULL) { usffd_errno = USFFD_SYS_ERROR; return false; } if((usffd->fd = open(filename, O_RDONLY)) < 0) { usffd_errno = USFFD_SYS_ERROR; free(usffd->filename); usffd->filename = NULL; return false; } if(fstat(usffd->fd, &buf) < 0) { usffd_errno = USFFD_SYS_ERROR; close(usffd->fd); free(usffd->filename); usffd->fd = -1; usffd->filename = NULL; return false; } if(!S_ISREG(buf.st_mode)) { usffd_errno = USFFD_NOT_REGULAR_FILE; close(usffd->fd); free(usffd->filename); usffd->fd = -1; usffd->filename = NULL; return false; } usffd->file_size = buf.st_size; return true; } bool usffd_read_data( USFFD* usffd ) { U8 buf[MAXCHANNELS]; /* read buffer */ // comparate time_thresold struct Threshold{ struct TimeThreshold{ int lower; // 下限 int upper; // 上限 }short_pulse, long_pulse, trail, trail2; int glitch; struct LevelThreshold{ U8 low; // LOW → HIGH の閾値 U8 high; // HIGH → LOW の閾値 }level; }threshold; struct History{ SIG_LV level; // signal level int time; // continuation time DAT data; // decode result }history[3]; // [0] 今回のデータ // [1] 直前のデータ // [2] 仮決定したデータ /* Check Data Type */ if( usffd->data_encoding != USFFD_UNSIGBYTE ){ return false; } if( MAXCHANNELS < usffd->nchannels ){ return false; } /* Calc. Parameter */ threshold.trail2.lower = (int)( 3.800 / ( 1000.0 / usffd->sample_rate ) ); threshold.trail2.upper = (int)( 4.600 / ( 1000.0 / usffd->sample_rate ) ); threshold.trail.lower = (int)( 1.900 / ( 1000.0 / usffd->sample_rate ) ); threshold.trail.upper = (int)( 2.300 / ( 1000.0 / usffd->sample_rate ) ); threshold.long_pulse.lower = (int)( 0.500 / ( 1000.0 / usffd->sample_rate ) ); threshold.long_pulse.upper = (int)( 0.700 / ( 1000.0 / usffd->sample_rate ) ); threshold.short_pulse.lower = (int)( 0.200 / ( 1000.0 / usffd->sample_rate ) ); threshold.short_pulse.upper = (int)( 0.400 / ( 1000.0 / usffd->sample_rate ) ); threshold.glitch = (int)( 0.050 / ( 1000.0 / usffd->sample_rate ) ); threshold.level.low = 0xA0; threshold.level.high = 0x60; printf("Time Threshold: TRAIL2[%d:%d], TRAIL[%d:%d], LONG[%d:%d], SHORT[%d:%d], GLITCH[%d]\n", threshold.trail2.lower , threshold.trail2.upper , threshold.trail.lower , threshold.trail.upper , threshold.long_pulse.lower , threshold.long_pulse.upper , threshold.short_pulse.lower, threshold.short_pulse.upper, threshold.glitch ); // Initial State history[2].level = history[1].level = history[0].level = X; history[2].time = history[1].time = history[0].time = 0; history[2].data = history[1].data = history[0].data = HAZARD; /* Read and Analyze */ pr_debug3("Data Analyze Start\n"); for( long i = usffd->data_size; 0 <= i; i -= usffd->nchannels ){ read( usffd->fd, buf, usffd->nchannels ); // Schmitt Trigger Buffer if( HIGH == history[1].level ) { if( threshold.level.high < buf[0] ) history[0].level = HIGH; else history[0].level = LOW; }else{ if( threshold.level.low < buf[0] ) history[0].level = HIGH; else history[0].level = LOW; } if( history[1].level == history[0].level ){ history[1].time++; }else{ if( X == history[1].level ){ history[1].level = history[0].level; pr_debug3("start"); }else if( (history[1].time <= threshold.glitch) ){ history[1].time += history[2].time; history[2].time = 0; history[1].level = history[2].level; history[2].level = X; history[2].data = HAZARD; pr_debug3("glitch"); }else if( (threshold.trail2.lower <= history[1].time) && (history[1].time <= threshold.trail2.upper) ){ history[1].data = TRAIL2; goto HIT; }else if( (threshold.trail.lower <= history[1].time) && (history[1].time <= threshold.trail.upper) ){ history[1].data = TRAIL; goto HIT; }else if( (threshold.long_pulse.lower <= history[1].time) && (history[1].time <= threshold.long_pulse.upper) ){ history[1].data = LONG; goto HIT; }else if( (threshold.short_pulse.lower <= history[1].time) && (history[1].time <= threshold.short_pulse.upper) ){ history[1].data = SHORT; goto HIT; }else{ history[1].data = UNK; HIT: if( HAZARD != history[2].data ) (*decode)( history[2].time, history[2].data, history[2].level ); history[2].level = history[1].level; history[1].level = history[0].level; history[2].time = history[1].time ; history[1].time = history[0].time ; history[2].data = history[1].data ; history[1].data = history[0].data ; history[0].time = 0; pr_debug3("(%d,%d,%d)\n", history[2].time, history[2].data, history[2].level ); } } } pr_debug3("Data Analyze Finish\n"); printf("\n" ); return true; } int main( int argc, char *argv[] ) { bool ret; USFFD usffd; char filename[FILENAME_MAX] = { 0 }; for( int i = 1; i < argc; i++ ){ if( '-' != (argv[i])[0] ){ strlcpy( filename, argv[i], sizeof(filename) ); }else if( !strcasecmp( argv[i], "-raw1" ) ){ decode = raw_decode1; }else if( !strcasecmp( argv[i], "-raw2" ) ){ decode = raw_decode2; }else if( !strcasecmp( argv[i], "-ppm1" ) ){ decode = ppm_decode1; }else if( !strcasecmp( argv[i], "-ppm2" ) ){ decode = ppm_decode2; }else if( !strcasecmp( argv[i], "-pe1" ) ){ decode = pe_decode1; }else if( !strcasecmp( argv[i], "-pe2" ) ){ decode = pe_decode2; }else if( !strcasecmp( argv[i], "-fsk1" ) ){ decode = fsk_decode1; }else if( !strcasecmp( argv[i], "-fsk2" ) ){ decode = fsk_decode2; }else{ ERR: fprintf(stderr, "usage> %s [Mode] [WaveFileName]\n" "\tmode\n" "\t\t-RAW1\n" "\t\t-RAW2\n" "\t\t-PPM1\n" "\t\t-PPM2\n" "\t\t-PE1\n" "\t\t-PE2\n" "\t\t-FSK1\n" "\t\t-FSK2\n" , argv[0] ); return -1; } } if( !filename[0] ) goto ERR; ret = usffd_open( filename, &usffd ); if( !ret ){ fprintf(stderr, "File not found.\n"); return -1; } ret = usffd_read_wav_header( &usffd ); if( !ret ){ fprintf(stderr, "File Header read error.\n"); close( usffd.fd ); return -1; } ret = usffd_read_data( &usffd ); if( !ret ){ fprintf(stderr, "Not supported data type.\n"); close( usffd.fd ); return -1; } close( usffd.fd ); return 0; } /* [ End of File ] */