EWS.htm

戻る

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