Ecg ko samjhao achese all cases of heart diseases with ecg findings with Marks in ecg slides
| Wave/Interval | Normal Duration | Kya Represent Karta Hai |
|---|---|---|
| P wave | <0.12 sec, <2.5 mm tall | Atrial depolarization |
| PR interval | 0.12-0.20 sec (120-200 ms) | AV node conduction |
| QRS complex | <0.12 sec | Ventricular depolarization |
| ST segment | Isoelectric (flat) | Early ventricular repolarization |
| T wave | Upright in most leads | Ventricular repolarization |
| QT interval | <0.44 sec (corrected) | Total ventricular activity |

Stage 1 (Minutes): Hyperacute T waves (tall, peaked, broad-based)
Stage 2 (Hours): ST elevation develops (convex upward / "tombstone")
Stage 3 (Days): Q waves form + ST elevation persists
Stage 4 (Weeks): ST normalizes, T wave inversion
Stage 5 (Months): Persistent Q waves (permanent scar)
| MI Location | ST Elevation Leads | Culprit Artery |
|---|---|---|
| Anteroseptal | V1, V2, (V3) | LAD (proximal) |
| Anterior | V1, V2, V3, V4 | LAD |
| Anterolateral | V1-V6, I, aVL | Proximal LAD |
| Lateral | I, aVL | LCx |
| Inferior | II, III, aVF | RCA (80%) or LCx |
| Inferolateral | II, III, aVF + V5, V6 | LCx |
| Posterior Wall | Tall R in V1-V2, R/S ≥1, ST depression V1-V3 | RCA/LCx |
| Right Ventricular | II, III, aVF + ST elevation in V3R-V6R | Proximal RCA |
| ECG Clue | Artery | Sensitivity/Specificity |
|---|---|---|
| ST↑ in III > II + ST↓ in I, aVL | Right coronary artery (RCA) | 90% / 71% |
| Same + ST↑ in V1 or V2R | Proximal RCA (RV involvement) | 79% / 100% |
| ST↑ in I, aVL, V2, V3 + ST↓ in V1, V2, V3 | Left Circumflex (LCx) | 83% / 96% |
| ST↑ V1-V3 + ST↓ in III, aVF >1mm | Distal LAD | - |
| ST↑ V2 >2.5 mm OR RBBB + Q wave | Proximal LAD | 12% / 100% |
| ST↑ aVR > V1 | Left main / proximal LAD occlusion | - |
| Stage | ECG Changes |
|---|---|
| Stage I (days 1-2) | Diffuse concave/saddle-shaped ST elevation in almost all leads + PR depression |
| Stage II (1-2 weeks) | ST returns to baseline, T waves flatten |
| Stage III | Diffuse T wave inversion |
| Stage IV | ECG normalizes |
K+ 5.5-6.5: Peaked, tall, narrow T waves (tent-shaped)
K+ 6.5-7.5: Widened QRS, prolonged PR
K+ 7.5-8.0: P wave disappears (sino-ventricular rhythm)
K+ >8.0: Sine wave pattern → VF → asystole
| Axis | Lead I | aVF | Causes |
|---|---|---|---|
| Normal (0° to +90°) | Positive | Positive | Normal |
| Left axis deviation (<0°) | Positive | Negative | LBBB, LVH, inferior MI |
| Right axis deviation (>+90°) | Negative | Positive | RVH, PE, lateral MI, RBBB |
| Extreme axis (±180°) | Negative | Negative | VT, severe COPD |
| Disease | Key ECG Finding | Mnemonic/Trick |
|---|---|---|
| STEMI Anterior | ST↑ V1-V4 + Q waves | LAD territory |
| STEMI Inferior | ST↑ II, III, aVF | RCA - "inferior = aVF" |
| Posterior MI | Tall R + ST↓ V1-V3 | Mirror image |
| RV MI | ST↑ V4R (right-sided) | Always do right leads in inferior MI |
| AF | No P, irregular QRS | "Totally chaotic" |
| Atrial Flutter | Sawtooth at 150/min | 2:1 block classically |
| VT | Wide QRS, AV dissociation | Broad, fast, independent P |
| WPW | Short PR + delta wave | Pre-excitation |
| LBBB | WiLLiaM (W in V1, M in V6) | New LBBB = treat like STEMI |
| RBBB | MaRRoW (M in V1, W in V6) | May be normal |
| LVH | S-V1 + R-V5 ≥35 mm | Sokolow-Lyon |
| Pericarditis | Diffuse concave ST↑, PR↓ | Saddle shape, all leads |
| PE | S1Q3T3, tachycardia | Sinus tachy most common |
| Hyperkalemia | Peaked T → sine wave | K kills in stages |
| Hypokalemia | U wave, flat T | U = hypo-Kalemia |
| Hypothermia | Osborn J waves | J = hypothermia hump |
| Tamponade | Electrical alternans | Heart swinging |
| Digoxin toxicity | Scooped ST, AV block | Reverse tick |
| Brugada | Coved ST↑ V1-V2 | RBBB pattern |
| Long QT | QTc >450 ms | TdP risk |
| 1st degree AV block | PR >200 ms | Prolonged but all conduct |
| Mobitz I (Wenckebach) | Progressive PR↑ then drop | "Longer longer longer... drop" |
| Mobitz II | Fixed PR then sudden drop | Dangerous, pace it! |
| Complete heart block | AV dissociation | P and QRS independent |
All heart'diseases with ecg findings ecg findings sholud me marked and explained properly in Hinglish
mkdir -p /home/daytona/workspace/ecg-guide && echo "Directory created"
~/ecg-guide/generate_ecg.py
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyArrowPatch
import warnings
warnings.filterwarnings('ignore')
# ─── ECG paper style ───────────────────────────────────────────────────────────
def ecg_paper(ax, xmax=10, ymin=-1.5, ymax=2.5):
ax.set_facecolor('#fff8f0')
for x in np.arange(0, xmax+0.04, 0.04):
ax.axvline(x, color='#ffcccc', lw=0.3)
for y in np.arange(ymin, ymax+0.1, 0.1):
ax.axhline(y, color='#ffcccc', lw=0.3)
for x in np.arange(0, xmax+0.2, 0.2):
ax.axvline(x, color='#ff9999', lw=0.7)
for y in np.arange(ymin, ymax+0.5, 0.5):
ax.axhline(y, color='#ff9999', lw=0.7)
ax.set_xlim(0, xmax)
ax.set_ylim(ymin, ymax)
ax.axis('off')
# ─── Building blocks ───────────────────────────────────────────────────────────
def p_wave(t, amp=0.2, width=0.08):
return amp * np.exp(-((t)**2) / (2*(width/2.5)**2))
def qrs_normal(t, amp=1.0):
q = -0.1 * np.exp(-((t+0.02)**2)/(2*0.008**2))
r = amp * np.exp(-((t) **2)/(2*0.010**2))
s = -0.3 * np.exp(-((t-0.03)**2)/(2*0.010**2))
return q + r + s
def t_wave(t, amp=0.3, width=0.07, skew=0):
return amp * np.exp(-((t-skew)**2)/(2*(width)**2))
def u_wave(t, amp=0.06):
return amp * np.exp(-((t)**2)/(2*0.04**2))
def single_beat(t_arr, start, pr=0.16, qrs_amp=1.0, st_elev=0.0,
st_depr=0.0, t_amp=0.3, t_inv=False, delta=False,
p_amp=0.2, hyperacute_t=False, q_deep=False):
sig = np.zeros_like(t_arr)
t = t_arr - start
# P wave
mask_p = (t > 0) & (t < 0.12)
sig[mask_p] += p_wave(t[mask_p] - 0.06, amp=p_amp)
# Delta wave (WPW)
if delta:
mask_d = (t > pr-0.05) & (t < pr)
sig[mask_d] += 0.3 * (t[mask_d]-(pr-0.05)) / 0.05
# QRS
mask_qrs = (t > pr) & (t < pr+0.12)
q_amp = -0.4 if q_deep else -0.1
sig[mask_qrs] += qrs_normal(t[mask_qrs]-pr-0.04, amp=qrs_amp)
if q_deep:
sig[mask_qrs] += (-0.3)*np.exp(-((t[mask_qrs]-pr-0.01)**2)/(2*0.015**2))
# ST segment
mask_st = (t > pr+0.12) & (t < pr+0.28)
sig[mask_st] += st_elev - st_depr
# T wave
t_a = 0.6 if hyperacute_t else t_amp
t_a = -abs(t_a) if t_inv else abs(t_a)
mask_t = (t > pr+0.18) & (t < pr+0.45)
sig[mask_t] += t_wave(t[mask_t]-pr-0.32, amp=t_a,
width=(0.05 if hyperacute_t else 0.07))
return sig
# ═══════════════════════════════════════════════════════════════════════════════
# 1. NORMAL ECG
# ═══════════════════════════════════════════════════════════════════════════════
fig, ax = plt.subplots(figsize=(14, 4))
ecg_paper(ax, xmax=6, ymin=-0.8, ymax=2.0)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.1, 1.9, 2.7, 3.5, 4.3, 5.1]:
sig += single_beat(t, s)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('NORMAL ECG - Sab Kuch Theek Hai', fontsize=13, fontweight='bold', color='#1a1a6e', pad=10)
# Annotations
beat_s = 0.3
ax.annotate('P wave\n(Atrial depol.)', xy=(beat_s+0.06, 0.22), xytext=(beat_s-0.2, 0.7),
arrowprops=dict(arrowstyle='->', color='blue', lw=1.5),
fontsize=8, color='blue', fontweight='bold')
ax.annotate('Q', xy=(beat_s+0.16+0.14, -0.05), xytext=(beat_s+0.05, -0.5),
arrowprops=dict(arrowstyle='->', color='darkgreen', lw=1.5),
fontsize=8, color='darkgreen', fontweight='bold')
ax.annotate('R wave\n(Peak)', xy=(beat_s+0.20, 1.0), xytext=(beat_s+0.45, 1.6),
arrowprops=dict(arrowstyle='->', color='red', lw=1.5),
fontsize=8, color='red', fontweight='bold')
ax.annotate('S', xy=(beat_s+0.23, -0.25), xytext=(beat_s+0.5, -0.6),
arrowprops=dict(arrowstyle='->', color='darkgreen', lw=1.5),
fontsize=8, color='darkgreen', fontweight='bold')
ax.annotate('ST seg.\n(Isoelectric)', xy=(beat_s+0.34, 0.02), xytext=(beat_s+0.6, 0.5),
arrowprops=dict(arrowstyle='->', color='purple', lw=1.5),
fontsize=8, color='purple', fontweight='bold')
ax.annotate('T wave\n(Ventricular repol.)', xy=(beat_s+0.48, 0.3), xytext=(beat_s+0.7, 0.85),
arrowprops=dict(arrowstyle='->', color='darkorange', lw=1.5),
fontsize=8, color='darkorange', fontweight='bold')
# PR interval brace
ax.annotate('', xy=(beat_s+0.16, -0.7), xytext=(beat_s, -0.7),
arrowprops=dict(arrowstyle='<->', color='navy', lw=1.5))
ax.text(beat_s+0.08, -0.75, 'PR 0.12-0.20s', fontsize=7, color='navy', ha='center')
# QRS brace
ax.annotate('', xy=(beat_s+0.28, -0.7), xytext=(beat_s+0.16, -0.7),
arrowprops=dict(arrowstyle='<->', color='darkred', lw=1.5))
ax.text(beat_s+0.22, -0.75, 'QRS <0.12s', fontsize=7, color='darkred', ha='center')
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/01_normal_ecg.png', dpi=130, bbox_inches='tight')
plt.close()
print("1. Normal ECG done")
# ═══════════════════════════════════════════════════════════════════════════════
# 2. STEMI (Inferior) - II, III, aVF
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(2, 1, figsize=(14, 8))
fig.patch.set_facecolor('#f0f4ff')
for idx, (label, st_e, t_amp, q_d, title_c) in enumerate([
('Lead II / III / aVF → INFERIOR MI', 0.35, 0.4, True, '#cc0000'),
('Lead V1-V4 → Reciprocal ST Depression', -0.0, 0.2, False, '#0055aa'),
]):
ax = axes[idx]
ecg_paper(ax, xmax=6, ymin=-0.8, ymax=2.2)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.2, 2.1, 3.0, 3.9, 4.8]:
st_d = 0.25 if idx == 1 else 0
sig += single_beat(t, s, st_elev=st_e if idx==0 else 0,
st_depr=st_d, t_amp=t_amp, q_deep=q_d if idx==0 else False)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title(label, fontsize=11, fontweight='bold', color=title_c, pad=6)
if idx == 0:
s = 0.3
ax.annotate('⬆ ST Elevation\n(Myocardial injury!)', xy=(s+0.34, 0.37),
xytext=(s+0.7, 1.1),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=9, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.8))
ax.annotate('Pathological Q wave\n(Dead tissue = Necrosis)', xy=(s+0.175, -0.28),
xytext=(s+0.5, -0.65),
arrowprops=dict(arrowstyle='->', color='darkred', lw=2),
fontsize=9, color='darkred', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
ax.annotate('Hyperacute T\n(Early ischemia)', xy=(s+0.49, 0.42),
xytext=(s+1.1, 0.9),
arrowprops=dict(arrowstyle='->', color='darkorange', lw=2),
fontsize=9, color='darkorange', fontweight='bold')
else:
s = 0.3
ax.annotate('⬇ Reciprocal ST Depression\n(Mirror image of injury)', xy=(s+0.30, -0.23),
xytext=(s+0.8, 0.4),
arrowprops=dict(arrowstyle='->', color='blue', lw=2),
fontsize=9, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
fig.suptitle('❤ STEMI - INFERIOR MI (RCA Occlusion)\nII, III, aVF mein ST↑ + V1-V4 mein Reciprocal ST↓',
fontsize=13, fontweight='bold', color='#8b0000', y=1.02)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/02_inferior_stemi.png', dpi=130, bbox_inches='tight')
plt.close()
print("2. Inferior STEMI done")
# ═══════════════════════════════════════════════════════════════════════════════
# 3. STEMI (Anterior) - V1-V4
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(3, 1, figsize=(14, 11))
fig.patch.set_facecolor('#fff0f0')
stages = [
('Stage 1: Hyperacute T waves (Minutes)', 0.0, 0.7, False, False, '#ff6600'),
('Stage 2: ST Elevation (Hours) - Classic STEMI', 0.45, 0.35, False, False, '#cc0000'),
('Stage 3: Q waves + T inversion (Days)', 0.15, -0.2, True, True, '#6600aa'),
]
for idx, (label, st_e, t_a, t_inv, q_d, col) in enumerate(stages):
ax = axes[idx]
ecg_paper(ax, xmax=6, ymin=-0.8, ymax=2.0)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.2, 2.1, 3.0, 3.9, 4.8]:
sig += single_beat(t, s, st_elev=st_e, t_amp=t_a, t_inv=t_inv,
q_deep=q_d, hyperacute_t=(idx==0))
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title(label, fontsize=10, fontweight='bold', color=col, pad=5)
s = 0.3
if idx == 0:
ax.annotate('TALL PEAKED T wave\n(Hyperacute - very early!)', xy=(s+0.48, 0.65),
xytext=(s+1.0, 1.5),
arrowprops=dict(arrowstyle='->', color='#ff6600', lw=2),
fontsize=8.5, color='#ff6600', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#fff3cc', alpha=0.9))
elif idx == 1:
ax.annotate('ST ELEVATION (Tombstone shape)\nConvex upward = INJURY', xy=(s+0.30, 0.47),
xytext=(s+0.9, 1.4),
arrowprops=dict(arrowstyle='->', color='red', lw=2.5),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.85))
elif idx == 2:
ax.annotate('Deep Q wave (>0.04s, >25% R)\nPermanent Scar (Necrosis)', xy=(s+0.175, -0.35),
xytext=(s+0.7, -0.65),
arrowprops=dict(arrowstyle='->', color='darkred', lw=2),
fontsize=8.5, color='darkred', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
ax.annotate('T wave INVERSION\n(Repolarisation abnormality)', xy=(s+0.48, -0.18),
xytext=(s+1.1, 0.5),
arrowprops=dict(arrowstyle='->', color='purple', lw=2),
fontsize=8.5, color='purple', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#eeddff', alpha=0.9))
fig.suptitle('❤ ANTERIOR STEMI (LAD Occlusion)\nV1, V2, V3, V4 leads mein changes - 3 Stages',
fontsize=13, fontweight='bold', color='#8b0000', y=1.02)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/03_anterior_stemi.png', dpi=130, bbox_inches='tight')
plt.close()
print("3. Anterior STEMI done")
# ═══════════════════════════════════════════════════════════════════════════════
# 4. AV BLOCKS
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(4, 1, figsize=(14, 14))
fig.patch.set_facecolor('#f0fff0')
# 1st degree
ax = axes[0]
ecg_paper(ax, xmax=7, ymin=-0.6, ymax=1.8)
t = np.linspace(0, 7, 7000)
sig = np.zeros_like(t)
for s in [0.3, 1.4, 2.5, 3.6, 4.7, 5.8]:
sig += single_beat(t, s, pr=0.28) # long PR
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('1st Degree AV Block – PR > 200 ms (1 large box)', fontsize=11, fontweight='bold', color='#006600', pad=5)
s = 0.3
ax.annotate('P wave', xy=(s+0.06, 0.2), xytext=(s-0.1, 0.8),
arrowprops=dict(arrowstyle='->', color='blue'), fontsize=9, color='blue')
ax.annotate('PROLONGED PR INTERVAL\n(>200ms / >1 large box)\n= AV Node Delay', xy=(s+0.14, 0.0),
xytext=(s+0.5, 0.8),
arrowprops=dict(arrowstyle='->', color='darkgreen', lw=2),
fontsize=8.5, color='darkgreen', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddffdd', alpha=0.9))
ax.annotate('', xy=(s+0.28, -0.4), xytext=(s, -0.4),
arrowprops=dict(arrowstyle='<->', color='darkgreen', lw=2))
ax.text(s+0.14, -0.5, '← LONG PR →', fontsize=8, color='darkgreen', ha='center', fontweight='bold')
# Mobitz I
ax = axes[1]
ecg_paper(ax, xmax=7, ymin=-0.6, ymax=1.8)
t = np.linspace(0, 7, 7000)
sig = np.zeros_like(t)
starts = [0.3, 1.22, 2.06, 2.82] # getting closer (RR shortening)
prs = [0.16, 0.22, 0.28, 0.34] # increasing PR
for s, pr in zip(starts, prs):
sig += single_beat(t, s, pr=pr)
# Drop beat - only P wave
sig += single_beat(t, 3.5, pr=10, p_amp=0.2) # P without QRS
for s, pr in zip([4.3, 5.2, 6.0, 6.78], [0.16, 0.22, 0.28, 0.34]):
sig += single_beat(t, s, pr=pr)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('2nd Degree Mobitz I (Wenckebach) – PR progressively badhta hai, phir QRS DROP!', fontsize=11, fontweight='bold', color='#cc6600', pad=5)
ax.annotate('PR badhta\njaa raha hai ↗', xy=(2.06+0.1, 0.1), xytext=(1.5, 1.2),
arrowprops=dict(arrowstyle='->', color='darkorange', lw=2),
fontsize=8.5, color='darkorange', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffe0aa', alpha=0.9))
ax.annotate('DROPPED BEAT!\n(QRS missing - P wave only)', xy=(3.56, 0.18), xytext=(3.8, 1.2),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.9))
ax.axvspan(3.4, 4.2, alpha=0.12, color='red')
# Mobitz II
ax = axes[2]
ecg_paper(ax, xmax=7, ymin=-0.6, ymax=1.8)
t = np.linspace(0, 7, 7000)
sig = np.zeros_like(t)
for s in [0.3, 1.3, 2.3, 3.3]:
sig += single_beat(t, s, pr=0.18)
# dropped beat P
sig += single_beat(t, 4.3, pr=15, p_amp=0.2)
for s in [5.3, 6.3]:
sig += single_beat(t, s, pr=0.18)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('2nd Degree Mobitz II – PR Fixed raha, Achanak QRS DROP! (Dangerous!)', fontsize=11, fontweight='bold', color='#aa0000', pad=5)
for sx in [0.3, 1.3, 2.3, 3.3, 5.3, 6.3]:
ax.axvline(sx+0.18, color='green', lw=1, alpha=0.4, ls='--')
ax.annotate('PR FIXED throughout\n(Koi change nahi)', xy=(1.3+0.09, 0.0), xytext=(0.8, 1.2),
arrowprops=dict(arrowstyle='->', color='green', lw=2),
fontsize=8.5, color='darkgreen', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddffdd', alpha=0.9))
ax.annotate('SUDDEN DROP!\n(Without warning)\nPACEMAKER CHAHIYE!', xy=(4.36, 0.18), xytext=(4.7, 1.2),
arrowprops=dict(arrowstyle='->', color='red', lw=2.5),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.9))
ax.axvspan(4.1, 5.1, alpha=0.15, color='red')
# Complete heart block
ax = axes[3]
ecg_paper(ax, xmax=7, ymin=-0.8, ymax=1.8)
t = np.linspace(0, 7, 7000)
sig = np.zeros_like(t)
# P waves at faster rate (atrial - 75/min)
for s in np.arange(0.2, 7.0, 0.8):
mt = t - s
mask = (mt > 0) & (mt < 0.12)
sig[mask] += p_wave(mt[mask] - 0.06, amp=0.25)
# QRS at slower rate (ventricular escape - 35/min) - COMPLETELY INDEPENDENT
for s in [0.8, 2.5, 4.2, 5.9]:
sig += single_beat(t, s, pr=10, p_amp=0) # No P, wide QRS
# make QRS wider
mask = (t > s+0.16) & (t < s+0.34)
sig[mask] += 0.4 * np.sin(np.pi*(t[mask]-s-0.16)/0.18)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('3rd Degree (Complete) Heart Block – AV Dissociation = EMERGENCY!', fontsize=11, fontweight='bold', color='#8b0000', pad=5)
for s in np.arange(0.2, 7.0, 0.8):
ax.axvline(s+0.06, color='blue', lw=1.2, alpha=0.3, ls=':')
for s in [0.8, 2.5, 4.2, 5.9]:
ax.axvline(s+0.2, color='red', lw=1.2, alpha=0.3, ls=':')
ax.annotate('P waves (Atria)\nRate ~75/min\n(Regular)', xy=(0.26, 0.25), xytext=(0.0, 1.3),
arrowprops=dict(arrowstyle='->', color='blue', lw=2),
fontsize=8.5, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('QRS (Ventricles)\nRate ~35/min\nWIDE (Escape rhythm)\nCOMPLETELY INDEPENDENT!', xy=(0.96, 0.9), xytext=(1.6, 1.5),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
fig.suptitle('AV HEART BLOCKS - Sabhi 4 Types with ECG Marks\n(1st → 2nd Mobitz I → 2nd Mobitz II → Complete)',
fontsize=14, fontweight='bold', color='#003300', y=1.01)
plt.tight_layout(h_pad=2.5)
plt.savefig('/home/daytona/workspace/ecg-guide/04_av_blocks.png', dpi=130, bbox_inches='tight')
plt.close()
print("4. AV Blocks done")
# ═══════════════════════════════════════════════════════════════════════════════
# 5. ATRIAL FIBRILLATION vs FLUTTER
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(2, 1, figsize=(14, 8))
fig.patch.set_facecolor('#f5f0ff')
# AF
ax = axes[0]
ecg_paper(ax, xmax=8, ymin=-0.8, ymax=1.8)
t = np.linspace(0, 8, 8000)
sig = np.zeros_like(t)
# fibrillatory baseline (irregular bumps)
np.random.seed(42)
for _ in range(180):
pos = np.random.uniform(0, 8)
amp = np.random.uniform(0.04, 0.12)
sig += amp * np.exp(-((t-pos)**2)/(2*0.02**2))
# irregularly irregular QRS
qrs_times = [0.4, 1.05, 1.55, 2.4, 2.75, 3.6, 4.1, 4.95, 5.6, 6.0, 6.8, 7.5]
for s in qrs_times:
sig += single_beat(t, s, pr=10, p_amp=0)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('ATRIAL FIBRILLATION (AF) – Chaotic baseline, No P waves, Irregular-Irregular QRS', fontsize=11, fontweight='bold', color='#5500aa', pad=5)
ax.annotate('Fibrillatory baseline\n(P waves ABSENT)\nChaotic, wavy line', xy=(0.7, 0.1), xytext=(0.2, 1.2),
arrowprops=dict(arrowstyle='->', color='purple', lw=2),
fontsize=8.5, color='purple', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#eeddff', alpha=0.9))
ax.annotate('IRREGULAR-IRREGULAR\nQRS spacing\n(Totally unpredictable)', xy=(4.1, 0.9), xytext=(4.8, 1.5),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.85))
# mark irregular spacing
for i in range(len(qrs_times)-1):
ax.annotate('', xy=(qrs_times[i+1]+0.05, -0.55), xytext=(qrs_times[i]+0.05, -0.55),
arrowprops=dict(arrowstyle='<->', color='darkblue', lw=1))
# Atrial Flutter
ax = axes[1]
ecg_paper(ax, xmax=8, ymin=-0.8, ymax=1.8)
t = np.linspace(0, 8, 8000)
sig = np.zeros_like(t)
# sawtooth flutter waves at 300/min → 0.2s cycle
for ft in np.arange(0.1, 8.0, 0.2):
mt = t - ft
mask = (mt >= 0) & (mt < 0.18)
# sawtooth: negative slope then sharp up
sig[mask] += -0.18 * mt[mask]/0.18 + 0.09 * np.exp(-((mt[mask]-0.16)**2)/(2*0.012**2))
# QRS every 0.4s (2:1 conduction → 150/min)
for s in np.arange(0.35, 8.0, 0.4):
sig += single_beat(t, s, pr=10, p_amp=0) * 1.0
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('ATRIAL FLUTTER – Sawtooth waves 300/min, 2:1 → Ventricular rate 150/min', fontsize=11, fontweight='bold', color='#006699', pad=5)
ax.annotate('"Sawtooth / Picket fence"\nFlutter waves (F waves)\n~300/min, regular', xy=(1.0, -0.1), xytext=(0.5, -0.6),
arrowprops=dict(arrowstyle='->', color='navy', lw=2),
fontsize=8.5, color='navy', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('QRS: REGULAR\n(2:1 block → 150 bpm)\nHR exactly 150 = THINK FLUTTER!', xy=(1.55, 0.95), xytext=(2.2, 1.5),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.85))
fig.suptitle('ATRIAL FIBRILLATION vs ATRIAL FLUTTER\nDono ki ECG mein Key Difference Dekho',
fontsize=13, fontweight='bold', color='#330066', y=1.02)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/05_af_flutter.png', dpi=130, bbox_inches='tight')
plt.close()
print("5. AF & Flutter done")
# ═══════════════════════════════════════════════════════════════════════════════
# 6. VENTRICULAR TACHYCARDIA + VF
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(2, 1, figsize=(14, 8))
fig.patch.set_facecolor('#fff0f0')
# VT
ax = axes[0]
ecg_paper(ax, xmax=8, ymin=-1.5, ymax=2.5)
t = np.linspace(0, 8, 8000)
sig = np.zeros_like(t)
# Few normal beats first
for s in [0.3, 1.1]:
sig += single_beat(t, s)
# Then VT: wide bizarre QRS at rate ~160/min
for s in np.arange(2.0, 8.0, 0.375):
mt = t - s
mask = (mt >= 0) & (mt < 0.37)
# Wide, bizarre monophasic QRS
sig[mask] += 1.8 * np.sin(np.pi * mt[mask] / 0.375) * np.exp(-mt[mask]/0.3)
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('VENTRICULAR TACHYCARDIA (VT) – Wide QRS, Rate >100, No P waves', fontsize=11, fontweight='bold', color='#cc0000', pad=5)
ax.axvspan(0, 1.9, alpha=0.1, color='green', label='Normal')
ax.axvspan(2.0, 8.0, alpha=0.1, color='red', label='VT')
ax.text(0.95, 2.2, 'Normal beats', fontsize=9, color='darkgreen', ha='center',
bbox=dict(boxstyle='round', facecolor='#ddffdd', alpha=0.8))
ax.text(5.0, 2.2, 'VENTRICULAR TACHYCARDIA begins here ⚠', fontsize=9, color='darkred', ha='center',
bbox=dict(boxstyle='round', facecolor='#ffdddd', alpha=0.8))
ax.annotate('WIDE QRS (>0.12s)\nBizarre shape\nRate ~160/min', xy=(2.2, 1.6), xytext=(3.0, 2.4),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.85))
ax.axvline(2.0, color='red', lw=2.5, ls='--')
ax.text(1.95, -1.3, 'Onset', fontsize=9, color='red', fontweight='bold', ha='right')
# VF
ax = axes[1]
ecg_paper(ax, xmax=8, ymin=-1.5, ymax=2.5)
t = np.linspace(0, 8, 8000)
np.random.seed(7)
sig = np.zeros_like(t)
# Chaotic - multiple frequencies
for f in [3, 4, 5, 7, 9, 11, 14]:
amp = np.random.uniform(0.3, 0.9)
phase = np.random.uniform(0, 2*np.pi)
sig += amp * np.sin(2*np.pi*f*t + phase) * np.exp(-0.05*t) * 0.6
sig += 0.3 * np.random.randn(len(t)) * 0.5
ax.plot(t, sig, 'k', lw=1.2)
ax.set_title('VENTRICULAR FIBRILLATION (VF) – COMPLETELY CHAOTIC → No cardiac output → DEATH if not defibrillated!', fontsize=11, fontweight='bold', color='#8b0000', pad=5)
ax.annotate('Completely CHAOTIC\nNo QRS, No P wave\nNo T wave\n= No effective heartbeat!', xy=(1.5, 0.8), xytext=(3.0, 1.8),
arrowprops=dict(arrowstyle='->', color='darkred', lw=2.5),
fontsize=9, color='darkred', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.9))
ax.text(4.0, 2.2, '⚡ IMMEDIATE DEFIBRILLATION REQUIRED! ⚡', fontsize=11, color='red',
ha='center', fontweight='bold',
bbox=dict(boxstyle='round', facecolor='#ffcccc', alpha=0.9))
fig.suptitle('VT & VF - Life-Threatening Ventricular Arrhythmias\n(Dono mein immediate treatment required!)',
fontsize=13, fontweight='bold', color='#8b0000', y=1.02)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/06_vt_vf.png', dpi=130, bbox_inches='tight')
plt.close()
print("6. VT & VF done")
# ═══════════════════════════════════════════════════════════════════════════════
# 7. LBBB vs RBBB
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(2, 2, figsize=(14, 8))
fig.patch.set_facecolor('#f0f8ff')
def lbbb_qrs(t, start):
mt = t - start
sig = np.zeros_like(t)
# V1: deep W (QS pattern)
mask = (mt > 0) & (mt < 0.16)
sig[mask] += -0.6 * np.sin(np.pi*mt[mask]/0.16)
return sig
def rbbb_qrs(t, start):
mt = t - start
sig = np.zeros_like(t)
# RSR' in V1
mask = (mt > 0) & (mt < 0.16)
r1 = 0.7 * np.exp(-((mt[mask]-0.03)**2)/(2*0.015**2))
s = -0.4 * np.exp(-((mt[mask]-0.07)**2)/(2*0.015**2))
r2 = 0.9 * np.exp(-((mt[mask]-0.12)**2)/(2*0.018**2))
sig[mask] += r1 + s + r2
return sig
def rbbb_v6(t, start):
mt = t - start
sig = np.zeros_like(t)
mask = (mt > 0) & (mt < 0.16)
r = 0.9 * np.exp(-((mt[mask]-0.03)**2)/(2*0.015**2))
s = -0.5 * np.exp(-((mt[mask]-0.12)**2)/(2*0.020**2))
sig[mask] += r + s
return sig
configs = [
(0, 0, 'LBBB - Lead V1\n(Deep W / QS pattern)', lbbb_qrs, '#000088'),
(0, 1, 'LBBB - Lead V5/V6\n(Broad notched M pattern)', None, '#000088'),
(1, 0, "RBBB - Lead V1\n(RSR' pattern = 'Bunny Ears')", rbbb_qrs, '#880000'),
(1, 1, 'RBBB - Lead V6\n(Broad S wave)', rbbb_v6, '#880000'),
]
for r, c, title, func, col in configs:
ax = axes[r][c]
ecg_paper(ax, xmax=5, ymin=-1.0, ymax=1.8)
t = np.linspace(0, 5, 5000)
sig = np.zeros_like(t)
for s in [0.3, 1.4, 2.5, 3.6]:
# P wave
mt = t - s
mask = (mt > 0) & (mt < 0.10)
sig[mask] += p_wave(mt[mask]-0.05, amp=0.2)
# QRS
if func:
sig += func(t, s+0.16)
else:
# LBBB V6: broad notched M
mask2 = (mt > 0.16) & (mt < 0.34)
sig[mask2] += 0.9*(np.sin(np.pi*(mt[mask2]-0.16)/0.18)
+ 0.3*np.sin(2*np.pi*(mt[mask2]-0.16)/0.18))
# T wave
mt2 = t - s
mask3 = (mt2 > 0.36) & (mt2 < 0.56)
t_inv = (r == 0) # LBBB has discordant T
ta = -0.25 if t_inv else 0.25
sig[mask3] += ta * np.exp(-((mt2[mask3]-0.46)**2)/(2*0.05**2))
ax.plot(t, sig, 'k', lw=1.8)
ax.set_title(title, fontsize=9, fontweight='bold', color=col, pad=5)
if (r, c) == (0, 0):
ax.annotate('DEEP W (QS)\n= No R wave in V1', xy=(0.3+0.16+0.08, -0.58), xytext=(0.3+0.5, -0.8),
arrowprops=dict(arrowstyle='->', color='blue', lw=1.8),
fontsize=8, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.2', facecolor='#ddeeff', alpha=0.9))
elif (r, c) == (0, 1):
ax.annotate('Broad NOTCHED M\n(Rabbit ear / bifid R)', xy=(0.3+0.24, 0.85), xytext=(0.3+0.6, 1.4),
arrowprops=dict(arrowstyle='->', color='blue', lw=1.8),
fontsize=8, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.2', facecolor='#ddeeff', alpha=0.9))
elif (r, c) == (1, 0):
ax.annotate("RSR' = Bunny Ears!\nR' taller than R", xy=(0.3+0.16+0.12, 0.88), xytext=(0.3+0.6, 1.4),
arrowprops=dict(arrowstyle='->', color='red', lw=1.8),
fontsize=8, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.2', facecolor='#ffdddd', alpha=0.9))
elif (r, c) == (1, 1):
ax.annotate('Wide TERMINAL S wave\n(Broad & deep)', xy=(0.3+0.16+0.12, -0.48), xytext=(0.3+0.6, -0.75),
arrowprops=dict(arrowstyle='->', color='red', lw=1.8),
fontsize=8, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.2', facecolor='#ffdddd', alpha=0.9))
fig.suptitle('BUNDLE BRANCH BLOCKS\nLBBB: WiLLiaM mnemonic (W in V1, M in V6) | RBBB: MaRRoW mnemonic (M in V1, W in V6)',
fontsize=12, fontweight='bold', color='#003366', y=1.03)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/07_bbb.png', dpi=130, bbox_inches='tight')
plt.close()
print("7. BBB done")
# ═══════════════════════════════════════════════════════════════════════════════
# 8. WPW SYNDROME
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
fig.patch.set_facecolor('#fffbf0')
for idx, (title, is_wpw) in enumerate([
('NORMAL ECG (Comparison)', False),
('WPW SYNDROME - Pre-excitation', True),
]):
ax = axes[idx]
ecg_paper(ax, xmax=5, ymin=-0.8, ymax=1.8)
t = np.linspace(0, 5, 5000)
sig = np.zeros_like(t)
for s in [0.3, 1.4, 2.5, 3.6]:
sig += single_beat(t, s, pr=0.07 if is_wpw else 0.16,
delta=is_wpw)
ax.plot(t, sig, 'k', lw=1.8)
col = '#aa6600' if is_wpw else '#006600'
ax.set_title(title, fontsize=10, fontweight='bold', color=col, pad=5)
s = 0.3
if is_wpw:
ax.annotate('SHORT PR (<0.12s)\n= Bypasses AV node!', xy=(s+0.04, 0.01), xytext=(s-0.15, 0.8),
arrowprops=dict(arrowstyle='->', color='darkblue', lw=2),
fontsize=8.5, color='darkblue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('DELTA WAVE\n(Slurred QRS upstroke)\n= Accessory pathway!', xy=(s+0.09, 0.28), xytext=(s+0.4, 1.3),
arrowprops=dict(arrowstyle='->', color='darkorange', lw=2),
fontsize=8.5, color='darkorange', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#fff3cc', alpha=0.9))
ax.annotate('Wide QRS\n(Fusion beat)', xy=(s+0.15, 0.6), xytext=(s+0.6, 1.5),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
# Mark short PR
ax.annotate('', xy=(s+0.07, -0.55), xytext=(s, -0.55),
arrowprops=dict(arrowstyle='<->', color='darkblue', lw=2))
ax.text(s+0.035, -0.68, 'SHORT PR', fontsize=8, color='darkblue', ha='center', fontweight='bold')
fig.suptitle('WPW SYNDROME (Wolff-Parkinson-White)\nShort PR + Delta wave + Wide QRS = Pre-excitation Triad',
fontsize=13, fontweight='bold', color='#664400', y=1.05)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/08_wpw.png', dpi=130, bbox_inches='tight')
plt.close()
print("8. WPW done")
# ═══════════════════════════════════════════════════════════════════════════════
# 9. PERICARDITIS
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(2, 1, figsize=(14, 8))
fig.patch.set_facecolor('#f5fffa')
for idx, (title, pr_dep, st_type, col) in enumerate([
('Stage I Pericarditis – ALL leads mein Concave (Saddle) ST Elevation + PR Depression', True, 'saddle', '#006600'),
('Compare: STEMI – Localized Convex (Tombstone) ST Elevation (only specific leads)', False, 'convex', '#cc0000'),
]):
ax = axes[idx]
ecg_paper(ax, xmax=6, ymin=-0.8, ymax=2.0)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.3, 2.3, 3.3, 4.3, 5.3]:
mt = t - s
# P wave with PR depression
mask_p = (mt > 0) & (mt < 0.12)
sig[mask_p] += p_wave(mt[mask_p]-0.06, amp=0.22)
if pr_dep:
mask_pr = (mt > 0.12) & (mt < 0.18)
sig[mask_pr] -= 0.08 # PR depression
# QRS
mask_qrs = (mt > 0.18) & (mt < 0.30)
sig[mask_qrs] += qrs_normal(mt[mask_qrs]-0.24, amp=0.9)
# ST + T
if st_type == 'saddle':
mask_st = (mt > 0.30) & (mt < 0.55)
sig[mask_st] += 0.22 * (1 - ((mt[mask_st]-0.425)/0.125)**2) + 0.05 # concave saddle
else: # convex tombstone
mask_st = (mt > 0.30) & (mt < 0.55)
sig[mask_st] += 0.22 * np.exp(-((mt[mask_st]-0.30)**2)/(2*0.15**2)) + 0.18
# T wave
mask_t = (mt > 0.42) & (mt < 0.62)
sig[mask_t] += 0.3 * np.exp(-((mt[mask_t]-0.52)**2)/(2*0.06**2))
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title(title, fontsize=10, fontweight='bold', color=col, pad=5)
s = 0.3
if idx == 0:
ax.annotate('CONCAVE / SADDLE shape\n(U shape mein ST↑)\nDIFFUSE - ALL leads mein!', xy=(s+0.40, 0.28),
xytext=(s+0.85, 1.2),
arrowprops=dict(arrowstyle='->', color='darkgreen', lw=2),
fontsize=8.5, color='darkgreen', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddffdd', alpha=0.9))
ax.annotate('PR DEPRESSION\n(Pericarditis ka\nPathognomonic sign!)', xy=(s+0.14, -0.06),
xytext=(s-0.1, -0.55),
arrowprops=dict(arrowstyle='->', color='blue', lw=2),
fontsize=8.5, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
else:
ax.annotate('CONVEX / TOMBSTONE shape\n(Dome shape mein ST↑)\nLOCALIZED - Only 1 territory!', xy=(s+0.32, 0.42),
xytext=(s+0.85, 1.4),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
fig.suptitle('PERICARDITIS vs STEMI\nSabse Important Difference: Shape + Distribution + PR changes',
fontsize=13, fontweight='bold', color='#003300', y=1.02)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/09_pericarditis.png', dpi=130, bbox_inches='tight')
plt.close()
print("9. Pericarditis done")
# ═══════════════════════════════════════════════════════════════════════════════
# 10. ELECTROLYTES: Hyperkalemia + Hypokalemia
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(4, 1, figsize=(14, 14))
fig.patch.set_facecolor('#fafff0')
stages_k = [
('Normal (K+ = 3.5-5 mmol/L) - Reference', 0.3, 0.0, 0.35, False, 0.05, '#333333'),
('Mild Hyperkalemia (K+ 5.5-6.5) - PEAKED T waves (Tent shaped)', 0.35, 0.0, 0.7, False, 0.05, '#cc6600'),
('Severe Hyperkalemia (K+ 7-8) - Wide QRS + SINE WAVE', 0.30, 0.0, 0.4, False, 0.05, '#cc0000'),
('Hypokalemia (K+ < 3.5) - FLAT T wave + PROMINENT U wave', 0.20, 0.0, 0.06, False, 0.18, '#0055cc'),
]
for i, (title, p_a, st_e, t_a, t_inv, u_a, col) in enumerate(stages_k):
ax = axes[i]
ecg_paper(ax, xmax=6, ymin=-0.6, ymax=2.2)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.3, 2.3, 3.3, 4.3, 5.3]:
mt = t - s
# P
mask_p = (mt > 0) & (mt < 0.12)
sig[mask_p] += p_wave(mt[mask_p]-0.06, amp=p_a)
# QRS - widen in hyperK
qrs_w = 0.22 if i == 2 else 0.10
mask_q = (mt > 0.16) & (mt < 0.16+qrs_w)
if i == 2: # sine wave
sig[mask_q] += 1.5 * np.sin(np.pi*(mt[mask_q]-0.16)/qrs_w)
else:
sig[mask_q] += qrs_normal(mt[mask_q]-0.16-qrs_w/2, amp=0.9)
# T
mask_t = (mt > 0.30) & (mt < 0.55)
sig[mask_t] += t_a * np.exp(-((mt[mask_t]-0.42)**2)/(2*(0.025 if i==1 else 0.06)**2))
# U wave
if u_a > 0.1:
mask_u = (mt > 0.58) & (mt < 0.72)
sig[mask_u] += u_a * np.exp(-((mt[mask_u]-0.65)**2)/(2*0.04**2))
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title(title, fontsize=9.5, fontweight='bold', color=col, pad=4)
s = 0.3
if i == 1:
ax.annotate('PEAKED T wave (Tent shaped)\nNarrow base, tall peak\nEarliest sign of HyperK!', xy=(s+0.42, 0.68),
xytext=(s+0.9, 1.6),
arrowprops=dict(arrowstyle='->', color='darkorange', lw=2),
fontsize=8.5, color='darkorange', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#fff3cc', alpha=0.9))
elif i == 2:
ax.annotate('SINE WAVE pattern!\nP wave disappears\nWide QRS - DANGER!', xy=(s+0.26, 1.4), xytext=(s+1.0, 2.0),
arrowprops=dict(arrowstyle='->', color='red', lw=2.5),
fontsize=8.5, color='red', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.9))
elif i == 3:
ax.annotate('Flat/Absent T wave', xy=(s+0.42, 0.07), xytext=(s+0.1, 0.8),
arrowprops=dict(arrowstyle='->', color='blue', lw=2),
fontsize=8.5, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('PROMINENT U wave\n(After T wave)\nBest seen V2-V3', xy=(s+0.65, 0.18), xytext=(s+1.1, 0.7),
arrowprops=dict(arrowstyle='->', color='darkblue', lw=2),
fontsize=8.5, color='darkblue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
fig.suptitle('ELECTROLYTE DISTURBANCES - ECG Changes\nHyperkalemia (K+↑) vs Hypokalemia (K+↓)',
fontsize=13, fontweight='bold', color='#003300', y=1.01)
plt.tight_layout(h_pad=2.0)
plt.savefig('/home/daytona/workspace/ecg-guide/10_electrolytes.png', dpi=130, bbox_inches='tight')
plt.close()
print("10. Electrolytes done")
# ═══════════════════════════════════════════════════════════════════════════════
# 11. LVH + RVH
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
fig.patch.set_facecolor('#fff5ee')
# LVH - V5 lead (tall R wave)
ax = axes[0]
ecg_paper(ax, xmax=5, ymin=-1.0, ymax=3.5)
t = np.linspace(0, 5, 5000)
sig = np.zeros_like(t)
for s in [0.3, 1.4, 2.5, 3.6]:
sig += single_beat(t, s, qrs_amp=2.8, t_amp=-0.4, t_inv=True) # tall R, ST strain
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('LVH - Lead V5/V6\nTall R wave (>25mm) + ST strain', fontsize=10, fontweight='bold', color='#8b0000', pad=5)
ax.annotate('TALL R wave\n>25 mm in V5\nSokolow criterion:\nS(V1)+R(V5)≥35mm', xy=(0.3+0.20, 2.8), xytext=(0.3+0.7, 3.2),
arrowprops=dict(arrowstyle='->', color='darkred', lw=2),
fontsize=8.5, color='darkred', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
ax.annotate('ST-T "STRAIN" pattern\n(Asymmetric ST depression\n+ T inversion)', xy=(0.3+0.45, -0.38),
xytext=(0.3+0.9, -0.8),
arrowprops=dict(arrowstyle='->', color='purple', lw=2),
fontsize=8.5, color='purple', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#eeddff', alpha=0.9))
# Measure bar
ax.annotate('', xy=(0.3+0.20, 0.0), xytext=(0.3+0.20, 2.8),
arrowprops=dict(arrowstyle='<->', color='red', lw=2))
ax.text(0.3+0.33, 1.4, '≥35mm\ntotal', fontsize=8, color='red', fontweight='bold')
# RVH - V1 lead (dominant R)
ax = axes[1]
ecg_paper(ax, xmax=5, ymin=-1.0, ymax=2.2)
t = np.linspace(0, 5, 5000)
sig = np.zeros_like(t)
for s in [0.3, 1.4, 2.5, 3.6]:
mt = t - s
mask = (mt > 0) & (mt < 0.12)
sig[mask] += p_wave(mt[mask]-0.05, amp=0.3) # P pulmonale (tall P)
mask2 = (mt > 0.16) & (mt < 0.28)
# Dominant R in V1
sig[mask2] += 1.5 * np.exp(-((mt[mask2]-0.22)**2)/(2*0.02**2))
sig[mask2] -= 0.3 * np.exp(-((mt[mask2]-0.26)**2)/(2*0.012**2)) # small S
mask3 = (mt > 0.32) & (mt < 0.52)
sig[mask3] -= 0.3 * np.exp(-((mt[mask3]-0.42)**2)/(2*0.06**2)) # T inversion
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('RVH - Lead V1\nDominant R wave + Right axis deviation', fontsize=10, fontweight='bold', color='#0044aa', pad=5)
ax.annotate('P PULMONALE\n(Tall peaked P >2.5mm)\n= RA enlargement', xy=(0.3+0.06, 0.3), xytext=(0.0, 1.5),
arrowprops=dict(arrowstyle='->', color='blue', lw=2),
fontsize=8.5, color='blue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('DOMINANT R in V1\n(R/S ratio >1)\n= RVH sign!', xy=(0.3+0.22, 1.5), xytext=(0.3+0.7, 1.9),
arrowprops=dict(arrowstyle='->', color='darkblue', lw=2),
fontsize=8.5, color='darkblue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('T inversion V1-V3\n(RV strain)', xy=(0.3+0.42, -0.29), xytext=(0.3+0.8, -0.7),
arrowprops=dict(arrowstyle='->', color='navy', lw=2),
fontsize=8.5, color='navy', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
fig.suptitle('VENTRICULAR HYPERTROPHY\nLVH (Hypertension, Aortic Stenosis) | RVH (COPD, Pulmonary Hypertension)',
fontsize=13, fontweight='bold', color='#440000', y=1.05)
plt.tight_layout()
plt.savefig('/home/daytona/workspace/ecg-guide/11_hypertrophy.png', dpi=130, bbox_inches='tight')
plt.close()
print("11. Hypertrophy done")
# ═══════════════════════════════════════════════════════════════════════════════
# 12. LONG QT + BRUGADA + HYPOTHERMIA (Osborn wave)
# ═══════════════════════════════════════════════════════════════════════════════
fig, axes = plt.subplots(3, 1, figsize=(14, 12))
fig.patch.set_facecolor('#f5f5ff')
# Long QT
ax = axes[0]
ecg_paper(ax, xmax=6, ymin=-0.6, ymax=1.8)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.5, 2.7, 3.9, 5.1]:
sig += single_beat(t, s, t_amp=0.35)
# extra long QT - extend ST
mt = t - s
mask = (mt > 0.28) & (mt < 0.75)
sig[mask] += 0.02 # prolonged flat ST
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('LONG QT SYNDROME – QTc >450ms (Men) / >470ms (Women)', fontsize=11, fontweight='bold', color='#440088', pad=5)
s = 0.3
ax.annotate('', xy=(s+0.73, -0.4), xytext=(s+0.16, -0.4),
arrowprops=dict(arrowstyle='<->', color='purple', lw=2))
ax.text(s+0.45, -0.55, '← PROLONGED QT interval →', fontsize=9, color='purple', ha='center', fontweight='bold')
ax.annotate('Long flat ST segment\n(Prolonged repolarization)', xy=(s+0.42, 0.03), xytext=(s+0.8, 0.7),
arrowprops=dict(arrowstyle='->', color='purple', lw=2),
fontsize=8.5, color='purple', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#eeddff', alpha=0.9))
ax.text(5.0, 1.5, 'Risk: Torsades de Pointes (TdP)!', fontsize=10, color='red',
bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.8), fontweight='bold')
# Brugada
ax = axes[1]
ecg_paper(ax, xmax=6, ymin=-0.8, ymax=2.0)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.4, 2.5, 3.6, 4.7]:
mt = t - s
# P wave
mask_p = (mt > 0) & (mt < 0.10)
sig[mask_p] += p_wave(mt[mask_p]-0.05, amp=0.2)
# RBBB + Coved ST elevation
mask_q = (mt > 0.16) & (mt < 0.30)
# RSR' morphology
sig[mask_q] += (0.7*np.exp(-((mt[mask_q]-0.19)**2)/(2*0.015**2))
- 0.3*np.exp(-((mt[mask_q]-0.22)**2)/(2*0.010**2))
+ 1.0*np.exp(-((mt[mask_q]-0.26)**2)/(2*0.018**2)))
# Coved ST: downsloping from peak
mask_st = (mt > 0.30) & (mt < 0.55)
sig[mask_st] += 0.5 * np.exp(-3*(mt[mask_st]-0.30)) - 0.05
# T wave inversion
mask_t = (mt > 0.42) & (mt < 0.60)
sig[mask_t] -= 0.15 * np.exp(-((mt[mask_t]-0.51)**2)/(2*0.05**2))
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('BRUGADA SYNDROME (Type 1) – V1-V2 mein Coved ST Elevation + RBBB pattern', fontsize=11, fontweight='bold', color='#8b0044', pad=5)
s = 0.3
ax.annotate("RSR' (RBBB morphology)\nin V1", xy=(s+0.26, 0.99), xytext=(s+0.6, 1.6),
arrowprops=dict(arrowstyle='->', color='darkred', lw=2),
fontsize=8.5, color='darkred', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ffdddd', alpha=0.9))
ax.annotate('COVED ST elevation\n(Downsloping from peak)\n≥2mm in V1-V2\n= Type 1 Brugada!', xy=(s+0.38, 0.44),
xytext=(s+0.85, 1.3),
arrowprops=dict(arrowstyle='->', color='#cc0044', lw=2),
fontsize=8.5, color='#cc0044', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.9))
ax.annotate('T wave INVERSION', xy=(s+0.51, -0.13), xytext=(s+1.0, -0.5),
arrowprops=dict(arrowstyle='->', color='navy', lw=2),
fontsize=8.5, color='navy', fontweight='bold')
# Hypothermia - Osborn wave
ax = axes[2]
ecg_paper(ax, xmax=6, ymin=-0.6, ymax=2.0)
t = np.linspace(0, 6, 6000)
sig = np.zeros_like(t)
for s in [0.3, 1.6, 2.9, 4.2, 5.5]: # bradycardia
mt = t - s
mask_p = (mt > 0) & (mt < 0.14)
sig[mask_p] += p_wave(mt[mask_p]-0.07, amp=0.22)
mask_q = (mt > 0.22) & (mt < 0.38)
sig[mask_q] += qrs_normal(mt[mask_q]-0.30, amp=0.85)
# Osborn / J wave - positive deflection at J point
mask_j = (mt > 0.38) & (mt < 0.55)
sig[mask_j] += 0.45 * np.exp(-((mt[mask_j]-0.46)**2)/(2*0.035**2))
# Long QT with late T
mask_t = (mt > 0.58) & (mt < 0.85)
sig[mask_t] += 0.28 * np.exp(-((mt[mask_t]-0.72)**2)/(2*0.07**2))
ax.plot(t, sig, 'k', lw=1.5)
ax.set_title('HYPOTHERMIA – Osborn (J) Waves + Bradycardia + Prolonged intervals', fontsize=11, fontweight='bold', color='#0033aa', pad=5)
s = 0.3
ax.annotate('OSBORN (J) WAVE\n(Hump at J point)\nPathognomonic of\nhypothermia!', xy=(s+0.46, 0.44),
xytext=(s+0.85, 1.5),
arrowprops=dict(arrowstyle='->', color='darkblue', lw=2),
fontsize=8.5, color='darkblue', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('BRADYCARDIA\n(Rate <50 in\nsevere hypothermia)', xy=(1.6+0.3, 0.85), xytext=(2.0, 1.7),
arrowprops=dict(arrowstyle='->', color='navy', lw=2),
fontsize=8.5, color='navy', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#ddeeff', alpha=0.9))
ax.annotate('Prolonged QT', xy=(s+0.65, 0.03), xytext=(s+1.1, -0.4),
arrowprops=dict(arrowstyle='->', color='blue', lw=1.5),
fontsize=8, color='blue', fontweight='bold')
fig.suptitle('SPECIAL ECG PATTERNS\nLong QT | Brugada Syndrome | Hypothermia',
fontsize=13, fontweight='bold', color='#220055', y=1.01)
plt.tight_layout(h_pad=2.5)
plt.savefig('/home/daytona/workspace/ecg-guide/12_special_patterns.png', dpi=130, bbox_inches='tight')
plt.close()
print("12. Special patterns done")
print("\n✅ ALL 12 ECG diagrams generated successfully!")
cd /home/daytona/workspace/ecg-guide && python generate_ecg.py 2>&1
cd /home/daytona/workspace/ecg-guide && python3 -c " import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.image as mpimg from matplotlib.backends.backend_pdf import PdfPages import os images = sorted([f for f in os.listdir('.') if f.endswith('.png')]) print('Images found:', images) with PdfPages('ECG_Complete_Guide_Hinglish.pdf') as pdf: for img_file in images: img = mpimg.imread(img_file) fig, ax = plt.subplots(figsize=(14, 9)) ax.imshow(img) ax.axis('off') pdf.savefig(fig, bbox_inches='tight', dpi=130) plt.close() print(f'Added: {img_file}') print('PDF created!') " 2>&1
ECG Complete Guide Hinglish
PDF Document · PDF

| Wave/Interval | Duration | Kya hota hai |
|---|---|---|
| P wave | <0.12 sec, <2.5 mm | Atria depolarize hote hain (SA node se signal) |
| PR interval | 0.12 - 0.20 sec (1 large box) | AV node mein conduction |
| QRS complex | <0.12 sec | Ventricles depolarize hote hain |
| ST segment | Isoelectric (flat/baseline par) | Early ventricular repolarization |
| T wave | Upright in most leads | Ventricular repolarization |
| QT interval | <0.44 sec | Total ventricular activity |
Trick: ECG paper - 1 small box = 0.04 sec | 1 large box = 0.2 sec | Standard speed = 25 mm/sec

| MI Location | ST Elevation Leads | Culprit Artery |
|---|---|---|
| Anteroseptal | V1, V2 | LAD proximal |
| Anterior | V1-V4 | LAD |
| Anterolateral | V1-V6, I, aVL | Proximal LAD |
| Lateral | I, aVL | LCx |
| Inferior | II, III, aVF | RCA (80%) |
| Posterior | Tall R in V1-V2, R/S≥1 | RCA/LCx |
| RV Infarct | ST↑ in V4R (right-sided lead) | Proximal RCA |
Important: Inferior MI dekho to HAMESHA right-sided leads lagao (V4R) - RV infarct rule out karna zaroori hai!

Clinical Pearl: New LBBB + chest pain = treat karo jaise STEMI!



VT vs SVT differentiation: AV dissociation ya fusion beats dikhe → VT confirm! Age >50 + history of MI → VT zyada likely.



| Feature | Pericarditis | STEMI |
|---|---|---|
| ST shape | Concave (saddle/U shape) | Convex (tombstone/dome) |
| Distribution | DIFFUSE - ALL leads (except aVR, V1) | Localized (one territory) |
| PR segment | DEPRESSED (pathognomonic!) | Normal |
| Reciprocal changes | ABSENT | Present |
| Q waves | Absent | Present (later) |
| aVR | ST depression | ST elevation (in STEMI) |
Spodick's sign = Downsloping TP segment in lead II = specific for pericarditis

K+ 5.5-6.5: Peaked (tent-shaped) T waves → EARLIEST sign!
K+ 6.5-7.5: Widened QRS, PR prolongation, P wave flattens
K+ 7.5-8.0: P wave disappears (sinoventricular rhythm)
K+ >8.0: SINE WAVE pattern → VF → Asystole


Important: S1Q3T3 only ~20% cases mein hota hai. Sinus tachycardia is the most common finding. RBBB + tachycardia + clinical suspicion → PE consider karo.
| Disease | ECG Finding | Mnemonic/Trick |
|---|---|---|
| Normal | P-PR-QRS-ST-T sab normal range mein | Baseline yaad rakho |
| Anterior STEMI | ST↑ V1-V4, hyperacute T, Q waves | LAD territory |
| Inferior STEMI | ST↑ II, III, aVF + reciprocal ST↓ V1-V4 | "aVF = inferior" |
| RV MI | ST↑ V4R (right-sided lead) | Inferior MI mein ALWAYS right leads lagao |
| Posterior MI | Tall R + ST↓ V1-V3 (mirror image) | Posterior leads V7-V9 lagao |
| 1st Degree AV Block | PR >200 ms, no dropped beats | Prolonged but all conduct |
| Mobitz I | PR progressively↑ then QRS drops | "Longer Longer DROP - Wenckebach" |
| Mobitz II | Fixed PR then sudden QRS drop | Dangerous → PACE IT! |
| Complete AV Block | AV dissociation, P aur QRS independent | EMERGENCY - Pacing! |
| AF | No P waves, irregular-irregular QRS | "Totally chaotic" |
| Atrial Flutter | Sawtooth waves, HR 150/min (2:1) | Exactly 150 = think Flutter |
| VT | Wide QRS >100/min, AV dissociation | Fusion beats = diagnostic |
| VF | Completely chaotic - no identifiable waves | DEFIBRILLATE NOW! |
| WPW | Short PR + Delta wave + Wide QRS | Pre-excitation triad |
| LBBB | W in V1, M in V5/V6, QRS >120ms | WiLLiaM |
| RBBB | RSR' (bunny ears) V1, Wide S V6 | MaRRoW |
| LVH | S-V1 + R-V5 ≥ 35mm + strain pattern | Sokolow-Lyon criterion |
| RVH | Dominant R in V1, deep S V6, RAD | R in V1 = RVH |
| Pericarditis | Diffuse concave ST↑ + PR depression | Saddle + PR down |
| PE | S1Q3T3 + sinus tachycardia + new RBBB | McGinn-White sign |
| Hyperkalemia | Peaked T → sine wave | Tent T, then chaos |
| Hypokalemia | Prominent U wave, flat T | U = hYpOkalemia |
| Long QT | QTc >450 ms | TdP risk → avoid QT-prolonging drugs |
| Brugada | Coved ST↑ V1-V2 + RBBB | Nocturnal VF |
| Hypothermia | Osborn J wave + bradycardia | J = hypothermia hump |
| Digoxin toxicity | Scooped ST + AV block + bidirectional VT | Salvador Dali mustache |
| Cardiac Tamponade | Electrical alternans + low voltage | Heart swinging in fluid |
| Diagram | Disease | Link |
|---|---|---|
| 1 | Normal ECG (labeled) | 01 normal ecg Image · PNG |
| 2 | Inferior STEMI | 02 inferior stemi Image · PNG |
| 3 | Anterior STEMI (3 stages) | 03 anterior stemi Image · PNG |
| 4 | AV Blocks (all 4 types) | 04 av blocks Image · PNG |
| 5 | AF vs Atrial Flutter | 05 af flutter Image · PNG |
| 6 | VT & VF | 06 vt vf Image · PNG |
| 7 | LBBB & RBBB | 07 bbb Image · PNG |
| 8 | WPW Syndrome | 08 wpw Image · PNG |
| 9 | Pericarditis vs STEMI | 09 pericarditis Image · PNG |
| 10 | Electrolyte disturbances | 10 electrolytes Image · PNG |
| 11 | LVH & RVH | 11 hypertrophy Image · PNG |
| 12 | Long QT, Brugada, Hypothermia | 12 special patterns Image · PNG |
| Saare 12 diagrams ek PDF mein | ECG Complete Guide Hinglish PDF Document · PDF |