diff --git a/src/types.jl b/src/types.jl index 37eeaa1..e691acf 100644 --- a/src/types.jl +++ b/src/types.jl @@ -89,12 +89,6 @@ const ANNOTATIONS_SIGNAL_LABEL = ["EDF Annotations", "BDF Annotations"] A type representing a time-stamped annotations list (TAL). -Note that this type's constructor may attempt to round given `onset_in_seconds` and -`duration_in_seconds` arguments to their nearest representable values in accordance -with the EDF+ specification, which a) represents these values as ASCII, b) constrains -these values to an 8 character limit, and c) does not allow the use of scientific -notation for these fields. - See EDF+ specification for details. # Fields @@ -107,17 +101,6 @@ struct TimestampedAnnotationList onset_in_seconds::Float64 duration_in_seconds::Union{Float64,Nothing} annotations::Vector{String} - function TimestampedAnnotationList(onset_in_seconds, duration_in_seconds, annotations) - onset_in_seconds = _nearest_representable_edf_time_value(onset_in_seconds) - duration_in_seconds = _nearest_representable_edf_time_value(duration_in_seconds) - return new(onset_in_seconds, duration_in_seconds, annotations) - end -end - -_nearest_representable_edf_time_value(::Nothing) = nothing - -function _nearest_representable_edf_time_value(x) - return round(x; digits=(8 - (ndigits(floor(Int, x)) + signbit(x) + isinteger(x)))) end function Base.:(==)(a::TimestampedAnnotationList, b::TimestampedAnnotationList) diff --git a/src/write.jl b/src/write.jl index e1e327f..89fd733 100644 --- a/src/write.jl +++ b/src/write.jl @@ -138,10 +138,10 @@ function write_tal(io::IO, tal::TimestampedAnnotationList) if !signbit(tal.onset_in_seconds) # otherwise, the `-` will already be in number string bytes_written += Base.write(io, '+') end - bytes_written += Base.write(io, _edf_repr(tal.onset_in_seconds)) + bytes_written += Base.write(io, @sprintf("%f", tal.onset_in_seconds)) if tal.duration_in_seconds !== nothing bytes_written += Base.write(io, 0x15) - bytes_written += Base.write(io, _edf_repr(tal.duration_in_seconds)) + bytes_written += Base.write(io, @sprintf("%f", tal.duration_in_seconds)) end if isempty(tal.annotations) bytes_written += Base.write(io, 0x14) diff --git a/test/runtests.jl b/test/runtests.jl index 8421181..2b44df9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -81,7 +81,7 @@ const DATADIR = joinpath(@__DIR__, "data") [TimestampedAnnotationList(4.0, nothing, String[""]), TimestampedAnnotationList(2.5, 2.5, ["type A"])], [TimestampedAnnotationList(5.0, nothing, String[""])]] @test all(signal.records .== expected) - @test AnnotationsSignal(signal.records).samples_per_record == 16 + @test AnnotationsSignal(signal.records).samples_per_record == 20 end end @@ -108,6 +108,23 @@ const DATADIR = joinpath(@__DIR__, "data") @test pyconvert(Float64, ann["duration"]) == 0.256 @test pyconvert(String, ann["description"]) == "type A" + # Modify it to have a start time with high precision + edf_high = @set edf.signals[end].records[1][2].onset_in_seconds = 1e-7 + @test edf_high.signals[end].records[1][2].onset_in_seconds == 1e-7 + py = mne_read(edf_high) + ann = py.annotations[0] + # MNE drops the precision, but doesn't error + @test pyconvert(Float64, ann["onset"]) == 0 + @test pyconvert(Float64, ann["duration"]) == 0 + + edf_high = @set edf.signals[end].records[1][2].onset_in_seconds = 0.1055043123123120 + @test edf_high.signals[end].records[1][2].onset_in_seconds == 0.1055043123123120 + py = mne_read(edf_high) + ann = py.annotations[0] + # MNE doesn't read it in with the last digit, but it also doesn't error: + @test pyconvert(Float64, ann["onset"]) == 0.105504 + @test pyconvert(Float64, ann["duration"]) == 0 + # test that EDF.write(::IO, ::EDF.File) errors if file is # discontiguous w/o an AnnotationsSignal present bad_file = EDF.File(IOBuffer(), @@ -133,13 +150,6 @@ const DATADIR = joinpath(@__DIR__, "data") @test eof(io) end - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(-0.0023405432)) == "-0.00234" - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(0.0023405432)) == "0.002340" - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(1.002343)) == "1.002343" - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(1011.05432)) == "1011.054" - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(-1011.05432)) == "-1011.05" - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(-1013441.5)) == "-1013442" - @test EDF._edf_repr(EDF._nearest_representable_edf_time_value(-1013441.3)) == "-1013441" @test EDF._edf_repr(34577777) == "34577777" @test EDF._edf_repr(0.0345) == "0.034500" @test EDF._edf_repr(-0.02) == "-0.02000" @@ -267,7 +277,6 @@ const DATADIR = joinpath(@__DIR__, "data") edf = EDF.read(joinpath(DATADIR, "test_float_extrema.edf")) @test edf.signals[1].header.digital_minimum ≈ -32767.0f0 edf = @set edf.signals[1].header.digital_minimum = -32767 * 2 - py = mne_read(edf) @test isempty(py.annotations) end