Skip to content

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):

#RequiredContent
0requiredPointers to every section present in the record
1requiredPatient demographic data + ECG acquisition data (tag/length/value layout)
2dependentHuffman tables (required if Sections 5/6 use Huffman encoding)
3requiredECG lead definition (lead identifiers, sample ranges, simultaneity flags)
4optionalQRS locations, reference-beat subtraction zones, protected areas
5optionalEncoded reference beat type 0
6requiredRhythm data (full ECG or residual signal after reference-beat subtraction)
7optionalGlobal measurements + pacemaker spike data
8optionalFull-text interpretive statements
9optionalManufacturer-specific interpretive statements / overreading trail
10optionalPer-lead measurement block
11optionalUniversal 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.

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:

CategoryRequired sectionsCompatibility bits (tag 14 byte 16, MSB first)Content
I0, 1, [2], 3, 6, (7), (8), (10)1101 0000Demographics + rhythm data (uncompressed or with lossless compression)
II0, 1, [2], 3, 4, 5, 6, (7), (8), (10)1110 0000Demographics + 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.

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).

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 0x00 padding 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.
OffSizeFieldContent
02crcCRC-CCITT over bytes 2..end-of-record (see CRC-CCITT)
24record_sizeUnsigned 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 domain

Every section begins with the same 16-byte layout. The data part length equals section_length - 16.

OffSizeFieldContent
02crcCRC-CCITT over the rest of the section (bytes 2..section_length-1)
22section_idSection ID number (0..11 defined; 128..1023 manufacturer-specific)
44section_lengthTotal section length in bytes, including these 16 header bytes; always even
81section_versionSection version (= protocol version for sections 0..11; manufacturer-defined for 128..1023)
91protocol_versionProtocol version, mirror of Section 1 tag 14 byte 15
106reservedZero, 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.

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:

OffSizeFieldContent
02section_idSection ID (0..1023)
24lengthSection length in bytes, including its 16-byte ID header; 0 if absent
64index1-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 = 0 and index = 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:

TagFieldNotes
2Patient IDPrimary key. First 16 characters must be unique.
14Acquiring Device ID
25Date of Acquisition
26Time of AcquisitionLocal wall-clock time at acquisition

Strongly recommended:

TagField
0Patient Last Name
1Patient First Name
5Patient Date of Birth
8Patient Sex
15Analyzing Device ID
34Date Time Zone
TagLengthFieldEncoding
0var (text)Last nameUsed for the entire name if no first name is split out
1var (text)First name
2var (text)Patient ID
3var (text)Second last nameLocale-specific (US: family-member prefix; FR: maiden name; ES/PT/LATAM: second surname)
43AgeBytes 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.
54Date of birthBytes 1-2: year (full 4-digit); byte 3: month 1..12; byte 4: day 1..31. All-zero = not specified.
63HeightBytes 1-2: value; byte 3: unit (0=unspecified, 1=cm, 2=in, 3=mm)
73WeightBytes 1-2: value; byte 3: unit (0=unspecified, 1=kg, 2=g, 3=lb, 4=oz)
81Sex0=not known, 1=male, 2=female, 9=unspecified
91Race0=unspecified, 1=Caucasian, 2=Black, 3=Oriental, 4..9 reserved, 10..255 manufacturer
10varDrugsByte 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.
112Systolic blood pressuremmHg
122Diastolic blood pressuremmHg
13var (text)Diagnosis or referral indicationMultiple instances allowed
14varAcquiring device IDSee Device ID structure
15varAnalyzing device IDSame layout as tag 14
16var (text)Acquiring institution description
17var (text)Analyzing institution description
18var (text)Acquiring department description
19var (text)Analyzing department description
20var (text)Referring physician
21var (text)Latest confirming physician
22var (text)Technician description
23var (text)Room description
241Stat code0=routine; higher = more urgent (1..10 recommended)
254Date of acquisitionSame layout as tag 5
263Time of acquisitionByte 1: hours 0..23; byte 2: minutes 0..59; byte 3: seconds 0..59. Local time in the acquisition timezone.
272Baseline filter cutoff-3 dB high-pass cutoff in 1/100 Hz
282Low-pass filter cutoff-3 dB cutoff in Hz
291Filter bit mapBit 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.
30var (text)Free textMultiple instances allowed
31var (text)ECG sequence number
32varMedical history codesByte 1: code-table indicator (0 = standard table below); remaining bytes: one code per byte. Multiple instances allowed.
332Electrode configuration codeByte 1: 12-lead placement system; byte 2: XYZ-lead system (see Electrode configuration)
34varDate time zoneSee Time zone
35var (text)Free-text medical historyMultiple instances allowed
2550Section terminatorNo value

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.

