Skip to content

Instantly share code, notes, and snippets.

@tanmaykm
Created May 19, 2016 13:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tanmaykm/e22099f2713f896bef2c46dc1d847b31 to your computer and use it in GitHub Desktop.
Save tanmaykm/e22099f2713f896bef2c46dc1d847b31 to your computer and use it in GitHub Desktop.
WFDB parser
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