<!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>
|