TagFieldMultipleRecommended length
0Last nameno40
1First nameno40
2Patient IDno40
3Second last nameno40
10Drugsyes40
13Diagnosis / referralyes80
14Acquiring device IDno40
15Analyzing device IDno40
16Acquiring institutionno40
17Analyzing institutionno40
18Acquiring departmentno40
19Analyzing departmentno40
20Referring physicianno60
21Latest confirming physicianno60
22Technician descriptionno40
23Room descriptionno40
30Free textyes80
31ECG sequence numberno12
35Free-text medical historyyes80

Used by tags 14 (acquiring device) and 15 (analyzing device).

OffSizeField
02Institution number
22Department number
42Device ID
61Device type: 0=cart, 1=system/host
71Set to 255. Legacy use: numeric manufacturer code (0..21 assigned, 100 = other). Use the trailing string instead.
86Model description as up to 5 ASCII bytes + NULL terminator
141SCP-ECG protocol revision (e.g. 1.0 -> 10, 2.0 -> 20; backward-compatible revisions where possible)
151SCP-ECG compatibility level (per Annex B; upper 4 bits encode the conformance category)
161Language support code, bit-mapped (see Language support)
171Device 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
181AC mains frequency: 0=unspecified, 1=50 Hz, 2=60 Hz
1917Reserved (bytes 20..35 of the tag value); zero-fill
361Length of the analysing-program revision string (>= 1)
37varNULL-terminated analysing-program revision number
varNULL-terminated serial number of the acquisition device
varNULL-terminated acquisition-device system software identifier
varNULL-terminated SCP implementation software identifier (<= 24 chars + NULL)
varNULL-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.

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 xx008-bit ASCII only
xxxx xx01ISO/IEC 8859-1 Latin-1
0000 0011ISO/IEC 8859-2 Latin-2 (Central/Eastern European)
0000 1011ISO/IEC 8859-4 Latin-4 (Baltic)
0001 0011ISO/IEC 8859-5 Cyrillic
0001 1011ISO/IEC 8859-6 Arabic
0010 0011ISO/IEC 8859-7 Greek
0010 1011ISO/IEC 8859-8 Hebrew
0011 0011ISO/IEC 8859-11 Thai
0011 1011ISO/IEC 8859-15 Latin-9 (“Latin-0”)
0000 1111ISO/IEC 10646 (Unicode)
0001 1111JIS X 0201-1976 (Japanese single-byte)
0010 1111JIS X 0208-1997 (Japanese multi-byte)
0011 1111JIS X 0212-1990 (Japanese supplementary)
0100 1111GB 2312-80 (Simplified Chinese)
0101 1111KS C5601-1987 (Korean)
1111 1111Manufacturer-specific

Other bit combinations are reserved.

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).

ClassNameExamples (byte 3)
0Unspecifiedbyte 3 must also be 0; text in bytes 4.. is the only description
1Digitalis1=Digoxin/Lanoxin, 2=Digitoxin
2Antiarrhythmic1=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
3Diuretics1=Thiazide, 2=Furosemide, 3=Potassium chloride
4Antihypertensive1=Clonidine, 2=Prazosin, 3=Hydralazine
5Antianginal1=Isosorbide, 2=Calcium blockers, 3=Nitrates
6Antithrombotic1=Aspirin, 2=Coumadin, 3=Heparin, 4=Warfarin, 5=Streptokinase, 6=t-PA
7Beta blockers1=Propranolol, 2=Corgard, 3=Atenolol, 4=Metoprolol, 5=Pindolol, 6=Acebutolol
8Psychotropic1=Tricyclic antidepressant, 2=Phenothiazide, 3=Barbiturate
9Calcium blockers1=Nifedipine, 2=Verapamil
10Antihypotensive1=Asthmatic drug, 2=Aminophyline, 3=Isuprel
11Anticholesterol1=Colestid, 2=Lovastatin, 3=Simvastatin, 4=Fibrates
12ACE inhibitors1=Captopril
13..99Reserved
100Not taking drugsbyte 3 = 0
101Drugs, unknownbyte 3 = 0
102Other medicationbyte 3 = 0
103..255Manufacturer-specific

Sub-code 9 is reserved for “other” within each class.

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:

