Created
May 19, 2016 13:07
-
-
Save tanmaykm/e22099f2713f896bef2c46dc1d847b31 to your computer and use it in GitHub Desktop.
WFDB parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Base.Dates | |
const DEFREQ = 250. | |
const DEFGAIN = 200. | |
immutable Record | |
name::AbstractString | |
nsegments::Int | |
nsignals::Int | |
sampfreq::Float64 | |
counterfreq::Float64 | |
basecounter::Float64 | |
nsamplespersignal::Int | |
basedatetime::DateTime | |
end | |
function Record(line) | |
# regex for record line becomes more complex than useful | |
# e.g. still incomplete: ([a-zA-Z0-9_]*) (\d*) ([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\/([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?) (\d*)[ (\d*:\d*:\d*)]?[ (\d*\/\d*\/\d*)]? | |
# simple parsing seems better | |
parts = split(line) | |
nparts = length(parts) | |
# token 1: (recordname + optional nsegments) | |
if '/' in parts[1] | |
nameparts = split(parts[1], '/') | |
name = nameparts[1] | |
nsegments = parse(Int, nameparts[2]) | |
else | |
name = parts[1] | |
nsegments = 1 | |
end | |
# token 2: nsignals | |
nsignals = parse(Int, parts[2]) | |
# token 3: optional. sampling_freq/counter_freq(base_counter) | |
# both counter freq and base counter are optional | |
counterfreq = sampfreq = DEFREQ | |
basecounter = 0. | |
if nparts > 2 | |
if '(' in parts[3] | |
scbpart = split(parts[3], '(') | |
scpart = scbpart[1] | |
basecounter = parse(Float64, split(scbpart[2], ')')[1]) | |
else | |
scpart = parts[3] | |
end | |
if '/' in scpart | |
fparts = split(parts[3], '/') | |
sampfreq = parse(Float64, fparts[1]) | |
counterfreq = parse(Float64, fparts[2]) | |
(counterfreq > 0) || (counterfreq = sampfreq) | |
else | |
counterfreq = sampfreq = parse(Float64, scpart) | |
end | |
end | |
# token 4: optional. number of samples per signal | |
nsamplespersignal = 0 | |
if nparts > 3 | |
nsamplespersignal = parse(Int64, parts[4]) | |
end | |
# token 5: optional. time of day that corresponds to the beginning of the record, in HH:MM:SS format | |
datetimestr = "01/01/1970 00:00:00" | |
df = DateFormat("dd/mm/yyyy HH:MM:SS") | |
if nparts > 5 | |
datetimestr = parts[6] * " " * parts[5] | |
elseif nparts > 4 | |
datetimestr = "01/01.1970 " * parts[5] | |
end | |
basedatetime = DateTime(datetimestr, df) | |
Record(name, nsegments, nsignals, sampfreq, counterfreq, basecounter, nsamplespersignal, basedatetime) | |
end | |
immutable SignalSpec | |
file::AbstractString | |
format::Int | |
samplesperframe::Int | |
skew::Int | |
byteoffset::Int | |
adc_gain::Float64 | |
baseline::Int | |
units::AbstractString | |
adc_resolution::Int | |
adc_zero::Int | |
initial_value::Int | |
checksum::Int16 | |
block_sz::Int | |
desc::AbstractString | |
end | |
function parseprefixed{T}(::Type{T}, parts, idx, chk, default::T) | |
if length(parts) >= idx | |
(parts[idx][1] == chk) ? (parse(T, parts[idx][2:end]), (idx+1)) : (default, idx) | |
else | |
(default, idx) | |
end | |
end | |
function parsenth{T}(::Type{T}, parts, idx, default::T) | |
val = default | |
if length(parts) >= idx | |
val = parse(T, parts[idx]) | |
idx += 1 | |
end | |
val, idx | |
end | |
function SignalSpec(line) | |
parts = split(line) | |
nparts = length(parts) | |
idx = 1 | |
# token 1: the name of the file in which samples of the signal are kept | |
file = parts[idx] | |
idx += 1 | |
# token 2: this field is an integer that specifies the storage format of the signal | |
format = parse(Int, parts[idx]) | |
idx += 1 | |
# if present, this field follows an ‘x’ that serves as a field separator | |
samplesperframe, idx = parseprefixed(Int, parts, idx, 'x', 1) | |
# if present, this field follows a ‘:’ that serves as a field separator | |
skew, idx = parseprefixed(Int, parts, idx, ':', 1) | |
# if present, this field follows a ‘+’ that serves as a field separator | |
byteoffset, idx = parseprefixed(Int, parts, idx, '+', 0) | |
adc_gain = DEFGAIN | |
baseline = 0 | |
units = "" | |
if nparts >= idx | |
# floating-point number that specifies the difference in sample values that would be observed if a step of one physical unit occurred in the original analog signal | |
adcparts = split(parts[idx], r"[()/]") | |
adcparts = adcparts[!map(isempty, adcparts)] | |
adc_gain = parse(Float64, adcparts[1]) | |
if length(adcparts) > 2 | |
baseline = parse(Int, adcparts[2]) | |
units = adcparts[3] | |
else | |
if '/' in parts[idx] | |
units = adcparts[2] | |
else | |
baseline = parse(Int, adcparts[2]) | |
end | |
end | |
idx += 1 | |
end | |
# present only if the ADC gain is also present. It specifies the resolution of the analog-to-digital converter used to digitize the signal. | |
adc_resolution, idx = parsenth(Int, parts, idx, 0) | |
adc_zero, idx = parsenth(Int, parts, idx, 0) | |
initial_value, idx = parsenth(Int, parts, idx, 0) | |
checksum, idx = parsenth(Int16, parts, idx, Int16(0)) | |
block_sz, idx = parsenth(Int, parts, idx, 0) | |
desc = (nparts >= idx) ? join(parts[idx:end], " ") : "" | |
SignalSpec(file, format, samplesperframe, skew, byteoffset, adc_gain, baseline, units, adc_resolution, adc_zero, initial_value, checksum, block_sz, desc) | |
end | |
type Header | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment