SCP-ECG (Standard Communications Protocol for Computer-Assisted Electrocardiography)
Interchange format for resting-ECG recordings: patient demographics, acquisition metadata, lead definitions, optionally compressed waveform data, optional reference-beat data, and optional measurements and interpretive statements. A file is a 6-byte record header followed by a sequence of variable-length sections, each prefixed by its own 16-byte section header.
The reference text is EN 1064:2005+A1:2007 (CEN/TC 251 “Health informatics”), which supersedes ENV 1064:1993 and incorporates the SCP-ECG specification from the AIM SCP-ECG Project (1989-1990).
A file has two structural parts:
- Record header — fixed 6 bytes: a 2-byte CRC over the rest of the record, then a 4-byte unsigned record length in bytes (including the 6-byte header itself).
- Section list — a concatenation of sections, each with its own 16-byte ID header (CRC, section ID, section length, section version, protocol version, reserved) followed by a variable-length data part. Section 0 (Pointer Section) must come first; the other sections may appear in any order.
Twelve sections are defined by the spec (numbered 0..11):
| # | Required | Content |
|---|---|---|
| 0 | required | Pointers to every section present in the record |
| 1 | required | Patient demographic data + ECG acquisition data (tag/length/value layout) |
| 2 | dependent | Huffman tables (required if Sections 5/6 use Huffman encoding) |
| 3 | required | ECG lead definition (lead identifiers, sample ranges, simultaneity flags) |
| 4 | optional | QRS locations, reference-beat subtraction zones, protected areas |
| 5 | optional | Encoded reference beat type 0 |
| 6 | required | Rhythm data (full ECG or residual signal after reference-beat subtraction) |
| 7 | optional | Global measurements + pacemaker spike data |
| 8 | optional | Full-text interpretive statements |
| 9 | optional | Manufacturer-specific interpretive statements / overreading trail |
| 10 | optional | Per-lead measurement block |
| 11 | optional | Universal ECG interpretation statement codes (Annex F vocabulary) |
Section IDs 12..127 and 1024+ are reserved for future use. IDs 128..1023 are manufacturer-specific; their layout is not constrained by the spec but the Section ID header (16 bytes) is still mandatory.
Conformance categories
Section titled “Conformance categories”A conforming writer commits to one of two data-format categories. The upper four bits of Section 1 tag 14 byte 16 encode the category that the record itself was written under:
| Category | Required sections | Compatibility bits (tag 14 byte 16, MSB first) | Content |
|---|---|---|---|
| I | 0, 1, [2], 3, 6, (7), (8), (10) | 1101 0000 | Demographics + rhythm data (uncompressed or with lossless compression) |
| II | 0, 1, [2], 3, 4, 5, 6, (7), (8), (10) | 1110 0000 | Demographics + rhythm data + reference beats (lossless or “high” compression allowed) |
Square brackets mean Section 2 is required when Huffman encoding is used. Parentheses mark optional sections.
A device that imports SCP-ECG must accept at minimum Sections 0, 1, 3, 6, 7, 8.
File identification
Section titled “File identification”SCP-ECG has no magic number. A reader detects the format by:
- CRC-CCITT at bytes 0..1 of the record, covering bytes 2 through record-length-1. A correct CRC at that position is the strongest signal.
- Record length at bytes 2..5 (unsigned 32-bit little-endian) that matches the actual file size.
- Section 0 marker: bytes 11..16 of Section 0’s ID header contain
the ASCII string
"SCPECG".
Files conventionally use no fixed extension; clinical systems pick
their own (often .scp, .ecg).
Encoding rules
Section titled “Encoding rules”These rules apply to every field, every section, every byte.
- Byte order: little-endian for all multi-byte integers (“least significant byte transmitted first”). Single-byte values are unsigned unless declared signed.
- Signed values: two’s complement.
- Byte numbering: bytes are numbered from left to right, starting at 1. Bits within a byte are numbered from right (LSB = bit 0) to left (MSB = bit 7).
- Record byte 1 is the first byte of the 2-byte CRC.
- Sample numbering: ECG samples are 1-based. Sample index 0 is not used. Sample 1 is at time 0; with a 500 Hz rate sample 2 is at time 2 ms.
- Even-byte sections: every section’s byte count is even. If a
section’s payload is odd, a single
0x00padding byte is appended. Padding occurs only at the end of a section. - Reserved fields are
0x00. - Text fields are NULL-terminated and the NULL is counted toward the declared length. They follow the ISO/IEC 2022 conformance profile in Multi-lingual text encoding; the default character set is ISO/IEC 8859-1 (Latin-1).
- KByte in this document means exactly 1024 bytes.
- Pointer arithmetic: all byte indexes and pointers are 1-based unless stated otherwise.
Record header (6 bytes)
Section titled “Record header (6 bytes)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | crc | CRC-CCITT over bytes 2..end-of-record (see CRC-CCITT) |
| 2 | 4 | record_size | Unsigned 32-bit total record size in bytes, including the 6 bytes here |
After the record header comes Section 0, then any subset of the other sections in any order.
Record overview:
+-----+---------------+-----------+-----------+ ... +---------------+| CRC | Record Length | Section 0 | Section X | ... | Final Section |+-----+---------------+-----------+-----------+ ... +---------------+| 2 | 4 | 120 + var | var | ... | var | +--------------------------------------------------------+ CRC domainSection ID header (16 bytes)
Section titled “Section ID header (16 bytes)”Every section begins with the same 16-byte layout. The data part
length equals section_length - 16.
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | crc | CRC-CCITT over the rest of the section (bytes 2..section_length-1) |
| 2 | 2 | section_id | Section ID number (0..11 defined; 128..1023 manufacturer-specific) |
| 4 | 4 | section_length | Total section length in bytes, including these 16 header bytes; always even |
| 8 | 1 | section_version | Section version (= protocol version for sections 0..11; manufacturer-defined for 128..1023) |
| 9 | 1 | protocol_version | Protocol version, mirror of Section 1 tag 14 byte 15 |
| 10 | 6 | reserved | Zero, except in Section 0 where bytes 11..16 hold the ASCII string "SCPECG" |
If a section is not present in the record, its pointer entry in Section 0 carries length 0 and index 0 (see Section 0). Sections 2..11 always have a pointer slot in Section 0 even when absent.
Section 0 — Pointer section
Section titled “Section 0 — Pointer section”Always present, always first. Section 0 inventories the location and
length of every other section. Bytes 11..16 of the Section ID header
are the ASCII bytes "SCPECG" (no NULL terminator).
The data part is a sequence of 10-byte pointer fields, one per section. Pointer fields appear in ascending section-ID order:
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | section_id | Section ID (0..1023) |
| 2 | 4 | length | Section length in bytes, including its 16-byte ID header; 0 if absent |
| 6 | 4 | index | 1-based byte offset of the section’s first byte; 0 if absent |
Constraints:
- Pointer fields for Sections 0..11 must all be present even when the
section itself is absent. Absent sections set
length = 0andindex = 0. - The first pointer field is for Section 0 itself. Its index is always 7, because Section 0 sits immediately after the 6-byte record header.
- Manufacturer-specific sections (128..1023) get pointer fields too if they are present.
- A pointer field for Section 0 has length = 120 (16-byte header + ten 10-byte pointer fields for Sections 0..11) plus 10 bytes for each manufacturer-specific section that is present.
Section 1 — Patient and acquisition data
Section titled “Section 1 — Patient and acquisition data”Variable-length sequence of header fields. Each field has a
1-byte tag, a 2-byte unsigned length (counting only the value bytes,
not the tag or length), and length value bytes. The section ends at
the terminator tag 255 with length 0.
Field-level encoding rules:
- The tag and length bytes are not included in the declared length.
- Text values are NULL-terminated; the NULL is counted in
length. Length 0 means “field not defined”. - Maximum length per field is 65535 bytes. The spec recommends capping practical fields at 64 bytes and free-text fields at 80 bytes; see recommended text lengths.
- Tags 200..254 are reserved for manufacturer-defined fields.
- Tag 255 is the section terminator; length is 0.
- At most one instance per tag, except tags 10, 13, 30, 32, 35 which allow repetition.
Mandatory fields:
| Tag | Field | Notes |
|---|---|---|
| 2 | Patient ID | Primary key. First 16 characters must be unique. |
| 14 | Acquiring Device ID | |
| 25 | Date of Acquisition | |
| 26 | Time of Acquisition | Local wall-clock time at acquisition |
Strongly recommended:
| Tag | Field |
|---|---|
| 0 | Patient Last Name |
| 1 | Patient First Name |
| 5 | Patient Date of Birth |
| 8 | Patient Sex |
| 15 | Analyzing Device ID |
| 34 | Date Time Zone |
Defined tags (0..35)
Section titled “Defined tags (0..35)”| Tag | Length | Field | Encoding |
|---|---|---|---|
| 0 | var (text) | Last name | Used for the entire name if no first name is split out |
| 1 | var (text) | First name | |
| 2 | var (text) | Patient ID | |
| 3 | var (text) | Second last name | Locale-specific (US: family-member prefix; FR: maiden name; ES/PT/LATAM: second surname) |
| 4 | 3 | Age | Bytes 1-2: age as unsigned int; byte 3: unit (0=unspecified, 1=years, 2=months, 3=weeks, 4=days, 5=hours). All-zero = not specified. |
| 5 | 4 | Date of birth | Bytes 1-2: year (full 4-digit); byte 3: month 1..12; byte 4: day 1..31. All-zero = not specified. |
| 6 | 3 | Height | Bytes 1-2: value; byte 3: unit (0=unspecified, 1=cm, 2=in, 3=mm) |
| 7 | 3 | Weight | Bytes 1-2: value; byte 3: unit (0=unspecified, 1=kg, 2=g, 3=lb, 4=oz) |
| 8 | 1 | Sex | 0=not known, 1=male, 2=female, 9=unspecified |
| 9 | 1 | Race | 0=unspecified, 1=Caucasian, 2=Black, 3=Oriental, 4..9 reserved, 10..255 manufacturer |
| 10 | var | Drugs | Byte 1: drug-table indicator (0 = standard table below); byte 2: class code; byte 3: drug code within class; bytes 4..: optional drug name as NULL-terminated text. Multiple instances allowed. |
| 11 | 2 | Systolic blood pressure | mmHg |
| 12 | 2 | Diastolic blood pressure | mmHg |
| 13 | var (text) | Diagnosis or referral indication | Multiple instances allowed |
| 14 | var | Acquiring device ID | See Device ID structure |
| 15 | var | Analyzing device ID | Same layout as tag 14 |
| 16 | var (text) | Acquiring institution description | |
| 17 | var (text) | Analyzing institution description | |
| 18 | var (text) | Acquiring department description | |
| 19 | var (text) | Analyzing department description | |
| 20 | var (text) | Referring physician | |
| 21 | var (text) | Latest confirming physician | |
| 22 | var (text) | Technician description | |
| 23 | var (text) | Room description | |
| 24 | 1 | Stat code | 0=routine; higher = more urgent (1..10 recommended) |
| 25 | 4 | Date of acquisition | Same layout as tag 5 |
| 26 | 3 | Time of acquisition | Byte 1: hours 0..23; byte 2: minutes 0..59; byte 3: seconds 0..59. Local time in the acquisition timezone. |
| 27 | 2 | Baseline filter cutoff | -3 dB high-pass cutoff in 1/100 Hz |
| 28 | 2 | Low-pass filter cutoff | -3 dB cutoff in Hz |
| 29 | 1 | Filter bit map | Bit 0: 60 Hz notch; bit 1: 50 Hz notch; bit 2: artifact; bit 3: baseline (adaptive/spline); bits 4..7 undefined. All-zero = not specified. |
| 30 | var (text) | Free text | Multiple instances allowed |
| 31 | var (text) | ECG sequence number | |
| 32 | var | Medical history codes | Byte 1: code-table indicator (0 = standard table below); remaining bytes: one code per byte. Multiple instances allowed. |
| 33 | 2 | Electrode configuration code | Byte 1: 12-lead placement system; byte 2: XYZ-lead system (see Electrode configuration) |
| 34 | var | Date time zone | See Time zone |
| 35 | var (text) | Free-text medical history | Multiple instances allowed |
| 255 | 0 | Section terminator | No value |
Recommended free-text lengths
Section titled “Recommended free-text lengths”The spec recommends, per field, the following practical caps. Multiple instances are allowed where noted; each instance may use the full allowed length and is NULL-terminated.
| Tag | Field | Multiple | Recommended length |
|---|---|---|---|
| 0 | Last name | no | 40 |
| 1 | First name | no | 40 |
| 2 | Patient ID | no | 40 |
| 3 | Second last name | no | 40 |
| 10 | Drugs | yes | 40 |
| 13 | Diagnosis / referral | yes | 80 |
| 14 | Acquiring device ID | no | 40 |
| 15 | Analyzing device ID | no | 40 |
| 16 | Acquiring institution | no | 40 |
| 17 | Analyzing institution | no | 40 |
| 18 | Acquiring department | no | 40 |
| 19 | Analyzing department | no | 40 |
| 20 | Referring physician | no | 60 |
| 21 | Latest confirming physician | no | 60 |
| 22 | Technician description | no | 40 |
| 23 | Room description | no | 40 |
| 30 | Free text | yes | 80 |
| 31 | ECG sequence number | no | 12 |
| 35 | Free-text medical history | yes | 80 |
Device ID structure
Section titled “Device ID structure”Used by tags 14 (acquiring device) and 15 (analyzing device).
| Off | Size | Field |
|---|---|---|
| 0 | 2 | Institution number |
| 2 | 2 | Department number |
| 4 | 2 | Device ID |
| 6 | 1 | Device type: 0=cart, 1=system/host |
| 7 | 1 | Set to 255. Legacy use: numeric manufacturer code (0..21 assigned, 100 = other). Use the trailing string instead. |
| 8 | 6 | Model description as up to 5 ASCII bytes + NULL terminator |
| 14 | 1 | SCP-ECG protocol revision (e.g. 1.0 -> 10, 2.0 -> 20; backward-compatible revisions where possible) |
| 15 | 1 | SCP-ECG compatibility level (per Annex B; upper 4 bits encode the conformance category) |
| 16 | 1 | Language support code, bit-mapped (see Language support) |
| 17 | 1 | Device capability bit map: bit 4 = can print ECG reports; bit 5 = can interpret; bit 6 = can store; bit 7 = can acquire; bits 0..3 reserved |
| 18 | 1 | AC mains frequency: 0=unspecified, 1=50 Hz, 2=60 Hz |
| 19 | 17 | Reserved (bytes 20..35 of the tag value); zero-fill |
| 36 | 1 | Length of the analysing-program revision string (>= 1) |
| 37 | var | NULL-terminated analysing-program revision number |
| … | var | NULL-terminated serial number of the acquisition device |
| … | var | NULL-terminated acquisition-device system software identifier |
| … | var | NULL-terminated SCP implementation software identifier (<= 24 chars + NULL) |
| … | var | NULL-terminated manufacturer registered trade name |
Each trailing string is its own NULL-terminated UTF/ISO-8859 byte run. An empty trailing string is encoded as a single NULL byte.
Language support code
Section titled “Language support code”One byte in tag 14 byte 17. Bit map identifies the character sets the device can read and write:
| Bit pattern (MSB..LSB) | Meaning |
|---|---|
xxxx xx00 | 8-bit ASCII only |
xxxx xx01 | ISO/IEC 8859-1 Latin-1 |
0000 0011 | ISO/IEC 8859-2 Latin-2 (Central/Eastern European) |
0000 1011 | ISO/IEC 8859-4 Latin-4 (Baltic) |
0001 0011 | ISO/IEC 8859-5 Cyrillic |
0001 1011 | ISO/IEC 8859-6 Arabic |
0010 0011 | ISO/IEC 8859-7 Greek |
0010 1011 | ISO/IEC 8859-8 Hebrew |
0011 0011 | ISO/IEC 8859-11 Thai |
0011 1011 | ISO/IEC 8859-15 Latin-9 (“Latin-0”) |
0000 1111 | ISO/IEC 10646 (Unicode) |
0001 1111 | JIS X 0201-1976 (Japanese single-byte) |
0010 1111 | JIS X 0208-1997 (Japanese multi-byte) |
0011 1111 | JIS X 0212-1990 (Japanese supplementary) |
0100 1111 | GB 2312-80 (Simplified Chinese) |
0101 1111 | KS C5601-1987 (Korean) |
1111 1111 | Manufacturer-specific |
Other bit combinations are reserved.
Drug class table
Section titled “Drug class table”Tag 10 byte 2 selects a class; byte 3 selects a drug within the class. Codes 100..255 in byte 2 are out-of-band markers (“not taking drugs”, “drugs unknown”, “other”, or manufacturer-specific).
| Class | Name | Examples (byte 3) |
|---|---|---|
| 0 | Unspecified | byte 3 must also be 0; text in bytes 4.. is the only description |
| 1 | Digitalis | 1=Digoxin/Lanoxin, 2=Digitoxin |
| 2 | Antiarrhythmic | 1=Disopyramide, 2=Quinidine, 3=Procainamide, 4=Lidocaine, 5=Phenytoin, 6=Dilantin, 7=Amiodarone, 8=Tocainide, 9=other, 10=Encainide, 11=Mexiletine, 12=Flecainide, 13=Lorcainide |
| 3 | Diuretics | 1=Thiazide, 2=Furosemide, 3=Potassium chloride |
| 4 | Antihypertensive | 1=Clonidine, 2=Prazosin, 3=Hydralazine |
| 5 | Antianginal | 1=Isosorbide, 2=Calcium blockers, 3=Nitrates |
| 6 | Antithrombotic | 1=Aspirin, 2=Coumadin, 3=Heparin, 4=Warfarin, 5=Streptokinase, 6=t-PA |
| 7 | Beta blockers | 1=Propranolol, 2=Corgard, 3=Atenolol, 4=Metoprolol, 5=Pindolol, 6=Acebutolol |
| 8 | Psychotropic | 1=Tricyclic antidepressant, 2=Phenothiazide, 3=Barbiturate |
| 9 | Calcium blockers | 1=Nifedipine, 2=Verapamil |
| 10 | Antihypotensive | 1=Asthmatic drug, 2=Aminophyline, 3=Isuprel |
| 11 | Anticholesterol | 1=Colestid, 2=Lovastatin, 3=Simvastatin, 4=Fibrates |
| 12 | ACE inhibitors | 1=Captopril |
| 13..99 | Reserved | |
| 100 | Not taking drugs | byte 3 = 0 |
| 101 | Drugs, unknown | byte 3 = 0 |
| 102 | Other medication | byte 3 = 0 |
| 103..255 | Manufacturer-specific |
Sub-code 9 is reserved for “other” within each class.
Medical history codes
Section titled “Medical history codes”Tag 32 byte 1 selects the code table; byte 1 = 0 means the standard SCP-ECG table below, where each subsequent byte is one diagnosis code:
| Code | Meaning |
|---|---|
| 0 | Not specified |
| 1 | Apparently healthy |
| 10 | Acute myocardial infarction |
| 11 | Myocardial infarction |
| 12 | Previous myocardial infarction |
| 15 | Ischemic heart disease |
| 18 | Peripheral vascular disease |
| 20 | Cyanotic congenital heart disease |
| 21 | Acyanotic congenital heart disease |
| 22 | Valvular heart disease |
| 25 | Hypertension |
| 27 | Cerebrovascular accident |
| 30 | Cardiomyopathy |
| 35 | Pericarditis |
| 36 | Myocarditis |
| 40 | Post-operative cardiac surgery |
| 42 | Implanted cardiac pacemaker |
| 45 | Pulmonary embolism |
| 50 | Respiratory disease |
| 55 | Endocrine disease |
| 60 | Neurological disease |
| 65 | Alimentary disease |
| 70 | Renal disease |
| 80 | Pre-operative general surgery |
| 81 | Post-operative general surgery |
| 90 | General medical |
| 100 | Other |
| 128..255 | Manufacturer-specific |
Codes 2..9, 13, 14, 16, 17, 19, 23, 24, 26, 28, 29, 31..34, 37..39, 41, 43, 44, 46..49, 51..54, 56..59, 61..64, 66..69, 71..79, 82..89, 91..99 are reserved for future extension.
Electrode configuration
Section titled “Electrode configuration”Tag 33 byte 1: 12-lead electrode placement system.
| Code | System |
|---|---|
| 0 | Unspecified |
| 1 | Standard 12-lead: RA, RL, LA, LL on limb extremities; V1..V6 at standard chest sites |
| 2 | Mason-Likar: RA/RL/LA/LL on torso; V1..V6 standard chest, individually placed |
| 3 | Mason-Likar limb; V1..V6 in a single chest-pad assembly |
| 4 | All electrodes (limb + V1..V6) in one chest pad (e.g. Omnitrode) |
| 5 | 12-lead derived from Frank XYZ leads |
| 6 | 12-lead derived from non-standard leads |
| 7..255 | Reserved |
Tag 33 byte 2: XYZ-lead electrode placement system.
| Code | System |
|---|---|
| 0 | Unspecified |
| 1 | Frank lead system (Frank 1956) |
| 2 | McFee-Parungao lead system |
| 3 | Cube lead system (Grishman et al. 1951) |
| 4 | Bipolar uncorrected XYZ |
| 5 | Pseudo-orthogonal XYZ (Holter-style) |
| 6 | XYZ derived from 12-lead |
| 7..255 | Reserved |
Time zone
Section titled “Time zone”Tag 34. Lets the date/time in tags 25/26 be converted to UTC. Three
ways to specify the zone are stacked; later mechanisms are consulted
only when offset == 0x7FFF.
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | offset | Signed minutes east of UTC, range -780..+780 (i.e. ±13 h); 0x7FFF = unset |
| 2 | 2 | index | Unsigned manufacturer-defined lookup index when offset == 0x7FFF; 0 = unset; 1..1000 reserved; 1001..32766 manufacturer; 32767 reserved |
| 4 | var | description | NULL-terminated POSIX TZ-style string when offset == 0x7FFF; 1 byte minimum (the NULL) |
Tag 34 may be omitted entirely if the device does not record a time
zone. A present tag 34 with offset = 0x7FFF, index = 0, description = "" is the explicit “unknown” form.
Section 2 — Huffman tables
Section titled “Section 2 — Huffman tables”Optional. Required when Sections 5 (reference beat) or 6 (rhythm) use Huffman encoding. Defines one or more Huffman tables used to compress the reference-beat and residual data.
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | table_count | Number of Huffman tables; value 19999 means “use default table” (see Default Huffman table) |
| 2 | 2 | structure_count[0] | Number of code structures in table 1 |
| 4 | N*9 | structures[0] | structure_count[0] × 9-byte code structure |
| … | 2 | structure_count[k] | Number of code structures in table k+1 |
| … | N*9 | structures[k] | Code structures for table k+1 |
Tables are referenced by 1-based position. The first table is the implicit default; Huffman streams in Sections 5/6 start by decoding under table 1.
Each code structure is 9 bytes:
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | prefix_bits | Number of bits in the prefix |
| 1 | 1 | total_bits | Number of bits in the entire code |
| 2 | 1 | mode | 0 = switch to another Huffman table (the table number is in base_value); 1 = Huffman or initial encoding (see below) |
| 3 | 2 | base_value | Decoded sample value in AVM units (signed 16-bit). When mode == 0, this holds the target table number. |
| 5 | 4 | base_code | The prefix bit pattern. First bit in the code is the least significant bit of this 4-byte little-endian field. |
If mode == 1 and prefix_bits == total_bits, the structure is a
pure-Huffman code: the prefix is a single self-contained code mapping
to base_value. If prefix_bits < total_bits, the structure is an
initial-encoding code: after the prefix the bitstream carries an
explicit (total_bits - prefix_bits)-bit remainder, which is the
actual signed sample. The MSB of the remainder is sign-extended into
the reconstructed sample word.
If mode == 0, the structure switches the active table to the table
indexed by base_value. The active table resets at the start of each
lead.
Bits inside a byte are picked MSB-first. Bytes are consumed in stream order.
Default Huffman table
Section titled “Default Huffman table”When Section 2’s table_count == 19999 (or no Section 2 is present
and the writer chose Huffman encoding), the default table below is
used. It is tuned for second-difference residuals of typical resting
ECGs:
| # | Total bits | Prefix bits | Mode | Base value | Prefix (binary) | Stored base_code (decimal) |
|---|---|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 0 | 0 | 0 |
| 2 | 3 | 3 | 1 | +1 | 100 | 1 |
| 3 | 3 | 3 | 1 | -1 | 101 | 5 |
| 4 | 4 | 4 | 1 | +2 | 1100 | 3 |
| 5 | 4 | 4 | 1 | -2 | 1101 | 11 |
| 6 | 5 | 5 | 1 | +3 | 11100 | 7 |
| 7 | 5 | 5 | 1 | -3 | 11101 | 23 |
| 8 | 6 | 6 | 1 | +4 | 111100 | 15 |
| 9 | 6 | 6 | 1 | -4 | 111101 | 47 |
| 10 | 7 | 7 | 1 | +5 | 1111100 | 31 |
| 11 | 7 | 7 | 1 | -5 | 1111101 | 95 |
| 12 | 8 | 8 | 1 | +6 | 11111100 | 63 |
| 13 | 8 | 8 | 1 | -6 | 11111101 | 191 |
| 14 | 9 | 9 | 1 | +7 | 111111100 | 127 |
| 15 | 9 | 9 | 1 | -7 | 111111101 | 383 |
| 16 | 10 | 10 | 1 | +8 | 1111111100 | 255 |
| 17 | 10 | 10 | 1 | -8 | 1111111101 | 767 |
| 18 | 18 | 10 | 1 | 8-bit initial value | 1111111110 | 511 |
| 19 | 26 | 10 | 1 | 16-bit initial value | 1111111111 | 1023 |
base_code is stored bit-reversed in its 4-byte slot (LSB of the
field carries the first bit of the prefix). Structures 18 and 19
encode out-of-table samples by appending an 8- or 16-bit signed
remainder after the 10-bit prefix.
Decoding a Huffman bitstream
Section titled “Decoding a Huffman bitstream”Each lead’s payload in Sections 5 and 6 is a self-contained Huffman bitstream. The active table resets to table 1 at the start of every lead. The decoder maintains a single bit cursor into the lead bytes; bits within a byte are consumed from the most significant bit toward the least significant, then to the next byte.
To decode one sample:
- Read the prefix. Read bits one at a time and compare the
accumulated bit pattern against every structure in the active
table whose
prefix_bitsequals the number of bits read. If nothing matches, read one more bit and repeat. Continue until exactly one structure matches. - Branch on the matched structure’s
mode:mode == 0(table switch). The active table changes to the table number stored inbase_value. Do not emit a sample; loop back to step 1 and decode the next sample under the new table.mode == 1,prefix_bits == total_bits(pure Huffman). Emitbase_valueas the decoded sample. Advance the cursor byprefix_bits(already consumed) and return.mode == 1,prefix_bits < total_bits(initial encoding). Readtotal_bits - prefix_bitsmore bits from the stream as the remainder. Sign-extend the remainder by replicating its most significant bit into the higher bits of the 16-bit sample word, then emit.base_valueis unused for initial-encoding structures.
- Repeat for the next sample.
A worked example: with the default table, the bit sequence
1111111110 00000101 decodes by matching structure 18 (prefix
1111111110, total 18 bits), then reading the 8-bit remainder
00000101 (decimal 5), sign-extending the MSB (0) into the upper
8 bits, and emitting 0x0005 = 5.
A switching example with custom tables: prefix 1110 matched in
table 1 with mode = 0, base_value = 2 switches the active table
to table 2; the next bit starts a fresh match against table 2.
The decoder stops when the lead’s declared byte length is exhausted. Trailing bits inside the last byte are discarded.
Section 3 — ECG lead definition
Section titled “Section 3 — ECG lead definition”Required. Lists the leads in the record, the sample range covered by each lead, and whether reference-beat subtraction is used.
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | lead_count | Number of leads |
| 1 | 1 | flags | Bit 0: 1 = reference-beat subtraction used in compression. Bit 1: reserved. Bit 2: 1 = all leads recorded simultaneously. Bits 3..7: count of leads recorded simultaneously |
| 2 | 9*N | leads | lead_count × 9-byte lead descriptor |
Each lead descriptor:
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 4 | start_sample | Unsigned 1-based starting sample number |
| 4 | 4 | end_sample | Unsigned 1-based ending sample number (inclusive) |
| 8 | 1 | lead_id | Lead identification code (see Lead identification codes) |
When leads are not all simultaneously recorded, descriptors are
grouped: the first flags[3..7] descriptors describe one
simultaneously-recorded group, the next descriptors describe the
next group, and so on.
Sample numbering is 1-based and refers to all leads in a recording
group together. For 8 leads (I, II, V1..V6) recorded simultaneously
at 500 Hz for 10 s, every lead has start_sample = 1,
end_sample = 5000. For two groups of three leads recorded at 500 Hz
for 2.5 s each (I/II/III then aVR/aVL/aVF), the first group has
samples 1..1250, the second 1251..2500.
To convert sample numbers to time, divide by the per-section sample rate from Section 6 (rhythm) or Section 5 (reference beat).
Lead identification codes
Section titled “Lead identification codes”The lead-id byte uses the SCP-ECG lead vocabulary. Codes 200..255 are
manufacturer-specific. Codes 185..199 are reserved for future use.
The full vocabulary mirrors ISO/IEEE 11073-10101 (MDC_ECG_LEAD_*).
Standard 12-lead set:
| Code | Lead | Code | Lead | Code | Lead |
|---|---|---|---|---|---|
| 0 | Unspecified | 61 | III | 66 | V8 |
| 1 | I | 62 | aVR | 67 | V9 |
| 2 | II | 63 | aVL | 68 | V8R |
| 3 | V1 | 64 | aVF | 69 | V9R |
| 4 | V2 | 65 | aVR (inverted, aVRneg) | 147 | RL (right leg) |
| 5 | V3 | 21 | LA (left arm) | ||
| 6 | V4 | 22 | RA (right arm) | ||
| 7 | V5 | 23 | LL (left leg) | ||
| 8 | V6 |
Right-sided precordials: 9 (V7), 10..15 (V2R..V7R).
Orthogonal Frank-lead set: 16 (X), 17 (Y), 18 (Z).
Frank-derived leads (“f” prefix): 24..30 (fI, fE, fC, fA, fM, fF, fH).
Nehb leads: 70 (D, dorsal), 71 (A, anterior), 72 (J, inferior).
Modified chest leads: 91..97 (MCL, MCL1..MCL6), 121 (CM7), 122 (CH5), 105 (CM), 106..110 (CM1..CM6), 123..125 (CS5, CB5, CR5), 126 (ML).
Stress-test bipolar leads: 19 (CC5), 20 (CM5), 98 (CC, symmetric), 99..104 (CC1..CC4, CC6, CC7).
EASI lead set: 131 (ES), 132 (AS), 133 (AI), 134 (S, upper sternum).
Auxiliary leads: 75..78 (A1..A4 unipolar), 127..130 (AB1..AB4 bipolar).
Defibrillator and pacing leads: 73 (Defib, anterior-lateral), 74 (Extern, anterior-posterior external pacing).
Other: 86 (Chest), 87 (V precordial), 88..90 (VR, VL, VF nonaugmented), 19 (CC5), 91 (MCL).
Canine leads: 148 (CV5RL), 149 (CV6LL), 150 (CV6LU), 151 (V10).
Derived versions of any lead use code original + 30 for IDs
1..30, or are listed individually at 31..60 (derived I, II, V1..V7,
V2R..V7R, X, Y, Z, CC5, CM5, LA, RA, LL, fI, fE, fC, fA, fM, fF, fH)
and 79..85 (derived V8, V9, V8R, V9R, D, A, J) and 111..120 (derived
III, aVR, aVL, aVF, aVRneg, Chest, V, VR, VL, VF) and 135..146,
152..184 for the remaining derived leads.
Notes on lead semantics:
- V2R is equivalent to V1; V1R is equivalent to V2.
- X, Y, Z are the orthogonal Frank-lead set.
- The “d-” prefix marks an algebraically derived lead, distinguished from a directly recorded one.
- EASI is a trademark owned by Philips. Codes 131..134 use the positions described by Gordon Dower.
Section 4 — QRS locations, subtraction zones, protected areas
Section titled “Section 4 — QRS locations, subtraction zones, protected areas”Optional. Stores the location and reference-beat type of each QRS in the rhythm record. Required when reference-beat subtraction or bimodal compression is used.
Header (6 bytes)
Section titled “Header (6 bytes)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | ref_beat_length | Length of reference beat type 0 in milliseconds |
| 2 | 2 | fcM | 1-based sample number of the fiducial (QRS trigger) within reference beat 0 |
| 4 | 2 | qrs_count | Total number of QRS complexes in the record |
To convert ref_beat_length from ms to samples, divide by the
sample-time interval (in µs) from Section 5: N = (1000 * L) / SI,
truncating toward zero. The writer is responsible for picking a
length that gives the intended sample count.
Subtraction-zone blocks (14 bytes per QRS)
Section titled “Subtraction-zone blocks (14 bytes per QRS)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | beat_type | Reference-beat type for this QRS. Type 0 is the “normal” beat used for subtraction. |
| 2 | 4 | SB | 1-based sample on residual data where subtraction of beat 0 starts. 0 if beat_type != 0. |
| 6 | 4 | fc | 1-based sample on residual data of this QRS’s fiducial point |
| 10 | 4 | SE | 1-based sample on residual data where subtraction of beat 0 ends. 0 if beat_type != 0. |
All sample numbers refer to the original raw data before any decimation or filtering. First sample is 1.
Protected-area blocks (8 bytes per QRS)
Section titled “Protected-area blocks (8 bytes per QRS)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 4 | QB | 1-based sample on residual data: start of this QRS’s protected area |
| 4 | 4 | QE | 1-based sample on residual data: end of this QRS’s protected area |
The protected area always includes QRS onset..offset and is sized so that the non-protected gap between consecutive complexes is an integer multiple of the decimation factor (see Sample decimation).
Section 4 may carry protected-area blocks even when reference-beat
subtraction is not used, e.g. for bimodal-compression-only files. In
that case SB and SE are 0.
Section 5 — Encoded type 0 reference beat
Section titled “Section 5 — Encoded type 0 reference beat”Optional. The encoded representative beat used for subtraction reconstruction. Required when Section 3’s reference-beat-subtraction flag is set.
Header (6 bytes)
Section titled “Header (6 bytes)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | avm | Amplitude value multiplier in nanovolts (e.g. 1250 -> 1.250 µV per LSB) |
| 2 | 2 | sample_time | Sample time interval in microseconds (e.g. 2000 -> 500 Hz) |
| 4 | 1 | diff_used | 0 = raw samples; 1 = first differences; 2 = second differences (see Difference encoding) |
| 5 | 1 | reserved | 0 |
Lead-length table
Section titled “Lead-length table”lead_count × 2-byte unsigned values, listing the byte length of
each encoded lead’s payload in the order leads are declared in
Section 3.
Lead payload
Section titled “Lead payload”Each lead’s bytes follow the length table, concatenated in lead order. When Section 2 is present, the payload is a Huffman bitstream keyed by the indicated table. When Section 2 is absent, the payload is signed 16-bit samples (or differences) in little-endian.
A writer wanting non-Huffman fixed-width encoding can supply a
“dummy” Section 2 with one structure: prefix_bits = 0,
total_bits = N. That yields plain N-bit two’s-complement samples.
Section 6 — Rhythm data
Section titled “Section 6 — Rhythm data”Required. Carries either the full ECG rhythm or, if reference-beat subtraction is on, the residual signal after subtraction.
Header (6 bytes)
Section titled “Header (6 bytes)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | avm | Amplitude value multiplier in nanovolts |
| 2 | 2 | sample_time | Sample time interval in microseconds |
| 4 | 1 | diff_used | 0 = raw; 1 = first differences; 2 = second differences |
| 5 | 1 | bimodal | 0 = bimodal compression off; 1 = bimodal compression on |
When bimodal == 1, the per-sample geometry differs inside vs.
outside the protected areas listed in Section 4: protected samples
use the AVM and sample interval declared in Section 5 (reference
beat); non-protected samples use Section 6’s AVM and sample interval.
Lead-length table and lead payload
Section titled “Lead-length table and lead payload”Same layout as Section 5: a 2-byte length per lead, then the leads’ payloads concatenated in declaration order. Same Huffman / raw rules as Section 5.
Section 7 — Global measurements and pacemaker spikes
Section titled “Section 7 — Global measurements and pacemaker spikes”Optional. Aggregates per-beat and global measurements across leads, plus a list of pacemaker spikes.
Header and measurement block
Section titled “Header and measurement block”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | count_marker | If equal to (Section 7’s reference-beat-type count): subsequent measurement blocks describe each reference-beat type. If equal to (QRS count + 1): subsequent blocks describe each individual QRS. The first measurement block always describes reference beat type 0. |
| 1 | 1 | pacemaker_count | Number of pacemaker spikes whose timing/amplitude is reported |
| 2 | 2 | rr_average | Average RR interval over all QRS, in ms |
| 4 | 2 | pp_average | Average PP interval over all QRS, in ms |
Each measurement block is 16 bytes:
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | p_onset | ms from beat / record start |
| 2 | 2 | p_offset | ms |
| 4 | 2 | qrs_onset | ms |
| 6 | 2 | qrs_offset | ms |
| 8 | 2 | t_offset | ms |
| 10 | 2 | p_axis | Angular degrees in frontal plane; 999 = undefined |
| 12 | 2 | qrs_axis | Angular degrees; 999 = undefined |
| 14 | 2 | t_axis | Angular degrees; 999 = undefined |
Frontal-plane axis convention: 0 points to the patient’s left (positive horizontal); positive angles rotate clockwise (down); negative angles rotate counter-clockwise (up).
Onset/offset values are referenced to the start of the reference beat (when the block describes a beat type) or to the start of the ECG record (when the block describes one individual QRS).
Pacemaker spike data (4 bytes per spike)
Section titled “Pacemaker spike data (4 bytes per spike)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | time_ms | Unsigned ms from start of rhythm record |
| 2 | 2 | amplitude_uv | Signed µV |
Spike time has a resolution of 2 ms or finer.
Pacemaker spike info (6 bytes per spike, same order as the spike-data list)
Section titled “Pacemaker spike info (6 bytes per spike, same order as the spike-data list)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | spike_type | 0 = unknown; 1 = triggers neither P nor QRS; 2 = triggers QRS; 3 = triggers P wave; 4..127 reserved; 128..254 manufacturer; 255 = no analysis |
| 1 | 1 | source | 0 = unknown; 1 = internal; 2 = external; 3..255 reserved |
| 2 | 2 | triggered_qrs | 1-based index into the QRS list; 0 = no link |
| 4 | 2 | pulse_width_us | Microseconds; 0 = unknown |
QRS type information
Section titled “QRS type information”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | qrs_count | Number of QRS complexes |
| 2 | 1 | type_0 | Reference-beat type of the first QRS |
| 3 | 1 | type_1 | Reference-beat type of the second QRS |
| … | etc. |
Additional global measurements
Section titled “Additional global measurements”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | vent_rate_bpm | Ventricular rate, beats per minute |
| 2 | 2 | atrial_rate_bpm | Atrial rate, beats per minute |
| 4 | 2 | qtc_ms | Corrected QT in ms |
| 6 | 1 | qtc_formula | 0=unknown, 1=Bazett, 2=Hodges, 3..127 reserved, 128..254 manufacturer, 255 = not available |
| 7 | 2 | tagged_bytes | Bytes of tagged fields that follow (0 if none) |
| 9 | var | tagged_fields | Tag/length/value triples ending with tag 255 length 0 |
Defined tags in the tagged-field area:
| Tag | Length | Field |
|---|---|---|
| 0 | 5 | QTend all-lead dispersion. Bytes: 1 = max-min QT in ms; 2 = HR-corrected max-min; 3 = stdev QT in ms; 4 = HR-corrected stdev; 5 = HR formula. Values 0..254 ms; 255 = not provided. |
| 1 | 5 | QTpeak all-lead dispersion. Same layout as tag 0, measured to T-wave peak instead of T-wave offset. |
| 2 | 5 | QTend precordial dispersion. Same layout, precordial leads only. |
| 3 | 5 | QTpeak precordial dispersion. Same layout, precordial leads only. |
| 4..254 | 0 | Reserved |
| 255 | 0 | Terminator |
A variable-length manufacturer-specific block may follow the tagged fields. Its start is computed from the Section ID header length; its end is the section boundary.
Section 8 — Full-text interpretive statements
Section titled “Section 8 — Full-text interpretive statements”Optional. Carries the latest free-text interpretation, including any overreading. Only the most recent interpretation is kept.
Header (9 bytes)
Section titled “Header (9 bytes)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | confirmation | 0 = original (not overread); 1 = confirmed; 2 = overread, not confirmed |
| 1 | 2 | year | 4-digit year |
| 3 | 1 | month | 1..12 |
| 4 | 1 | day | 1..31 |
| 5 | 1 | hour | 0..23 |
| 6 | 1 | minute | 0..59 |
| 7 | 1 | second | 0..59 |
| 8 | 1 | statement_count | Number of statement entries |
Each statement entry:
| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | sequence_number | 1-based statement index |
| 1 | 2 | statement_length | Bytes in text including the NULL terminator |
| 3 | var | text | NULL-terminated text |
Mnemonic codes from the Annex F vocabulary may appear in the text, but always alongside descriptive prose. Pure unaccompanied codes are not allowed in Section 8.
Section 9 — Manufacturer-specific interpretive data
Section titled “Section 9 — Manufacturer-specific interpretive data”Optional. Carries vendor-defined diagnostic statements and the overreading trail of historical interpretations. The data part layout is entirely manufacturer-defined; only the 16-byte Section ID header is constrained.
Section 10 — Per-lead measurement block
Section titled “Section 10 — Per-lead measurement block”Optional. One record per recorded lead with up to 50 standard measurements plus a manufacturer area.
Header (4 bytes)
Section titled “Header (4 bytes)”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 2 | lead_count | Number of leads with measurement records |
| 2 | 2 | manufacturer | Manufacturer-specific |
Per-lead record
Section titled “Per-lead record”Bytes 5..104 are 50 signed 16-bit measurements. Bytes 105.. are a manufacturer measurement area. Bytes 67..104 are reserved and zero when a manufacturer block follows.
| Off | Size | Field |
|---|---|---|
| 0 | 2 | lead_id (matches Section 3 lead codes) |
| 2 | 2 | Record length in bytes, excluding bytes 0..3 |
| 4 | 2 | P_duration_ms (total P, including P+ and P-) |
| 6 | 2 | PR_interval_ms |
| 8 | 2 | QRS_duration_ms |
| 10 | 2 | QT_interval_ms |
| 12 | 2 | Q_duration_ms |
| 14 | 2 | R_duration_ms |
| 16 | 2 | S_duration_ms |
| 18 | 2 | R'_duration_ms |
| 20 | 2 | S'_duration_ms |
| 22 | 2 | Q_amplitude_uV (signed; Q is negative) |
| 24 | 2 | R_amplitude_uV (signed) |
| 26 | 2 | S_amplitude_uV (signed; S is negative) |
| 28 | 2 | R'_amplitude_uV |
| 30 | 2 | S'_amplitude_uV |
| 32 | 2 | J_point_amplitude_uV (= amplitude at end of QRS) |
| 34 | 2 | P+_amplitude_uV |
| 36 | 2 | P-_amplitude_uV (signed; negative) |
| 38 | 2 | T+_amplitude_uV |
| 40 | 2 | T-_amplitude_uV (signed; negative) |
| 42 | 2 | ST_slope_uV_per_s |
| 44 | 2 | P_morphology (see below) |
| 46 | 2 | T_morphology (see below) |
| 48 | 2 | iso_onset_ms (segment I: global QRS onset to lead’s QRS onset) |
| 50 | 2 | iso_offset_ms (segment K: lead’s QRS offset to global QRS offset) |
| 52 | 2 | intrinsicoid_deflection_ms |
| 54 | 2 | quality_code (see below) |
| 56 | 2 | ST_at_J_plus_20ms_uV |
| 58 | 2 | ST_at_J_plus_60ms_uV |
| 60 | 2 | ST_at_J_plus_80ms_uV |
| 62 | 2 | ST_at_J_plus_RR_div16_uV |
| 64 | 2 | ST_at_J_plus_RR_div8_uV |
| 66 | 38 | Reserved (zeros if a manufacturer block follows) |
| 104 | var | Manufacturer measurement block |
Special amplitude codes:
| Value | Meaning |
|---|---|
| 29999 | Measurement not computed |
| 29998 | Lead rejected by the measurement program |
| 19999 | Wave not present (e.g. Q wave absent, P wave during atrial fibrillation) |
P / T morphology codes:
| Code | Shape |
|---|---|
| 0 | Unknown |
| 1 | Positive |
| 2 | Negative |
| 3 | Positive / negative |
| 4 | Negative / positive |
| 5 | Positive / negative / positive |
| 6 | Negative / positive / negative |
| 7 | Notched M-shaped |
| 8 | Notched W-shaped |
Quality code (2 bytes, 8 two-bit fields). Bit 0 is the LSB of byte 54; bit 15 is the MSB of byte 55. Each pair encodes a noise level in one category:
| Bits | Category | Levels |
|---|---|---|
| 0..1 | AC mains noise | 0 = none; 1 = moderate; 2 = severe; 3 = unknown |
| 2..3 | Overrange | same |
| 4..5 | Baseline wander | same |
| 6..7 | Tremor / muscle artifact | same |
| 8..9 | Spikes / sudden jumps | same |
| 10..11 | Electrode loose or off | same |
| 12..13 | Pacemaker | same |
| 14..15 | Interchanged lead | same |
Section 11 — Universal interpretation statement codes
Section titled “Section 11 — Universal interpretation statement codes”Optional. Carries the same interpretation/overreading content as Section 8, but coded against the Annex F vocabulary instead of free text. Sections 8, 9, and 11 must be mutually consistent.
Header (9 bytes)
Section titled “Header (9 bytes)”Same layout as Section 8: confirmation byte + 7-byte timestamp + statement count.
Statement entry
Section titled “Statement entry”| Off | Size | Field | Content |
|---|---|---|---|
| 0 | 1 | sequence_number | 1-based, used by Type 3 logical operands |
| 1 | 2 | statement_length | Bytes from type through and including the terminating NULL |
| 3 | 1 | type | 1 = universal statement code; 2 = full text; 3 = statement logic |
| 4 | var | data | Type-dependent payload, NULL-terminated |
Type 1: one coded statement and zero or more modifiers, each NULL-terminated, concatenated. Total bytes capped at 65535.
Type 2: a single NULL-terminated free-text statement (same form as Section 8).
Type 3: a single NULL-terminated logical expression that combines
prior statements by sequence number. Operators: + for OR, ; for
AND, (...) for precedence, and the conjunctive terms in
Conjunctives and modifiers. At most
one Type 3 entry per Section 11.
Universal statement code grammar
Section titled “Universal statement code grammar”Each code consists of one or more underscore-separated fields:
basic_diagnosis _ certainty _ modifier _ modifier _ ...- Basic diagnosis (5 bytes): one of the acronyms in Statement acronyms.
- Certainty (1-2 bytes):
DEdefinite,PRprobable,PSpossible,UNunknown,CEcannot exclude / rule out,SSstrongly suggestive,COconsider,CWconsistent with. The short formsA,B,C,U,Dare also valid. - Modifiers (up to 3 bytes each): see Conjunctives and modifiers.
Adjacent statements are separated by ;. Conjunctive terms (up to
3 bytes) sit between statements, also flanked by ;. Within a
single statement, the underscore links fields.
Example:
AMI_PR_AC probable acute anterior infarctionLVH_PR_AND_STT_LV probable LVH and ST-T changes compatible with LV strainLVH_PR;AND;STT_LV same content as two linked statementsConjunctives and modifiers
Section titled “Conjunctives and modifiers”Boolean conjunctives: AND, OR, NOT, XOR, EOR. NOT reads
as “in absence of the next statement”.
Arithmetic / relational: ADD, SUB, MPY, DIV, EXP, SQR,
ABS, MAX, MIN, EQU, ILT, IGT, INE, IGE, ILE.
Serial-comparison: SER, DEC, INC, UNC, CHG, DIS, REP,
IMP, WRS.
Other links: RES (results in), SEC (secondary to), ASS
(associated with), EXC (exclude / also consider), WTH (with),
ALT (alternating with).
Age-of-event modifiers: OL old, RE recent, AC acute, SU
subacute, AI age indeterminate, AU age undetermined, EV
evolving, XO probably old, XA probably acute, YO possibly old,
YA possibly acute.
Location: AN, AS, AL, IN, IL, PO, LA, HL, IP,
BA, AF, SE, PL, SN, SP, EX, WI, DI (anterior,
anteroseptal, anterolateral, inferior, inferolateral, posterior,
lateral, high-lateral, inferoposterior, basal, antero-inferior,
septal, posterolateral, subendocardial, subepicardial, extensive,
widespread, diffuse).
Severity: MA major, MO moderate, MI minor; or graded S1..S5.
Time course: SE serial changes, CC continuing changes, OC
occasional, TR transient, UF unifocal, IM intermittent, FR
frequent, MF multifocal, TE temporary, EV evolving, NE new,
MU multiple.
ST-T physiopathology: LV LV strain, MD ischemic damage, PE
pericarditis, EL electrolyte abnormality.
Normality: NO normal, NX may be normal variant, BO borderline,
AB abnormal, BN borderline normal, BA borderline abnormal.
Rhythm location: SI sinus, AT atrial, SV supraventricular,
ND nodal, VE ventricular.
Miscellaneous: IC incomplete, CP complete, TY typical, YT
atypical.
Statement acronyms
Section titled “Statement acronyms”Annex F catalogs interpretation acronyms by clinical category. Each acronym is 1..5 bytes. Representative selections:
- Normal/abnormal:
NORM,NLECG,NLQRS,NLP,NLSTT,WHNOR,POSNL,BOECG,ABECG,POSAB,ABQRS,ABSTT,NFA,NFB,ABFA,ABFB,UFB. - Hypertrophy:
LVH,VCLVH,RVH,VCRVH,BVH,SEHYP,PRANT. - Myocardial infarction:
MI,AMI,ASMI,ALMI,LMI,HLMI,APMI,IMI,ILMI,IPMI,IPLMI,PMI. - Conduction:
BBB,CLBBB,ILBBB,ALBBB,CRBBB,IRBBB,IVCD,IVCD>,IVCD<,WPW,WPWA,WPWB,PREEX,LAFB,LPFB,BIFAS,TRFAS. - Rhythm:
SR,NSR,SARRH,MSAR,STACH,SBRAD,JTACH,SVTAC,JBRAD,SVBRA,WQTAC,NQTAC,ARRHY,IRREG,REGRH,AFIB,AFLT,ATACH,PSVT,PAT,MFAT,RATAC,IDIOR,VFIB,VTACH,RVTAC,SVT,NSVT,TORSA,MTACH,VFLT,ASYST. - AV conduction defects:
1AVB,2AVB,3AVB,I2AVB,A2AVB,AVDIS,WENCK,MOBI2,SAR,SABLK,SPAUS,WANDP,LRR,OCAP. - Ectopic complexes:
PAC/APC,PVC/VPC,JPC,MAPCS,MVPCS,RVPCS,RAPCS,RJPCS,VIC,SVPC,ABER,FUSC,CAPT,VEC,AEC,VPARA,APARA,VBIG,ABIG,SVBIG,BIGU,VTRIG,ATRIG,SVTRI,TRIGU,VQUAG,RECIP. - Block / conduction:
B2T1,B3T1,B4T1,B5T1,VARBL,EXIBL,ENTBL,VABL,BLOCK,C2T1..C5T1,VARCO,SVR,IVR,RVR,WRV,AAVCO,RETCO,ANTCO,ABBCO,CONCO,AVREN,CONRE,RENTR,AECHO,VECHO,FCOUP,VCOUP. - Pacemaker:
PACE,PACEA,PACEP,PACEF,PACED,PACEM,EPAVS,EPVC,EPDM,EPFC,EPFS,EPARV,EPU,EPURV,PAA,PAD,PAVA,PADEM. International ICHD codesPAVVI,PAAAI,PAVAT,PAVDD,PADVI,PADDDuse bytes 3..5 to encode chamber paced / sensed / response. - Axis:
LAD,RAD,AXL,AXR,AXIND,AXSUP,AXPOS,AXVER,AXHOR,TRSLT,TRSRT,CCWRT,CWRT. - ST-T descriptive:
ISC_,INJ_,EPI_,STT_,NST_,STE_,STD_,RST_,TAB_,NT_. Region suffix replaces the trailing_(e.g.ISCAN= ischemic anterior). - Atrial:
LAO/LAE,RAO/RAE,BAO/BAE,IACD,HPVOL,NSPEP,ABPAX,UNPAX. - Pediatric:
PED,RVD,ASD,ECD,EBSTA,TCA,ACA. - Calibration:
HSCAL,HSPRE,HSLIM,DSCAL,DSPRE,DSLIM,NSCAL. - Technical / quality:
ARMRE,LMISP,QCERR,AHERR,MEASE,NOISE,WANDR,FAULT,ARTEF,SIMUL,PINFO,INCAN,NODAT.
For overreading measurement results (Annex F.5), waveform tokens
P, Q, R, S, R2, S2, R3, S3, J, ST, T, T+,
T-, U, QRS, PR, PP, RR, QT, JT, TP and units
MSEC, SEC, MVOLT, MUVLT, DEGR, RATIO, UNIT,
together with LEAD, INN, AXIS, DUR, AMP, compose
expressions like P_AMP_INN_LEAD_V1_EQU_120_MVOLT (“P amplitude in
V1 equals 120 mV”).
Difference encoding
Section titled “Difference encoding”The waveform payload in Sections 5 and 6 can be raw samples, first differences, or second differences. The choice is signaled by byte 5 of each section’s data-part header.
For a signal X(1..N):
diff_used == 0: data[n] = X[n]diff_used == 1: data[1] = X[1] data[n] = X[n] - X[n-1] n >= 2diff_used == 2: data[1] = X[1] data[2] = X[2] data[n] = X[n] - 2*X[n-1] + X[n-2] n >= 3Reconstruction:
diff_used == 1: X[n] = X[n-1] + data[n] n >= 2diff_used == 2: X[n] = 2*X[n-1] - X[n-2] + data[n] n >= 3The first one or two samples are stored as raw values so the recurrence can be seeded. Higher-order differences cluster around zero in well-band-limited ECG segments, which improves Huffman compression.
Sample decimation (bimodal compression)
Section titled “Sample decimation (bimodal compression)”Optional. When bimodal == 1 in Section 6 header, the residual
record is decimated outside QRS-protected areas. The protected areas
(QB..QE per QRS, listed in Section 4) keep the original sample rate
and AVM of the reference beat (Section 5). Outside the protected
areas, the rhythm uses Section 6’s coarser AVM and sample interval.
The maximum sample interval is 8 ms (125 Hz). The non-protected gap between QE(k-1)+1 and QB(k)-1 should be an integer multiple of the decimation factor; writers pick QB and QE with that constraint.
Decimation algorithm: arithmetic average of decimation-factor consecutive samples. A typical 4× decimator stores one value per four input samples:
Z_av(m,1) = (X(m,1) + X(m,2) + X(m,3) + X(m,4)) / 4Z_av(m,2) = (X(m,5) + X(m,6) + X(m,7) + X(m,8)) / 4...Decompression interpolates between consecutive averages. The first two reconstructed samples of an interval take the first average; the last two take the last average; intermediate samples interpolate linearly:
X'(m, a) = Z'_av(m, a)X'(m, a+1) = Z'_av(m, a)X'(m, a+2) = Z'_av(m, a)X'(m, a+3) = Z'_av(m, a) + 1 * (Z'_av(m, a+1) - Z'_av(m, a)) / 4X'(m, a+4) = Z'_av(m, a) + 2 * (Z'_av(m, a+1) - Z'_av(m, a)) / 4X'(m, a+5) = Z'_av(m, a) + 3 * (Z'_av(m, a+1) - Z'_av(m, a)) / 4X'(m, a+6) = Z'_av(m, a+1)...The exact decimation and reconstruction algorithm is implementation- defined; the spec only constrains the RMS and absolute reconstruction errors (see Compression error limits).
After reconstruction, a 3-sample non-recursive moving-average filter smooths the boundaries outside the protected areas. Boundaries at SB(k) and SE(k) are not filtered, so the discontinuities from reference-beat subtraction survive into the reconstructed residual and disappear cleanly when the reference beat is re-added.
Reference-beat subtraction (high compression)
Section titled “Reference-beat subtraction (high compression)”When the Section 3 reference-beat-subtraction flag is set, the encoder:
- Locates one reference beat (type 0) per recorded lead by averaging “normal” complexes from the raw record.
- Synchronizes the reference beat’s fiducial
fcM(stored in Section 4 bytes 3..4) to each type-0 QRS fiducialfc(k)in the raw record. - Subtracts the reference beat from the raw signal at every type-0
complex location. The subtracted span is
SB(k) = fc(k) - PMtoSE(k) = fc(k) + TM, wherePMandTMare the spans fromfcMto the reference beat’s P-onset and T-offset respectively. - Stores the reference beat in Section 5 and the residual in Section 6.
The residual is what remains after subtraction. Outside QRS protected areas it is low-amplitude, low-bandwidth, and compresses well with first/second differences and Huffman coding plus optional decimation.
Decoding reverses the encoded transforms. The exact composition depends on which compression knobs the encoder used; each step is conditional on the corresponding header field:
- Huffman decode of the lead bitstream into integer samples,
reversed only when Section 2 is present or
Section 2.table_count == 19999. See Decoding a Huffman bitstream. - Reverse 1st/2nd differences when Section 5 byte 5 or Section 6
byte 5 is
1or2. See Difference encoding. - Reverse decimation (bimodal case only, Section 6 byte 6 == 1). Interpolates the missing samples in the non-protected ranges. See Sample decimation.
- 3-sample reconstruction filter on the non-protected ranges only. See Reconstruction smoothing filter.
- AVM scaling: multiply the integer samples by their section’s AVM to recover nanovolts. See AVM scaling.
- Reference-beat addition at every
fc(k)whosebeat_type == 0. See Reference-beat addition.
A reader that targets a lighter compression mode runs a subset:
| Mode | Steps |
|---|---|
| Raw integers (no Section 2, no diff, no subtraction, no bimodal) | 5 |
| Plain Huffman over raw samples | 1, 5 |
| Huffman + 1st/2nd diff | 1, 2, 5 |
| Reference-beat subtraction (no bimodal) | 1, 2, 5, 6 |
| High compression (bimodal + subtraction) | 1, 2, 3, 4, 5, 6 |
QRS complexes whose beat_type != 0 (e.g. extrasystoles) skip step
6: their full waveform sits in the residual record. Their protected
areas in Section 4 still bypass steps 3 and 4 so the morphology is
preserved.
Truncation to 5 µV resolution
Section titled “Truncation to 5 µV resolution”Raw 1 µV/LSB data is truncated to 5 µV/LSB by:
X_t(m,n) = (X_r(m,n) + 2) / 5 when X_r(m,n) >= 0X_t(m,n) = (X_r(m,n) - 2) / 5 when X_r(m,n) < 0Integer division truncates toward zero. The same form applies to the reference beat. Rounding constants are negated for negative values.
Encoder low-pass filter (9-sample)
Section titled “Encoder low-pass filter (9-sample)”A simple non-recursive moving-average filter, length 9 samples, applied by the encoder to the residual outside the protected areas:
F(m, n) = (X(m, n - (L-1)/2) + ... + X(m, n) + ... + X(m, n + (L-1)/2) + (L-1)/2) / Lwhere L = 9. Edge samples taper:
F(m, a) = X(m, a)F(m, a+1) = (X(m, a) + X(m, a+1) + X(m, a+2) + 1) / 3F(m, a+2) = (X(m, a) + X(m, a+1) + X(m, a+2) + X(m, a+3) + X(m, a+4) + 2) / 5F(m, b-2) = (X(m, b-4) + X(m, b-3) + X(m, b-2) + X(m, b-1) + X(m, b) + 2) / 5F(m, b-1) = (X(m, b-2) + X(m, b-1) + X(m, b) + 1) / 3F(m, b) = X(m, b)The filter does not cross SB(k) / SE(k) boundaries: the subtraction edges must survive into the residual so they cancel when the reference beat is re-added on decode.
Reconstruction smoothing filter
Section titled “Reconstruction smoothing filter”After Sample decimation has been reversed, the decoder runs a length-3 non-recursive moving- average filter on the non-protected ranges to absorb interpolation steps. Inside protected areas the filter is not applied (the QRS must remain bit-exact under the 15 µV error budget). At SB / SE boundaries the filter does not cross either, so the reference-beat subtraction discontinuities survive into the filtered residual and cancel cleanly when the reference beat is re-added.
F''(m, a) = X''(m, a)F''(m, a+1) = (X''(m, a) + X''(m, a+1) + X''(m, a+2) + 1) / 3...F''(m, n) = (X''(m, n-1) + X''(m, n) + X''(m, n+1) + 1) / 3...F''(m, b-1) = (X''(m, b-2) + X''(m, b-1) + X''(m, b) + 1) / 3F''(m, b) = X''(m, b)Rounding constants are negated for negative values.
The filter runs over four kinds of interval per record (K = number
of QRS complexes, N = last sample index):
| # | Range start a | Range end b | Meaning |
|---|---|---|---|
| 1 | SE(k-1) + 1 | SB(k) - 1 | Between end of reference-beat subtraction for QRS k-1 and start of subtraction for k |
| 2 | SB(k) | QB(k) - 1 | From the start of subtraction for QRS k to its protected-area onset |
| 3 | QE(k) + 1 | SE(k) | From the protected-area end of QRS k to the end of its subtraction zone |
| 4 | SE(K) + 1 | N | Tail of the record after the last QRS |
Use SE(0) = 0 as the sentinel for the leading interval.
AVM scaling
Section titled “AVM scaling”After all integer-domain processing, samples become nanovolts by multiplying by the relevant AVM (Section 5 byte 1..2 for the reference beat, Section 6 byte 1..2 for the rhythm / residual):
X'_r(m, n) = X'(m, n) * Section6.AVM 1 <= n <= N, 1 <= m <= MY'_r(m, p) = Y'(m, p) * Section5.AVM 1 <= p <= P, 1 <= m <= MX' is the integer residual after smoothing; Y' is the integer
reference beat after its own Huffman + difference reconstruction.
Both products are 32-bit signed nanovolts. To get microvolts divide
by 1000; to get millivolts divide by 1000000.
For bimodal records the protected samples inside QRS use the reference-beat AVM (typically 5 µV/LSB) and the non-protected samples use the residual AVM (typically 20 µV/LSB).
Reference-beat addition
Section titled “Reference-beat addition”For each QRS k with beat_type == 0 in Section 4:
- Align
fcM(reference-beat fiducial, Section 4 bytes 3..4) withfc(k)(residual fiducial, Section 4 subtraction-zone bytes 7..10 of block k). - Walk
jfrom0throughSE(k) - SB(k), mapping residual sampleSB(k) + jto reference-beat samplefcM - (fc(k) - SB(k)) + j. - Add the AVM-scaled reference-beat sample to the AVM-scaled residual sample. Both sides must be in the same physical unit (nanovolts) before addition.
X_raw(m, SB(k) + j) = X'_r(m, SB(k) + j) + Y'_r(m, fcM - (fc(k) - SB(k)) + j) for 0 <= j <= SE(k) - SB(k)SB(k) and SE(k) come from Section 4 bytes 3..6 and 11..14 of the
QRS-k block. When beat_type != 0, SB(k) = SE(k) = 0 and the QRS
contributes no addition; the residual already carries the full
sample values for that complex.
Default decompression parameters
Section titled “Default decompression parameters”When a file uses the default high-compression profile, the headers carry the following values. A decoder may use these as fallback expectations:
| Field | Default value |
|---|---|
| Section 3 byte 2 bit 0 (reference-beat subtraction) | 1 (on) |
| Section 5 bytes 1..2 (reference-beat AVM) | 5000 nV (= 5 µV) |
| Section 5 bytes 3..4 (reference-beat sample interval) | 2000 µs (= 500 Hz) |
| Section 5 byte 5 (reference-beat diff encoding) | 2 (second diff) |
| Section 6 bytes 1..2 (residual AVM) | 20000 nV (= 20 µV) |
| Section 6 bytes 3..4 (residual sample interval outside QRS) | 8000 µs (= 125 Hz) |
| Section 6 byte 5 (residual diff encoding) | 2 (second diff) |
| Section 6 byte 6 (bimodal) | 1 (on) |
| Huffman tables | Default table (Section 2 table_count = 19999) |
| Sample interpolation target | 2 ms inside the non-protected ranges |
Files conforming to these defaults can be decoded without consulting custom Huffman tables and with hard-coded AVM and sample-interval constants. Non-default writers override any subset of these values in their section headers; the decoder follows the header in every case.
Compression error limits
Section titled “Compression error limits”A conforming high-compression encoder must meet these limits on the SCP-ECG test set (Annex C.5):
| Quantity | Limit |
|---|---|
| Digitization | >= 500 samples/s; <= 5 µV/LSB |
| Reference beat resolution | >= 500 samples/s; <= 5 µV/LSB |
| Residual record truncation error | <= ±15 µV |
| Residual record sample interval | <= 8 ms |
| Reconstruction RMS error | <= 10 µV |
| Absolute error in a single sample outside QRS | <= 100 µV |
| Absolute error in a single sample inside QRS | <= 15 µV |
For pure redundancy reduction (no reference-beat subtraction, no decimation), the reconstruction error must be zero relative to a 500 Hz / 5 µV reference, modulo the initial quantization.
All leads must be recorded simultaneously when reference-beat subtraction is used for compression.
Multi-lingual text encoding
Section titled “Multi-lingual text encoding”Text fields follow a restricted ISO/IEC 2022 profile. Latin-1 (ISO/IEC 8859-1) is the default character set and requires no escape sequences.
- Designate ASCII (the left half of 8859-1) into G0 and invoke into GL.
- Designate Latin-1 (the right half of 8859-1) into G1 and invoke into GR.
- Designate ISO/IEC 646 control set into C0.
- No designation to C1; only the C0 format effectors
BS(0x08),HT(0x09),LF(0x0A),VT(0x0B),FF(0x0C),CR(0x0D), andESC(0x1B) are allowed. NULL(0x00) is reserved for string termination.NEWLINEisCR + LF.
To switch to another character set within a string, the writer emits a designation escape sequence:
| Sequence | Effect |
|---|---|
ESC 02/08 F | Designate 94-character set F into G0 |
ESC 02/04 02/08 F | Designate 94N-character multi-byte set into G0 |
ESC 02/04 F | Designate special 94N set into G0 (F = 04/01..04/03) |
ESC 02/09 F | Designate 94-character set into G1 |
ESC 02/13 F | Designate 96-character set into G1 |
ESC 02/04 02/09 F | Designate 94N-character multi-byte set into G1 |
Each text field implicitly starts in the default state. A new field or the trailing NULL resets to the default.
Common final-character codes:
| F | Set type | Description |
|---|---|---|
| 04/02 | 94 | 7-bit ASCII graphics (ANSI X3.4-1968) |
| 04/01 | 96 | ISO/IEC 8859-1 Latin-1 right half |
| 04/02 | 96 | ISO/IEC 8859-2 Latin-2 right half |
| 04/04 | 96 | ISO/IEC 8859-4 Latin-4 right half |
| 04/12 | 96 | ISO/IEC 8859-5 Cyrillic right half |
| 04/07 | 96 | ISO/IEC 8859-6 Arabic right half |
| 04/06 | 96 | ISO/IEC 8859-7 Greek right half |
| 04/08 | 96 | ISO/IEC 8859-8 Hebrew right half |
| 04/01 | 94² | GB2312-1980 (Simplified Chinese) |
| 04/02 | 94² | JIS X 0208-1983 (Japanese) |
| 04/03 | 94² | KS C5601-1987 (Korean) |
| 04/09 | 94 | JIS X 0201-1976 right half (Japanese single-byte) |
| 04/10 | 94 | JIS X 0201-1976 left half |
Final characters in 03/00..03/15 (private encoding) are forbidden. Intermediate characters 02/02, 02/03, 02/05, 02/06, 02/07, 02/0A, 02/0B, 02/0C, 02/0E, 02/0F are not permitted.
When an importer cannot render a foreign character set, it should
fall back to backslash escapes: \ becomes \\, an ESC introducing
an unsupported sequence becomes \033, and 8-bit GR bytes become
\nnn (3-digit octal) on ASCII-only displays.
CRC-CCITT
Section titled “CRC-CCITT”SCP-ECG uses CRC-CCITT (x^16 + x^12 + x^5 + 1). The CRC register
is preset to 0xFFFF for each calculation.
Two CRC scopes are computed per record:
- Record CRC at bytes 0..1 covers bytes 2..record_size-1.
- Section CRC at the start of each section’s 16-byte ID header covers the rest of the section (bytes 2..section_length-1 of the section).
Byte-at-a-time algorithm (transcribed from Annex E.5.5):
A = current input byteB = scratch byteCRCHI = high byte of the 16-bit CRCCRCLO = low byte of the 16-bit CRC
for each byte A in the CRC domain: A = A XOR CRCHI CRCHI = A A = A >> 4 // logical shift, zero-fill A = A XOR CRCHI // A now has bits IJKLMNOP after the prior XOR CRCHI = CRCLO // swap halves CRCLO = A rotate A left 4 bits // bits MNOPIJKL B = A // save rotate A left 1 bit // bits NOPIJKLM A = A AND 0x1F // mask to 000IJLLM CRCHI = A XOR CRCHI // CRCHI partial A = B AND 0xF0 // MNOP0000 CRCHI = A XOR CRCHI // CRCHI complete rotate B left 1 bit // NOP0000M B = B AND 0xE0 // NOP00000 CRCLO = B XOR CRCLO // CRCLO completeAppending CRCHI then CRCLO at the end of the domain and feeding the extended stream back through the same algorithm yields zero on a correctly received block.
Validation
Section titled “Validation”A conforming reader must reject inputs that violate any of the following:
- Record is shorter than 6 bytes.
- Record-length field is shorter than
6 + 16 + 10(the minimum Section 0). - Record-length field does not match the actual byte count of the file.
- Record CRC does not validate over bytes 2..record_size-1.
- Section 0 does not begin at byte 7.
- Section 0 ID header bytes 11..16 are not the ASCII bytes
"SCPECG". - Any section’s length is not even.
- Any section’s length is less than 16 (the ID header alone).
- Any section’s CRC does not validate over its bytes 2..length-1.
- Pointer entries for Sections 0..11 are not all present in Section 0.
- Section 0’s own pointer index is not 7.
- A non-zero pointer index plus length extends beyond the record.
- A section appears more than once.
- Section 1 does not start with the four mandatory tags (2, 14, 25, 26) at all (the spec marks these mandatory; readers may treat them as soft requirements).
- Section 1 is not terminated by tag 255 with length 0.
- A Section 1 field declares a length that overflows the section.
- Section 3 is missing.
- Section 3 declares zero leads.
- A lead descriptor’s
end_sample < start_sample. - A lead descriptor’s lead ID is in the reserved range 185..199.
- A lead in Section 5 or 6 has a length-table entry whose value overflows the section’s data part.
- Section 5 is referenced (via flag bit 0 of Section 3) but absent.
- Section 5 is present and
avm <= 0orsample_time <= 0ordiff_used > 2. - Section 6 is missing.
- Section 6’s
diff_used > 2orbimodal > 1. - Section 6 declares
bimodal = 1but Section 4 is absent. - Section 2 is required (per the Huffman-encoded lead payloads in Sections 5/6) but absent.
- A Huffman stream consumes more bits than are present.
- A Huffman code structure has
prefix_bits > total_bits. - A Huffman switch (
mode = 0) refers to a non-existent table number. - A Section 4 protected-area block has
QE < QB. - A Section 7 measurement block reports an axis outside -360..360 unless it equals the literal 999 “undefined” code.
- A Section 8/11 timestamp has an out-of-range field (month not 1..12, etc.).
- A Section 11 statement of type 3 references a sequence number that does not exist in the same Section 11.
- A Section 10 record’s declared length does not match its byte count.
- A measurement value equals 29999, 29998, or 19999 but is then used in arithmetic instead of being treated as a sentinel.
The validation order is at the reader’s discretion. Structural checks (record length, CRC, Section 0 presence, section-length bounds) should run before per-section payload checks.
Optional messaging and transport layers
Section titled “Optional messaging and transport layers”Annexes D and E of EN 1064 specify an informative pair of layers for exchanging SCP-ECG records over RS-232 between a cart and a host. They are not part of the file format and a writer/reader of SCP-ECG files does not need to implement them.
- Annex D (Query messaging) defines 256-byte ASCII-tagged
messages (types
I,R,S,A,D) for identification, request, status, advisory, and done. Two devices first exchangeImessages, then alternate between master and slave withRrequests for ECG transfers, patient lists, and ECG lists. Status flagsG(go) andE(error) carry error codes 0..255, with manufacturer codes at 128..255. - Annex E (Low-level transport) is a modified XMODEM with 256- byte blocks, CRC-CCITT framing, ENQ/ACK/NAK/SYN/TTD/EOT control bytes, a 2.5 s transmit timeout, and 3.5 s receive timeout. The CRC algorithm in CRC-CCITT is the same as used here.
Test set for conformance
Section titled “Test set for conformance”Annex C.5 lists a reference test set of 10-second 12-lead ECGs
(PD2-*, PD3-*, PWE-*, P120-N00), digitized at 500 samples/s
and 5 µV/LSB. An encoder claiming high-compression compliance must
meet the Compression error limits on
this set. Decompressed records compare against the originals with
documented absolute and RMS error caps per case.
- The 6-byte record header sits before Section 0 and is not part of any section. Pointers in Section 0 are computed from byte 1 of the record, i.e. byte 1 of the record CRC.
- Section CRCs are independent of the record CRC. A reader can validate Section 0 first, then jump to any referenced section and check its CRC before parsing the payload.
- Sections may appear in any order in the file body. Only the
pointer ordering is fixed (numerical by section ID). To enumerate
the sections present, walk Section 0’s pointer fields and ignore
any entry whose
length == 0andindex == 0. - Samples are 1-based throughout; sample index 0 is reserved as “not used”.
- The
AVM(amplitude value multiplier) is in nanovolts; the on-the-wire integer multiplied by AVM gives the value in nanovolts. Divide by 1000 for µV, by 1000000 for mV. - Section 5/6 lead order matches Section 3 lead order. Each lead’s encoded byte count is in the length table at the start of the section’s data part; the encoded bitstreams (or raw sample arrays) follow concatenated.
record_durationis implied: it is the total number of samples in the rhythm record (maxend_samplefrom Section 3) divided by1000000 / sample_timeHz from Section 6.- The numeric “tag 14 byte 7 manufacturer code” was a fixed enumeration in legacy files; recent files set this byte to 255 and use the trailing manufacturer trade-name string instead. Code 100 meant “other”, codes 1..21 were assigned to specific vendors. Use the string for new writers.
- Bit ordering inside the Quality Code field of Section 10 is unusual: bit 0 is the LSB of byte 54 (the first byte of the field), bit 15 the MSB of byte 55. AC noise is in the lowest bit pair.
- The mandatory four Section 1 tags (2, 14, 25, 26) are not gated by the protocol parser. Readers that require those fields should check them explicitly. Tag 14 also carries the conformance-category byte (byte 16), the protocol version (byte 15), and language support (byte 17), which are practical hard requirements for cross-vendor decoding.