CodeMeaning
0Not specified
1Apparently healthy
10Acute myocardial infarction
11Myocardial infarction
12Previous myocardial infarction
15Ischemic heart disease
18Peripheral vascular disease
20Cyanotic congenital heart disease
21Acyanotic congenital heart disease
22Valvular heart disease
25Hypertension
27Cerebrovascular accident
30Cardiomyopathy
35Pericarditis
36Myocarditis
40Post-operative cardiac surgery
42Implanted cardiac pacemaker
45Pulmonary embolism
50Respiratory disease
55Endocrine disease
60Neurological disease
65Alimentary disease
70Renal disease
80Pre-operative general surgery
81Post-operative general surgery
90General medical
100Other
128..255Manufacturer-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.

Tag 33 byte 1: 12-lead electrode placement system.

CodeSystem
0Unspecified
1Standard 12-lead: RA, RL, LA, LL on limb extremities; V1..V6 at standard chest sites
2Mason-Likar: RA/RL/LA/LL on torso; V1..V6 standard chest, individually placed
3Mason-Likar limb; V1..V6 in a single chest-pad assembly
4All electrodes (limb + V1..V6) in one chest pad (e.g. Omnitrode)
512-lead derived from Frank XYZ leads
612-lead derived from non-standard leads
7..255Reserved

Tag 33 byte 2: XYZ-lead electrode placement system.

CodeSystem
0Unspecified
1Frank lead system (Frank 1956)
2McFee-Parungao lead system
3Cube lead system (Grishman et al. 1951)
4Bipolar uncorrected XYZ
5Pseudo-orthogonal XYZ (Holter-style)
6XYZ derived from 12-lead
7..255Reserved

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.

OffSizeFieldContent
02offsetSigned minutes east of UTC, range -780..+780 (i.e. ±13 h); 0x7FFF = unset
22indexUnsigned manufacturer-defined lookup index when offset == 0x7FFF; 0 = unset; 1..1000 reserved; 1001..32766 manufacturer; 32767 reserved
4vardescriptionNULL-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.

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.

OffSizeFieldContent
02table_countNumber of Huffman tables; value 19999 means “use default table” (see Default Huffman table)
22structure_count[0]Number of code structures in table 1
4N*9structures[0]structure_count[0] × 9-byte code structure
2structure_count[k]Number of code structures in table k+1
N*9structures[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:

OffSizeFieldContent
01prefix_bitsNumber of bits in the prefix
11total_bitsNumber of bits in the entire code
21mode0 = switch to another Huffman table (the table number is in base_value); 1 = Huffman or initial encoding (see below)
32base_valueDecoded sample value in AVM units (signed 16-bit). When mode == 0, this holds the target table number.
54base_codeThe 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.

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 bitsPrefix bitsModeBase valuePrefix (binary)Stored base_code (decimal)
1111000
2331+11001
3331-11015
4441+211003
5441-2110111
6551+3111007
7551-31110123
8661+411110015
9661-411110147
10771+5111110031
11771-5111110195
12881+61111110063
13881-611111101191
14991+7111111100127
15991-7111111101383
1610101+81111111100255
1710101-81111111101767
18181018-bit initial value1111111110511
192610116-bit initial value11111111111023

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.

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:

  1. Read the prefix. Read bits one at a time and compare the accumulated bit pattern against every structure in the active table whose prefix_bits equals the number of bits read. If nothing matches, read one more bit and repeat. Continue until exactly one structure matches.
  2. Branch on the matched structure’s mode:
    • mode == 0 (table switch). The active table changes to the table number stored in base_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). Emit base_value as the decoded sample. Advance the cursor by prefix_bits (already consumed) and return.
    • mode == 1, prefix_bits < total_bits (initial encoding). Read total_bits - prefix_bits more 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_value is unused for initial-encoding structures.
  3. 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.

Required. Lists the leads in the record, the sample range covered by each lead, and whether reference-beat subtraction is used.

OffSizeFieldContent
01lead_countNumber of leads
11flagsBit 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
29*Nleadslead_count × 9-byte lead descriptor

Each lead descriptor:

OffSizeFieldContent
04start_sampleUnsigned 1-based starting sample number
44end_sampleUnsigned 1-based ending sample number (inclusive)
81lead_idLead 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).

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:

CodeLeadCodeLeadCodeLead
0Unspecified61III66V8
1I62aVR67V9
2II63aVL68V8R
3V164aVF69V9R
4V265aVR (inverted, aVRneg)147RL (right leg)
5V321LA (left arm)
6V422RA (right arm)
7V523LL (left leg)
8V6

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.

