npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@prx.org/eas-detect

v0.2.0

Published

Detect EAS (Emergency Alert System) tones and SAME messages in audio files

Readme

@prx.org/eas-detect

Detect EAS (Emergency Alert System) tones and SAME messages in audio files. Returns structured JSON with decoded headers, FIPS location lookups, and timecodes for when each EAS component occurs in the audio.

Supports full and partial match detection — even if only the attention tones, a fragment of the FSK digital header, or just the End of Message marker is present.

Warning: This software is provided without warranty and is not intended to be used for detecting EAS as part of a real, production, broadcast system. Its purpose is to detect the presence of EAS codes in audio files to help prevent their inadvertent inclusion in content where they should not be present.

Requirements

  • Node.js >= 18
  • FFmpeg (brew install ffmpeg)
  • multimon-ng (build from source or apt install multimon-ng)

Install

npm install @prx.org/eas-detect

CLI Usage

Call on any audio file, it will use ffmpeg to analyze the file:

npx eas-detect recording.wav

For raw PCM input (signed 16-bit little-endian) you also need to specify the sample rate:

npx eas-detect recording.bin --raw --sample-rate 22050

For audio where EAS tones are mixed with speech or music (e.g. TV broadcasts), use the sensitive FSK detection mode:

npx eas-detect recording.mp3 --fsk-mode sensitive

Output is JSON to stdout.

FSK Detection Modes

| Mode | Flag | Description | |------|------|-------------| | default | (none) | Energy-ratio analysis with alternation validation. Fast and precise, best for clean recordings. | | sensitive | --fsk-mode sensitive | Bandpass filtering + mark/space anti-correlation analysis. Detects faint FSK buried under speech or music, but may produce more false positives on music-heavy content. |

Library Usage

import { detect } from "@prx.org/eas-detect";

const result = await detect("recording.wav");
// Or with sensitive FSK detection:
// const result = await detect("recording.wav", { fskMode: "sensitive" });

if (result.easDetected) {
  console.log(`Match: ${result.matchType}`); // "full", "partial", or "none"

  for (const header of result.sameHeaders) {
    console.log(`${header.event.name} from ${header.originator.name}`);
    for (const loc of header.locations) {
      console.log(`  ${loc.county}, ${loc.state}`);
    }
  }
}

For processing audio from a Buffer (e.g., from S3):

import { detectBuffer } from "@prx.org/eas-detect";

const result = await detectBuffer(audioBuffer, ".wav");

Output Format

Full match

A complete EAS message with header, attention tone, and End of Message:

{
  "file": "recording.wav",
  "durationSeconds": 18.886,
  "easDetected": true,
  "matchType": "full",
  "timecodes": [
    { "type": "fsk", "start": 0.491, "end": 1.493 },
    { "type": "fsk", "start": 2.476, "end": 3.488 },
    { "type": "fsk", "start": 4.47, "end": 5.472 },
    { "type": "attentionTone", "start": 6.403, "end": 14.457 },
    { "type": "fsk", "start": 15.455, "end": 15.776 },
    { "type": "fsk", "start": 16.768, "end": 17.089 },
    { "type": "fsk", "start": 18.071, "end": 18.382 }
  ],
  "sameHeaders": [
    {
      "raw": "ZCZC-WXR-TOR-039173-039051+0030-1591829-KLST/NWS-",
      "originator": { "code": "WXR", "name": "National Weather Service" },
      "event": { "code": "TOR", "name": "Tornado Warning" },
      "locations": [
        {
          "code": "039173",
          "subdivision": "0",
          "stateFips": "39",
          "countyFips": "173",
          "state": "Ohio",
          "county": "Wood County"
        },
        {
          "code": "039051",
          "subdivision": "0",
          "stateFips": "39",
          "countyFips": "051",
          "state": "Ohio",
          "county": "Fulton County"
        }
      ],
      "duration": {
        "raw": "0030",
        "hours": 0,
        "minutes": 30,
        "totalMinutes": 30,
        "description": "30 minutes"
      },
      "issued": {
        "raw": "1591829",
        "julianDay": 159,
        "hour": 18,
        "minute": 29,
        "description": "Day 159, 18:29 UTC"
      },
      "sender": "KLST/NWS",
      "timecode": { "start": 0.491, "end": 5.472 }
    }
  ],
  "endOfMessage": true
}

Partial match

When only some EAS components are present — for example, just the End of Message marker:

{
  "file": "recording.wav",
  "durationSeconds": 3.914,
  "easDetected": true,
  "matchType": "partial",
  "timecodes": [
    { "type": "fsk", "start": 0.491, "end": 0.802 },
    { "type": "fsk", "start": 1.794, "end": 2.105 },
    { "type": "fsk", "start": 3.097, "end": 3.418 }
  ],
  "sameHeaders": [],
  "endOfMessage": true
}

Match Types

| matchType | Meaning | |-----------|---------| | full | SAME header + attention tone + End of Message all present | | partial | At least one EAS component detected (header, attention tone, EOM, or FSK energy) | | none | No EAS content detected |

How It Works

Three detection methods run on the audio:

  1. Attention tone detectionGoertzel algorithm tuned to 853 Hz and 960 Hz (the EAS dual-tone attention signal), using energy-ratio analysis. Also runs on bandpass-filtered audio (800-1000 Hz) to catch tones buried under speech or music.
  2. FSK detection — Detects the FSK modulation pattern (mark at 2083.3 Hz, space at 1562.5 Hz). The default mode uses Goertzel energy ratios with mark/space alternation validation. The sensitive mode applies narrow bandpass filters around each frequency and detects anti-correlation between the mark and space energy envelopes using Pearson correlation.
  3. SAME header decoding — Converts audio via FFmpeg and pipes into multimon-ng for full SAME protocol decoding with error correction.

Decoded SAME headers are parsed and enriched with human-readable lookups from 3,235 FIPS county codes and all standard EAS originator/event codes.

EAS Protocol Summary

An EAS message consists of:

  1. Digital header (SAME) — FSK at 520.83 baud, sent 3 times with ~1s silence between bursts
  2. Attention tone — 853 Hz + 960 Hz simultaneous, 8-25 seconds
  3. Voice/data message — the actual alert content
  4. End of MessageNNNN sent 3 times using the same FSK encoding

The SAME header format is: ZCZC-ORG-EEE-PSSCCC+TTTT-JJJHHMM-LLLLLLLL-

| Field | Description | |-------|-------------| | ORG | Originator (EAS, CIV, WXR, PEP) | | EEE | Event code (TOR, SVR, RWT, etc.) | | PSSCCC | Location: P=subdivision, SS=state FIPS, CCC=county FIPS | | TTTT | Duration (HHMM) | | JJJHHMM | Issue time (Julian day + UTC time) | | LLLLLLLL | Sender callsign |

Contributing

This project uses yarn, so install that before testing.

# Generate test audio fixtures
yarn generate-fixtures

# Run tests
yarn test

# Run lint
yarn lint

License

MIT

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request