![]() |
<!DOCTYPE HTML> <HTML LANG="ja"> <HEAD> <META CHARSET="Shift_JIS"> <TITLE>緊急警報信号</TITLE> </HEAD> <BODY onLoad="init()" STYLE="background-color:#CCFFFF"> <DIV STYLE="text-align:center"> <BR> <B>緊急警報信号</B> <BR><BR> <TABLE STYLE="margin-left:auto; margin-right:auto"> <TR><TD ALIGN=LEFT NOWRAP>種類:</TD><TD ALIGN=LEFT NOWRAP STYLE="min-width:10em"><SPAN ID="kind"></SPAN></TD></TR> <TR><TD ALIGN=LEFT NOWRAP>地域:</TD><TD ALIGN=LEFT NOWRAP><SPAN ID="area"></SPAN></TD></TR> <TR><TD ALIGN=LEFT NOWRAP>日時:</TD><TD ALIGN=LEFT NOWRAP><SPAN ID="time"></SPAN></TD></TR> </TABLE> <BR> <FORM> <INPUT TYPE=BUTTON VALUE="クリア" onClick="clear_info()"> </FORM> <BR> </DIV> <SCRIPT TYPE="text/javascript"> <!-- FIXED_1 = 0x0e6d; // 固定符号 第一種開始/終了 FIXED_2 = 0xf192; // 固定符号 第二種開始 // 地域符号 area_code = [0x0d34, 0x1694, 0x1ca8, 0x2354, 0x1a64, 0x154c, 0x05ac, 0x119c, 0x1750, 0x1d60, 0x2b18, 0x3930, 0x06b8, 0x31a4, 0x38e0, 0x262c, 0x192c, 0x071c, 0x2ab0, 0x15b0, 0x1338, 0x14e4, 0x1a98, 0x24b4, 0x3528, 0x2748, 0x2994, 0x2968, 0x2598, 0x0b70, 0x3390, 0x1668, 0x32c8, 0x19d0, 0x2a4c, 0x0e58, 0x348c, 0x0c6c, 0x0ad4, 0x2cc4, 0x2e60, 0x3988, 0x26d0, 0x0674, 0x0b8c, 0x18b4, 0x2564, 0x28ac, 0x229c, 0x3234, 0x3470, 0x3514, 0x0dc8]; // 地域名 area_name = ["共通", "関東広域圏", "中京広域圏", "近畿広域圏", "鳥取・島根圏", "岡山・香川圏", "北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県", "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"]; // 日符号 day_code = [0x1000, 0x0800, 0x1800, 0x0400, 0x1400, 0x0c00, 0x1c00, 0x0200, 0x1200, 0x0a00, 0x1a00, 0x0600, 0x1600, 0x0e00, 0x1e00, 0x0100, 0x1100, 0x0900, 0x1900, 0x0500, 0x1500, 0x0d00, 0x1d00, 0x0300, 0x1300, 0x0b00, 0x1b00, 0x0700, 0x1700, 0x0f00, 0x1f00]; // 月符号 month_code = [0x44, 0x24, 0x64, 0x14, 0x54, 0x34, 0x74, 0x0c, 0x4c, 0x2c, 0x6c, 0x1c]; // 時符号 hour_code = [0x0300, 0x1300, 0x0b00, 0x1b00, 0x0700, 0x1700, 0x0f00, 0x1f00, 0x0100, 0x1100, 0x0900, 0x1900, 0x0500, 0x1500, 0x0d00, 0x1d00, 0x0200, 0x1200, 0x0a00, 0x1a00, 0x0600, 0x1600, 0x0e00, 0x1e00]; // 年符号 year_code = [0x54, 0x34, 0x74, 0x0c, 0x4c, 0x2c, 0x44, 0x24, 0x64, 0x14]; function init() { sin_tbl = new Float32Array(383); // sin テーブル(0 〜 3π/2) i_rev = new Float32Array(512); // ビット反転テーブル win_tbl = new Float32Array(512); // 窓テーブル ar = new Float32Array(512); // FFT 作業領域 実部 ai = new Float32Array(512); // 虚部 var as = Math.PI / 256; var i; // sin テーブル(0 〜 3π/2) for(i = 0; i < 383; i++) sin_tbl[i] = Math.sin(i * as); // ビット反転テーブル var j = 0; for(i = 0; ; i++) { i_rev[i] = j; if(i == 511) break; for(var bit = 0x100; ; bit >>= 1) { if((j ^= bit) & bit) break; } } // 窓テーブル for(i = 0; i < 512; i++) win_tbl[i] = 0.5 - 0.5 * Math.cos(i * as); aud_ctx = new AudioContext(); capture = aud_ctx.createScriptProcessor(4096, 1, 0); var sample_rate = aud_ctx.sampleRate; var fi; // スペース周波数インデックス fi = 640 / (sample_rate / 512); i_sp_1 = Math.floor(fi); i_sp_2 = Math.ceil(fi); // マーク周波数インデックス fi = 1024 / (sample_rate / 512); i_mk_1 = Math.floor(fi); i_mk_2 = Math.ceil(fi); bit_ticks = sample_rate / 4096/* 64*64 */; // 1 ビットあたりのクロック数 bit_ticks_h = bit_ticks / 2; pcm_queue = []; // 入力データ キュー curr_pcm = undefined; prev_pcm = new Float32Array(4096); prev_bit = -1; fixed_reg = []; info_code = []; end1_check = 0; end1_check_time = 1.5 * sample_rate / 64; elem_kind = document.getElementById("kind"); elem_area = document.getElementById("area"); elem_time = document.getElementById("time"); if(navigator.mozGetUserMedia == undefined) navigator.getUserMedia({video:false, audio:true}, success, error); else navigator.mozGetUserMedia({video:false, audio:true}, success, error); } function success(stream) { mic = aud_ctx.createMediaStreamSource(stream); mic.connect(capture); capture.onaudioprocess = capture_proc; addEventListener("message", FFT, false); } function error(err) { alert("マイクが使用できません"); } function clear_info() { elem_kind.textContent = elem_area.textContent = elem_time.textContent = ""; end1_check = 0; } function capture_proc(e) { var pcm = e.inputBuffer.getChannelData(0); var new_pcm = new Float32Array(4096); for(var i = 0; i < 4096; i++) new_pcm[i] = pcm[i]; if(curr_pcm == undefined) { // 前の入力データの処理中でない curr_pcm = new_pcm; fft_cnt = 0; postMessage(undefined, "*"); } else { pcm_queue.push(new_pcm); } } function FFT() { fft_cnt++; var i, j; // ハニング窓 & 並び替え i = 0; if(fft_cnt < 8) { for(j = 3584 + (fft_cnt << 6); j < 4096; j++) { ar[i_rev[i]] = prev_pcm[j] * win_tbl[i]; i++; } j = 0; } else { j = (fft_cnt - 8) << 6; } for(; i < 512; i++) ar[i_rev[i]] = curr_pcm[j++] * win_tbl[i]; for(i = 0; i < 512; i++) ai[i] = 0; var th1 = 256; for(var n1 = 1; n1 < 512; ) { var n2 = n1 << 1; var th = 0; for(i = 0; i < n1; i++) { var cos = sin_tbl[128 + th]; var sin = sin_tbl[th]; for(j = i; j < 512; j += n2) { var k = j + n1; var wr = ar[k] * cos - ai[k] * sin; var wi = ai[k] * cos + ar[k] * sin; ar[k] = ar[j] - wr; ai[k] = ai[j] - wi; ar[j] += wr; ai[j] += wi; } th += th1; } th1 >>= 1; n1 = n2; } get_bit(); if(fft_cnt < 64) { postMessage(undefined, "*"); return; } prev_pcm = curr_pcm; if(pcm_queue.length) { curr_pcm = pcm_queue.shift(); fft_cnt = 0; postMessage(undefined, "*"); } else { curr_pcm = undefined; } } function get_bit() { if(end1_check) { if(end1_check_clock > end1_check_time) { end1_check = 0; elem_kind.textContent = "終了信号/試験信号"; } else { end1_check_clock++; } } var max = 0; var i_sig; // 最初の 64点だけ調べる for(var i = 0; i < 64; i++) { var power = ar[i] * ar[i] + ai[i] * ai[i]; if(power > max) { max = power; i_sig = i; } } var bit; switch(i_sig) { case i_sp_1: // スペース case i_sp_2: bit = 0; break; case i_mk_1: // マーク case i_mk_2: bit = 1; break; default: prev_bit = -1; return; } if(prev_bit < 0) { bit_cnt = 0; } else { if(bit_cnt) { clock++; if(clock >= sample_next) { reg = (reg << 1) & 0x3ffff | bit; bit_cnt++; sample_next = bit_cnt * bit_ticks + 1; decode(); } else { if(bit != prev_bit) { if(clock < sample_next - bit_ticks_h) { // ビット変化位置が大きくずれている // ビット同期やり直し clock = 0; reg = (bit) ? 0x00001 : 0x3fffe; bit_cnt = 1; sample_next = bit_ticks + 1; fixed_reg.length = info_code.length = 0; } } } } else { // ビット同期開始 clock = 0; reg = (bit) ? 0x00001 : 0x3fffe; bit_cnt = 1; sample_next = bit_ticks + 1; fixed_reg.length = info_code.length = 0; } } prev_bit = bit; } function decode() { var code = reg & 0xffff; switch(code) { case FIXED_1: case FIXED_2: if(end1_check) { if(code == FIXED_1) end1_check--; } switch(fixed_reg.length) { case 0: fixed_reg.push(reg); state = false; code_bits = 16; return; case 1: if(state && code_bits == 1 && code == (fixed_reg[0] & 0xffff)) { // 正常な固定符号 if(code == FIXED_1) { end1_check = 2; end1_check_clock = 0; } break; } fixed_reg[0] = reg; info_code.length = 0; state = false; code_bits = 16; return; } break; } if(fixed_reg.length) { // 同期中 if(!--code_bits) { if(state) // 固定符号 fixed_reg.push(reg); else // 地域区分符号/月日区分符号/年時区分符号 info_code.push(code); check_signal(); state = !state; code_bits = 16; } } } function check_signal() { if(fixed_reg.length < 2) return; var fixed = fixed_reg[0] & 0xffff; var reg; var info; var i; // 開始信号の識別 (1) var i_from = 0; var begin = 2; var ng = 0; for(i = 2; i < fixed_reg.length; i++) { if((fixed_reg[i] & 0xffff) == fixed) { if(!begin) i_from = i; begin++; ng = 0; if(begin >= 4) { info = 0; for(var j = i_from; j <= i; j++) { if(j == info_code.length) break; switch(info_code[j] & 0xe003) { case 0x8000: // 地域区分 開始 case 0xa000: info |= 0x1; break; case 0x4000: // 月日区分 開始 info |= 0x2; break; case 0x6000: // 年時区分 開始 info |= 0x4; break; } } if(info == 0x7) { show_info((fixed == FIXED_1) ? 1 : 2, i_from); return; } } } else { if(++ng == 3) begin = 0; } } // 開始信号の識別 (2)/終了信号の識別 (2) begin = 0; var end = 0; if(fixed == FIXED_1) { // 固定符号 第一種開始/終了 for(reg of fixed_reg) { switch(reg) { case 0x00e6d: // 第一種開始 begin++; break; case 0x30e6d: // 終了 end++; break; } } } else { // 固定符号 第二種開始 for(reg of fixed_reg) { if(reg == 0x0f192) // 第二種開始 begin++; } } if(begin >= 4) { // 開始 info = 0; for(var code of info_code) { switch(code & 0xe003) { case 0x8000: // 地域区分 開始 case 0xa000: info |= 0x1; break; case 0x4000: // 月日区分 開始 info |= 0x2; break; case 0x6000: // 年時区分 開始 info |= 0x4; break; } } if(info == 0x7) { show_info((fixed == FIXED_1) ? 1 : 2, 0); return; } } if(end >= 2) { // 終了 show_info(3, 0); return; } } function show_info(kind, i_from) { end1_check = 0; switch(kind) { case 1: elem_kind.textContent = "第一種開始信号"; break; case 2: elem_kind.textContent = "第二種開始信号"; break; default: elem_kind.textContent = "終了信号/試験信号"; } var area_cnt = {}; var m_d_cnt = {}; var y_h_cnt = {}; var i1, i2; for(var i = i_from; i < info_code.length; i++) { var code = info_code[i]; var info = code & 0xe003; switch((kind == 3) ? info ^ 0xc003 : info) { case 0x8000: // 地域区分 case 0xa000: i1 = area_code.indexOf(code & 0x3ffc); if(i1 >= 0) { // 正しい符号 if(area_cnt[i1] == undefined) area_cnt[i1] = 1; else area_cnt[i1]++; } break; case 0x4000: // 月日区分 if(!(code & 0x0080)) { // 日 i1 = day_code.indexOf(code & 0x1f00); // 月 i2 = month_code.indexOf(code & 0x7c); if(i1 >= 0 && i2 >= 0) { // 日,月とも正しい符号 i1 |= i2 << 5; if(m_d_cnt[i1] == undefined) m_d_cnt[i1] = 1; else m_d_cnt[i1]++; } } break; case 0x6000: // 年時区分 if(!(code & 0x0080)) { // 時 i1 = hour_code.indexOf(code & 0x1f00); // 年 i2 = year_code.indexOf(code & 0x7c); if(i1 >= 0 && i2 >= 0) { // 時,年とも正しい符号 i1 |= i2 << 5; if(y_h_cnt[i1] == undefined) y_h_cnt[i1] = 1; else y_h_cnt[i1]++; } } break; } } var indices; var max; // 地域表示 indices = Object.keys(area_cnt); if(indices.length) { max = 0; for(i1 of indices) { if(area_cnt[i1] > max) { max = area_cnt[i1]; i2 = i1; } } if(kind == 3 || max > 1) // 終了 or 2個以上検出 elem_area.textContent = area_name[i2]; } // 日時表示 // 月日 indices = Object.keys(m_d_cnt); if(indices.length) { var m_d; max = 0; for(i1 of indices) { if(m_d_cnt[i1] > max) { max = m_d_cnt[i1]; m_d = i1; } } if(kind == 3 || max > 1) { // 終了 or 2個以上検出 // 年時 indices = Object.keys(y_h_cnt); if(indices.length) { var y_h; max = 0; for(i1 of indices) { if(y_h_cnt[i1] > max) { max = y_h_cnt[i1]; y_h = i1; } } if(kind == 3 || max > 1) // 終了 or 2個以上検出 elem_time.textContent = "***" + String((5 + (y_h >> 5)) % 10) + "年" + String((m_d >> 5) + 1) + "月" + String((m_d & 0x1f) + 1) + "日 " + String(y_h & 0x1f) + "時"; } } } } //--> </SCRIPT> </BODY> </HTML> |