OffSizeFieldContent
02ref_beat_lengthLength of reference beat type 0 in milliseconds
22fcM1-based sample number of the fiducial (QRS trigger) within reference beat 0
42qrs_countTotal 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)”
OffSizeFieldContent
02beat_typeReference-beat type for this QRS. Type 0 is the “normal” beat used for subtraction.
24SB1-based sample on residual data where subtraction of beat 0 starts. 0 if beat_type != 0.
64fc1-based sample on residual data of this QRS’s fiducial point
104SE1-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.

OffSizeFieldContent
04QB1-based sample on residual data: start of this QRS’s protected area
44QE1-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.

OffSizeFieldContent
02avmAmplitude value multiplier in nanovolts (e.g. 1250 -> 1.250 µV per LSB)
22sample_timeSample time interval in microseconds (e.g. 2000 -> 500 Hz)
41diff_used0 = raw samples; 1 = first differences; 2 = second differences (see Difference encoding)
51reserved0

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.

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.

Required. Carries either the full ECG rhythm or, if reference-beat subtraction is on, the residual signal after subtraction.

OffSizeFieldContent
02avmAmplitude value multiplier in nanovolts
22sample_timeSample time interval in microseconds
41diff_used0 = raw; 1 = first differences; 2 = second differences
51bimodal0 = 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.

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.

OffSizeFieldContent
01count_markerIf 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.
11pacemaker_countNumber of pacemaker spikes whose timing/amplitude is reported
22rr_averageAverage RR interval over all QRS, in ms
42pp_averageAverage PP interval over all QRS, in ms

Each measurement block is 16 bytes:

OffSizeFieldContent
02p_onsetms from beat / record start
22p_offsetms
42qrs_onsetms
62qrs_offsetms
82t_offsetms
102p_axisAngular degrees in frontal plane; 999 = undefined
122qrs_axisAngular degrees; 999 = undefined
142t_axisAngular 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).

OffSizeFieldContent
02time_msUnsigned ms from start of rhythm record
22amplitude_uvSigned µ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)”
OffSizeFieldContent
01spike_type0 = unknown; 1 = triggers neither P nor QRS; 2 = triggers QRS; 3 = triggers P wave; 4..127 reserved; 128..254 manufacturer; 255 = no analysis
11source0 = unknown; 1 = internal; 2 = external; 3..255 reserved
22triggered_qrs1-based index into the QRS list; 0 = no link
42pulse_width_usMicroseconds; 0 = unknown
OffSizeFieldContent
02qrs_countNumber of QRS complexes
21type_0Reference-beat type of the first QRS
31type_1Reference-beat type of the second QRS
etc.
OffSizeFieldContent
02vent_rate_bpmVentricular rate, beats per minute
22atrial_rate_bpmAtrial rate, beats per minute
42qtc_msCorrected QT in ms
61qtc_formula0=unknown, 1=Bazett, 2=Hodges, 3..127 reserved, 128..254 manufacturer, 255 = not available
72tagged_bytesBytes of tagged fields that follow (0 if none)
9vartagged_fieldsTag/length/value triples ending with tag 255 length 0

Defined tags in the tagged-field area:

TagLengthField
05QTend 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.
15QTpeak all-lead dispersion. Same layout as tag 0, measured to T-wave peak instead of T-wave offset.
25QTend precordial dispersion. Same layout, precordial leads only.
35QTpeak precordial dispersion. Same layout, precordial leads only.
4..2540Reserved
2550Terminator

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.

OffSizeFieldContent
01confirmation0 = original (not overread); 1 = confirmed; 2 = overread, not confirmed
12year4-digit year
31month1..12
41day1..31
51hour0..23
61minute0..59
71second0..59
81statement_countNumber of statement entries

Each statement entry:

OffSizeFieldContent
01sequence_number1-based statement index
12statement_lengthBytes in text including the NULL terminator
3vartextNULL-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.

Optional. One record per recorded lead with up to 50 standard measurements plus a manufacturer area.

OffSizeFieldContent
02lead_countNumber of leads with measurement records
22manufacturerManufacturer-specific

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.

