539 lines (489 with data), 10.5 kB
#! /bin/sh
# get_hrv Joe Mietus Apr 1 2011
# get_hrv Joe Mietus Nov 9 2010
USAGE="$0 [options] -R rrfile | record annotator [start [end]]
Get HRV statistics :
REC : NN/RR AVNN SDNN SDANN SDNNINDX RMSSD PNN : TOTPWR ULF VLF LF HF LF/HF
options :
[-R rrfile] : RR interval file : time (sec), interval
[-f \"filt hwin\"] : filter outliers
[-p \"nndiff ...\"] : nn difference for pnn (default: 50 msec)
[-P \"lo1 hi1 lo2 hi2 lo3 hi3 lo4 hi4\"] : power bands
(default : 0 0.0033 0.0033 0.04 0.04 0.15 0.15 0.4)
[-s] : short term stats of
REC : NN/RR AVNN SDNN RMSSD PNN : TOTPWR VLF LF HF LF/HF
[-I c|h|m] : input time format: hh::mm:ss, hours, minutes (default: seconds)
[-m] : RR intervals in msec
[-M] : output statistics in msec rather than sec
[-L] : output statistics on one line
[-S] : plot HRV results on screen
plotting options :
[-F \"filt hwin\"] : filter outliers, plot filtered data
[-y \"ymin ymax\"] : time series y-axis limits (\"- -\" for self-scaling)
[-X maxfreq] : fft maximum frequency (default : 0.4 Hz)
[-Y fftmax] : fft maximum (\"-\" for self-scaling)
[-o] : output plot in postscript
"
while getopts R:f:p:P:sI:mtMLSF:y:X:Y:o c
do
case $c in
R) RRFILE=$OPTARG ;;
f) FILT=$OPTARG ;;
p) PFLAG="-p $OPTARG" ;;
P) PWRBANDS=$OPTARG ;;
s) SFLAG='-s' ;;
I) TIME=$OPTARG ;;
m) MFLAG=1 ;;
M) MSEC='-m' ;;
L) SLINE=1 ;;
S) SCREENPLOT=1 ;;
F) FILT=$OPTARG
PLTFILT=1 ;;
y) Y0LIMS=$OPTARG
if test `echo $Y0LIMS | wc -w` -ne 2
then
echo "$0 : [-y \"ymin ymax\"]"
exit 1
fi ;;
X) X1MAX=$OPTARG ;;
Y) Y1MAX=$OPTARG ;;
o) SCREENPLOT=1
PS=-ps ;;
\?) echo "$USAGE"
exit 1 ;;
esac
done
shift `expr $OPTIND - 1`
if test "$RRFILE"
then
if test ! -r "$RRFILE"
then
echo "$0 : Can't open $RRFILE"
exit 1
fi
NFLAG=1
REC=$1
ANN=$2
fi
START=$1
END=$2
if test ! "$START" -o "$START" = "-"
then
START=00:00:00
fi
STARTSEC=`seconds $START`
if test "$STARTSEC" -eq -1
then
echo "$0 : bad start time : $START"
exit 1
fi
START=`hours $STARTSEC`
if test "$END"
then
ENDSEC=`seconds $END`
if test $STARTSEC -gt $ENDSEC
then
echo "$0: start time greater than end time"
exit 1
fi
fi
if test ! "$PWRBANDS"
then
if test -n "$SFLAG"
then
LO1=0
HI1=0.04
LO2=0.04
HI2=0.15
LO3=0.15
HI3=0.4
else
LO1=0
HI1=0.0033
LO2=0.0033
HI2=0.04
LO3=0.04
HI3=0.15
LO4=0.15
HI4=0.4
fi
PWRBANDS="$LO1 $HI1 $LO2 $HI2 $LO3 $HI3 $LO4 $HI4"
elif test "$PWRBANDS" != 0
then
if test `echo $PWRBANDS | wc -w | awk '{print $1%2}'` -ne 0
then
echo "$0 : [-P \"lo hi [...]\"]"
exit 1
fi
fi
FMAX=`echo $PWRBANDS | tr ' ' '\n' | sort -n | tail -1`
(
if test "$RRFILE"
then
cat $RRFILE |
(
if test "$TFLAG"
then awk '{T+=$1}; {printf "%.3f %s\n", T, $0}'
else cat
fi
) |
(
if test "$MFLAG" -a "$TFLAG"
then awk '{$1/=1000; $2/=1000; print}'
elif test "$MFLAG"
then awk '{$2/=1000; print}'
else cat
fi
) |
(
if test "$TIME" = c
then
sed 's/[0-9:.][0-9:.]* /&: /' |
awk -F: 'NF==2 {print $1, $2}
NF==3 {print $1*60+$2, $3}
NF==4 {print $1*3600+$2*60+$3, $4}'
elif test "$TIME" = h
then
awk '{$1*=3600; print}'
elif test "$TIME" = m
then
awk '{$1*=60; print}'
else
cat
fi
) |
(
if test "$END"
then
awk "\$1>=$STARTSEC && \$1<=$ENDSEC && \$2!=0 {print}
\$1>$ENDSEC {exit}"
else
awk "\$1>=$STARTSEC && \$2!=0 {print}"
fi
) |
(
if test "$NFLAG"
then
awk '{print $1, $2, "N"}'
else
cat
fi
)
else
rrlist $ANN $REC -f $START ${END:+-t} $END -s
fi
) >foo.rr
cat foo.rr |
(
if test "$FILT"
then
filtnn $FILT -n 2>foo.nrr | sort -k 1n
else
awk '{RR++}
LAST=="N" && $3=="N" {NN++}
{print; LAST=$3}
END {printf "NN : RR = %d : %d = %f\n", NN, RR, NN/RR >"foo.nrr"}'
fi
) |
(
if test "$MSEC"
then
awk '{$2*=1000; print}'
else
cat
fi
) |
awk 'NR==1 {T0=$1}
{print $1-T0, $2, $3}' >foo.frr
cat foo.frr |
statnn $SFLAG $MSEC $PFLAG >foo.nnstat
NNSTATS=`cat foo.nnstat`
cat foo.frr |
awk 'LAST=="N" && $3=="N" {print $1, $2}; {LAST=$3}' |
tee foo.nn |
lomb - |
tee foo.fft |
awk "\$1<=$FMAX {print}" |
pwr $PWRBANDS |
sed 's/Total/TOT PWR/
s/0 - 0.0033/ULF PWR/
s/0.0033 - 0.04/VLF PWR/
s/0 - 0.04/VLF PWR/
s/0.04 - 0.15/LF PWR/
s/0.15 - 0.4/HF PWR/' |
awk '{print}
$1=="LF" {LF=$4}
$1=="HF" {HF=$4}
END {printf "LF/HF = %g\n", LF/HF}' >foo.pwr
NNPWRS=`cat foo.pwr | sed 's/ */ /'`
if test ! "$PS"
then
(
echo "${REC:-$RRFILE} :"
cat foo.nnstat | awk -F= '{printf "%-8s = %g\n", $1, $2}'
cat foo.pwr | awk -F= '{printf "%-8s = %g\n", $1, $2}'
) |
(
if test "$SLINE"
then
sed '/TOT.*/i\
:
s/.*= //' |
tr '\n' ' ' | awk '{print}' | sed 's/ */ /g'
else
cat
fi
)
fi
if test ! "$SCREENPLOT"
then
rm -f foo.rr foo.frr foo.nn foo.nrr foo.nnstat foo.fft foo.pwr foo.ab
exit
fi
##################################################################################
if test "$PS"
then
PTERM=lw
export PTERM
else
xpltwin -g 720x940
fi
RATIO=`cat foo.nrr |
awk '{printf "Filt : NN : RR = %d : %d : %d = %.3f : %.3f = %.3f\n", \
$7, $9, $11, $13, $15, $17}'`
EXCLUDED=`cat foo.nrr |
awk 'NF==17 {printf "[%d Filtered, %d non-NN]\n", $9-$7, $11-$9}
NF==9 {printf "[%d non-NN]\n", $7-$5}'`
PROG="NN interval"
if test "$FILT"
then PROG="$PROG, filt $FILT"
fi
PROG="$PROG, lomb"
TITLE="${RRFILE:-$REC $ANN} ($START)"
(
plt -wm 0 : -F"
t
L (P14) 0.5 0.99 CC $TITLE
L (P12) 0.5 0.96 CC ($PROG)
L (P12) 0.5 0.93 CC $RATIO $EXCLUDED
s f"
cat foo.rr |
(
if test "$FILT"
then
filtnn $FILT -p | sort -k 1n
else
cat
fi
) |
(
if test "$MSEC"
then
awk '{$2*=1000; print}'
else
cat
fi
) |
(
if test -z "$ZFLAG"
then
awk 'NR==1 {T0=$1}
{print $1-T0, $2, $3}'
else
cat
fi
) >foo.frr
X0MIN=`head -1 foo.frr | awk '{print $1/60}'`
X0MAX=`tail -1 foo.frr | awk '{print $1/60}'`
if test `expr "$X0MAX" : '\(.*\)\..*'` -ge 120
then
X0MAX=`echo $X0MAX | awk '{print $1/60}'`
TUNIT=hr
else
TUNIT=min
fi
if test "$MSEC"
then
if test ! "$Y0LIMS"
then
Y0LIMS="0 2000"
fi
IUNIT="msec"
PNN="%"
PUNIT="msec2"
cat foo.nnstat |
sed "s/^[^p].*NN.*/& $IUNIT/
s/rMSSD.*/& $IUNIT/
s/pNN.*/& $PNN/" |
awk 'NR==1 {printf "%s %s %.3f %s\n", $1, $2, $3, $4}
NR>1 {printf "%s %s %.2f %s\n", $1, $2, $3, $4}' >foo.nnstat.hl
cat foo.pwr |
sed "s/.*PWR.*/& $PUNIT/" |
awk 'NF==5 {printf "%s %s %s %.2f %s\n", $1, $2, $3, $4, $5}
NF==3 {printf "%s %s %.4f\n", $1, $2, $3}' >foo.pwr.hl
else
if test ! "$Y0LIMS"
then
Y0LIMS="0 2.0"
fi
IUNIT="sec"
PNN=""
PUNIT="sec2"
cat foo.nnstat |
sed "s/^[^p].*NN.*/& $IUNIT/
s/rMSSD.*/& $IUNIT/
s/pNN.*/& $PNN/" |
awk 'NR==1 {printf "%s %s %.3f %s\n", $1, $2, $3, $4}
NR>1 {printf "%s %s %.4f %s\n", $1, $2, $3, $4}' >foo.nnstat.hl
cat foo.pwr |
sed "s/.*PWR.*/& $PUNIT/" |
awk 'NF==5 {printf "%s %s %s %.6f %s\n", $1, $2, $3, $4, $5}
NF==3 {printf "%s %s %.4f\n", $1, $2, $3}' >foo.pwr.hl
fi
TLINES=`wc foo.nnstat.hl | awk '{print $1}'`
FLINES=`wc foo.pwr.hl | awk '{print $1}'`
Y0MIN=`expr "$Y0LIMS" : '\([^ ]*\)'`
Y0MAX=`expr "$Y0LIMS" : '.* \(.*\)'`
cat foo.frr |
(
if test "$PLTFILT"
then
cat
else
awk 'LAST=="N" && $3=="N" {print}; {LAST=$3}'
fi
) |
(
if test "$Y0MIN" = "-"
then
if test "$Y0MAX" = "-"
then
awk "{\$1/=60; print}"
else
awk "{if(\$2>$Y0MAX) \$2=$Y0MAX;
\$1/=60; print}"
fi
else
if test "$Y0MAX" = "-"
then
awk "{if(\$2<$Y0MIN) \$2=$Y0MIN;
\$1/=60; print}"
else
awk "{if(\$2<$Y0MIN) \$2=$Y0MIN;
if(\$2>$Y0MAX) \$2=$Y0MAX;
\$1/=60; print}"
fi
fi
) |
(
if test $TUNIT = hr
then
awk '{$1/=60; print}'
else
cat
fi
) >foo.frr1
mv foo.frr1 foo.frr
cat foo.frr |
plt 0 1 -wms 1 -F"
W 0.10 0.69 0.95 0.87
sf all P13
t
x Time ($TUNIT)
y NN Interval ($IUNIT)
xa $X0MIN $X0MAX
ya $Y0MIN $Y0MAX
s p
fa foo.axes"
cat foo.frr |
awk 'LAST=="N" && $3=="N" {print}
{LAST=$3}' |
plt 0 1 -wms 1 -F"
sf all P13
f foo.axes"
if test "$PLTFILT"
then
cat foo.frr | sed '/N/d' >foo.exc
# plot non-normals
cat foo.exc |
sed '/X/d' |
plt 0 1 -wms 1 -F"
sf all P12
lp 0.2 1.23 0
le 0 0
L 0.19 1.14 LC Non-normal
f foo.axes
o
p 0,1S0(P6)" 2>/dev/null
# plot filtered beats
cat foo.exc |
sed -n '/X/p' |
plt 0 1 -wms 1 -F"
sf all P12
lp 0.7 1.23 0
le 0 0
L 0.69 1.14 LC Outliers
f foo.axes
o
p 0,1S5(P6)" 2>/dev/null
rm foo.exc
fi
# NN interval histogram
N=`wc foo.nn | awk '{print $1}'`
cat foo.nn | awk '{print $2}' | sort | uniq -c |
awk "{print \$1/$N, \$2}" |
plt % -wms 1 -F"
W 0.1 0.38 0.4 0.57
sf all P13
t NN Interval Histogram
x NN interval ($IUNIT)
y Probability
p 1,0i"
# Power spectrum
if test ! "$X1MAX"
then
X1MAX=$FMAX
elif test "$X1MAX" = "-"
then
X1MAX=`sed -n '$p' foo.fft | awk '{print $1}'`
fi
if test ! "$Y1MAX"
then
cat foo.fft | awk "\$1<=$X1MAX {SUM+=\$2*\$2; N++}
END {printf \"%.1g\n\", 20*SUM/N}" >foo.y1max
Y1MAX=`cat foo.y1max`
rm foo.y1max
elif test "$Y1MAX" = "-"
then
cat foo.fft | awk "\$1<=$X1MAX {print \$2*\$2}" | max >foo.y1max
Y1MAX=`cat foo.y1max`
rm foo.y1max
fi
cat foo.fft |
awk '{print $1, $2*$2}' |
awk "\$1<=$X1MAX {if(\$2>$Y1MAX) \$2=$Y1MAX; print}" |
plt 0 1 -wms 1 -F"
W 0.6 0.38 0.9 0.57
sf all P13
t Lomb NN Interval Spectrum
x Frequency (Hz)
y Power ($PUNIT)
xa 0 $X1MAX
ya 0 $Y1MAX"
# HRV stats
plt : -wms 1 -F"
sf all P13
hl (P12) 0.075 -2.3 LC $TLINES
`cat foo.nnstat.hl`
hl (P12) 0.64 -2.3 LC $FLINES
`cat foo.pwr.hl`
f foo.axes"
) |
(
if test "$PTERM"
then cat | lwcat -full $PS
else cat
fi
)
rm -f foo.rr foo.frr foo.nn foo.nrr foo.nnstat foo.fft foo.pwr foo.ab
rm -f foo.axes foo.nnstat.hl foo.pwr.hl