OffSizeField
02lead_id (matches Section 3 lead codes)
22Record length in bytes, excluding bytes 0..3
42P_duration_ms (total P, including P+ and P-)
62PR_interval_ms
82QRS_duration_ms
102QT_interval_ms
122Q_duration_ms
142R_duration_ms
162S_duration_ms
182R'_duration_ms
202S'_duration_ms
222Q_amplitude_uV (signed; Q is negative)
242R_amplitude_uV (signed)
262S_amplitude_uV (signed; S is negative)
282R'_amplitude_uV
302S'_amplitude_uV
322J_point_amplitude_uV (= amplitude at end of QRS)
342P+_amplitude_uV
362P-_amplitude_uV (signed; negative)
382T+_amplitude_uV
402T-_amplitude_uV (signed; negative)
422ST_slope_uV_per_s
442P_morphology (see below)
462T_morphology (see below)
482iso_onset_ms (segment I: global QRS onset to lead’s QRS onset)
502iso_offset_ms (segment K: lead’s QRS offset to global QRS offset)
522intrinsicoid_deflection_ms
542quality_code (see below)
562ST_at_J_plus_20ms_uV
582ST_at_J_plus_60ms_uV
602ST_at_J_plus_80ms_uV
622ST_at_J_plus_RR_div16_uV
642ST_at_J_plus_RR_div8_uV
6638Reserved (zeros if a manufacturer block follows)
104varManufacturer measurement block

Special amplitude codes:

ValueMeaning
29999Measurement not computed
29998Lead rejected by the measurement program
19999Wave not present (e.g. Q wave absent, P wave during atrial fibrillation)

P / T morphology codes:

CodeShape
0Unknown
1Positive
2Negative
3Positive / negative
4Negative / positive
5Positive / negative / positive
6Negative / positive / negative
7Notched M-shaped
8Notched 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:

BitsCategoryLevels
0..1AC mains noise0 = none; 1 = moderate; 2 = severe; 3 = unknown
2..3Overrangesame
4..5Baseline wandersame
6..7Tremor / muscle artifactsame
8..9Spikes / sudden jumpssame
10..11Electrode loose or offsame
12..13Pacemakersame
14..15Interchanged leadsame

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.

Same layout as Section 8: confirmation byte + 7-byte timestamp + statement count.

OffSizeFieldContent
01sequence_number1-based, used by Type 3 logical operands
12statement_lengthBytes from type through and including the terminating NULL
31type1 = universal statement code; 2 = full text; 3 = statement logic
4vardataType-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.

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): DE definite, PR probable, PS possible, UN unknown, CE cannot exclude / rule out, SS strongly suggestive, CO consider, CW consistent with. The short forms A, B, C, U, D are 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 infarction
LVH_PR_AND_STT_LV probable LVH and ST-T changes compatible with LV strain
LVH_PR;AND;STT_LV same content as two linked statements

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.

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 codes PAVVI, PAAAI, PAVAT, PAVDD, PADVI, PADDD use 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”).

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 >= 2
diff_used == 2: data[1] = X[1]
data[2] = X[2]
data[n] = X[n] - 2*X[n-1] + X[n-2] n >= 3

Reconstruction:

diff_used == 1: X[n] = X[n-1] + data[n] n >= 2
diff_used == 2: X[n] = 2*X[n-1] - X[n-2] + data[n] n >= 3

The 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.

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)) / 4
Z_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)) / 4
X'(m, a+4) = Z'_av(m, a) + 2 * (Z'_av(m, a+1) - Z'_av(m, a)) / 4
X'(m, a+5) = Z'_av(m, a) + 3 * (Z'_av(m, a+1) - Z'_av(m, a)) / 4
X'(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:

  1. Locates one reference beat (type 0) per recorded lead by averaging “normal” complexes from the raw record.
  2. Synchronizes the reference beat’s fiducial fcM (stored in Section 4 bytes 3..4) to each type-0 QRS fiducial fc(k) in the raw record.
  3. Subtracts the reference beat from the raw signal at every type-0 complex location. The subtracted span is SB(k) = fc(k) - PM to SE(k) = fc(k) + TM, where PM and TM are the spans from fcM to the reference beat’s P-onset and T-offset respectively.
  4. 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:

  1. 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.
  2. Reverse 1st/2nd differences when Section 5 byte 5 or Section 6 byte 5 is 1 or 2. See Difference encoding.
  3. Reverse decimation (bimodal case only, Section 6 byte 6 == 1). Interpolates the missing samples in the non-protected ranges. See Sample decimation.
  4. 3-sample reconstruction filter on the non-protected ranges only. See Reconstruction smoothing filter.
  5. AVM scaling: multiply the integer samples by their section’s AVM to recover nanovolts. See AVM scaling.
  6. Reference-beat addition at every fc(k) whose beat_type == 0. See Reference-beat addition.

A reader that targets a lighter compression mode runs a subset:

ModeSteps
Raw integers (no Section 2, no diff, no subtraction, no bimodal)5
Plain Huffman over raw samples1, 5
Huffman + 1st/2nd diff1, 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.

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) >= 0
X_t(m,n) = (X_r(m,n) - 2) / 5 when X_r(m,n) < 0

Integer division truncates toward zero. The same form applies to the reference beat. Rounding constants are negated for negative values.

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) / L

where 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) / 3
F(m, a+2) = (X(m, a) + X(m, a+1) + X(m, a+2) + X(m, a+3) + X(m, a+4) + 2) / 5
F(m, b-2) = (X(m, b-4) + X(m, b-3) + X(m, b-2) + X(m, b-1) + X(m, b) + 2) / 5
F(m, b-1) = (X(m, b-2) + X(m, b-1) + X(m, b) + 1) / 3
F(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.

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) / 3
F''(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 aRange end bMeaning
1SE(k-1) + 1SB(k) - 1Between end of reference-beat subtraction for QRS k-1 and start of subtraction for k
2SB(k)QB(k) - 1From the start of subtraction for QRS k to its protected-area onset
3QE(k) + 1SE(k)From the protected-area end of QRS k to the end of its subtraction zone
4SE(K) + 1NTail of the record after the last QRS

Use SE(0) = 0 as the sentinel for the leading interval.

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 <= M
Y'_r(m, p) = Y'(m, p) * Section5.AVM 1 <= p <= P, 1 <= m <= M

X' 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).

For each QRS k with beat_type == 0 in Section 4:

  1. Align fcM (reference-beat fiducial, Section 4 bytes 3..4) with fc(k) (residual fiducial, Section 4 subtraction-zone bytes 7..10 of block k).
  2. Walk j from 0 through SE(k) - SB(k), mapping residual sample SB(k) + j to reference-beat sample fcM - (fc(k) - SB(k)) + j.
  3. 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.

When a file uses the default high-compression profile, the headers carry the following values. A decoder may use these as fallback expectations:

FieldDefault 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 tablesDefault table (Section 2 table_count = 19999)
Sample interpolation target2 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.

A conforming high-compression encoder must meet these limits on the SCP-ECG test set (Annex C.5):

QuantityLimit
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.

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), and ESC (0x1B) are allowed.
  • NULL (0x00) is reserved for string termination.
  • NEWLINE is CR + LF.

To switch to another character set within a string, the writer emits a designation escape sequence:

SequenceEffect
ESC 02/08 FDesignate 94-character set F into G0
ESC 02/04 02/08 FDesignate 94N-character multi-byte set into G0
ESC 02/04 FDesignate special 94N set into G0 (F = 04/01..04/03)
ESC 02/09 FDesignate 94-character set into G1
ESC 02/13 FDesignate 96-character set into G1
ESC 02/04 02/09 FDesignate 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:

FSet typeDescription
04/02947-bit ASCII graphics (ANSI X3.4-1968)
04/0196ISO/IEC 8859-1 Latin-1 right half
04/0296ISO/IEC 8859-2 Latin-2 right half
04/0496ISO/IEC 8859-4 Latin-4 right half
04/1296ISO/IEC 8859-5 Cyrillic right half
04/0796ISO/IEC 8859-6 Arabic right half
04/0696ISO/IEC 8859-7 Greek right half
04/0896ISO/IEC 8859-8 Hebrew right half
04/0194²GB2312-1980 (Simplified Chinese)
04/0294²JIS X 0208-1983 (Japanese)
04/0394²KS C5601-1987 (Korean)
04/0994JIS X 0201-1976 right half (Japanese single-byte)
04/1094JIS 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.

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 byte
B = scratch byte
CRCHI = high byte of the 16-bit CRC
CRCLO = 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 complete

Appending 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.

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 <= 0 or sample_time <= 0 or diff_used > 2.
  • Section 6 is missing.
  • Section 6’s diff_used > 2 or bimodal > 1.
  • Section 6 declares bimodal = 1 but 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.

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 exchange I messages, then alternate between master and slave with R requests for ECG transfers, patient lists, and ECG lists. Status flags G (go) and E (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.

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 == 0 and index == 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_duration is implied: it is the total number of samples in the rhythm record (max end_sample from Section 3) divided by 1000000 / sample_time Hz 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.