3
3
import re
4
4
import os
5
5
import copy
6
+ from . import records
6
7
from . import _headers
7
8
from . import downloads
8
9
@@ -79,11 +80,16 @@ def __eq__(self, other):
79
80
return True
80
81
81
82
# Write an annotation file
82
- def wrann (self ):
83
+ def wrann (self , writefs = False ):
83
84
"""
84
85
Instance method to write a WFDB annotation file from an Annotation object.
86
+
87
+ def wrann(self, writefs=False)
88
+
89
+ Input Parameters:
90
+ - writefs (default=False): Flag specifying whether to write the fs
91
+ attribute to the file.
85
92
86
- Example usage:
87
93
"""
88
94
# Check the validity of individual fields used to write the annotation file
89
95
self .checkfields ()
@@ -92,7 +98,7 @@ def wrann(self):
92
98
self .checkfieldcohesion ()
93
99
94
100
# Write the header file using the specified fields
95
- self .wrannfile ()
101
+ self .wrannfile (writefs )
96
102
97
103
# Check the mandatory and set fields of the annotation object
98
104
# Return indices of anntype field which are not encoded, and thus need
@@ -222,10 +228,10 @@ def checkfieldcohesion(self):
222
228
raise ValueError ("All written annotation fields: ['annsamp', 'anntype', 'num', 'subtype', 'chan', 'aux'] must have the same length" )
223
229
224
230
# Write an annotation file
225
- def wrannfile (self ):
231
+ def wrannfile (self , writefs ):
226
232
227
- # Calculate the fs bytes to write if present
228
- if self .fs is not None :
233
+ # Calculate the fs bytes to write if present and desired to write
234
+ if self .fs is not None and writefs :
229
235
fsbytes = fs2bytes (self .fs )
230
236
else :
231
237
fsbytes = []
@@ -359,19 +365,31 @@ def type2aux(self):
359
365
360
366
# Calculate the bytes written to the annotation file for the fs field
361
367
def fs2bytes (fs ):
362
- databytes = [0 ,88 ,23 , 252 ,35 ,35 ,32 ,116 ,105 ,109 ,101 ,32 ,114 ,101 ,115 ,111 ,108 ,117 ,116 ,105 ,111 ,110 ,58 ,32 ]
368
+
369
+ # Initial indicators of encoding fs
370
+ databytes = [0 ,88 , None , 252 ,35 ,35 ,32 ,116 ,105 ,109 ,101 ,32 ,114 ,101 ,115 ,111 ,108 ,117 ,116 ,105 ,111 ,110 ,58 ,32 ]
371
+
372
+ # Be aware of potential float and int
373
+
374
+ # Check if fs is close enough to int
375
+ if type (fs ) == float :
376
+ if round (fs ,8 ) == float (int (fs )):
377
+ fs = int (fs )
363
378
364
379
fschars = str (fs )
365
380
ndigits = len (fschars )
366
381
367
- for i in range (0 , ndigits ):
382
+ for i in range (ndigits ):
368
383
databytes .append (ord (fschars [i ]))
369
384
385
+ # Fill in the aux length
386
+ databytes [2 ] = ndigits + 20
387
+
370
388
# odd number of digits
371
389
if ndigits % 2 :
372
390
databytes .append (0 )
373
391
374
- # Add the extra -1 0 filler
392
+ # Add the extra -1 0 notqrs filler
375
393
databytes = databytes + [0 , 236 , 255 , 255 , 255 , 255 , 1 , 0 ]
376
394
377
395
return np .array (databytes ).astype ('u1' )
@@ -553,7 +571,7 @@ def wrann(recordname, annotator, annsamp, anntype, subtype = None, chan = None,
553
571
# Create Annotation object
554
572
annotation = Annotation (recordname , annotator , annsamp , anntype , num , subtype , chan , aux , fs )
555
573
# Perform field checks and write the annotation file
556
- annotation .wrann ()
574
+ annotation .wrann (writefs = True )
557
575
558
576
# Display the annotation symbols and the codes they represent
559
577
def showanncodes ():
@@ -619,8 +637,6 @@ def rdann(recordname, annotator, sampfrom=0, sampto=None, pbdir=None):
619
637
# bpi = byte pair index. The index at which to continue processing the bytes
620
638
fs , bpi = get_fs (filebytes )
621
639
622
-
623
-
624
640
# Get the main annotation fields from the annotation bytes
625
641
annsamp ,anntype ,num ,subtype ,chan ,aux ,ai = proc_ann_bytes (annsamp ,anntype ,num ,
626
642
subtype ,chan ,aux ,
@@ -641,6 +657,17 @@ def rdann(recordname, annotator, sampfrom=0, sampto=None, pbdir=None):
641
657
# Set the annotation type to the annotation codes
642
658
anntype = [allannsyms [code ] for code in anntype ]
643
659
660
+
661
+ # If the fs field was not present in the file, try to read a wfdb header
662
+ # the annotation is associated with to get the fs
663
+ if fs is None :
664
+ # Use try except to not make rdann dependent on the validity of the header
665
+ try :
666
+ rec = records .rdheader (recordname , pbdir )
667
+ fs = rec .fs
668
+ except :
669
+ pass
670
+
644
671
# Store fields in an Annotation object
645
672
annotation = Annotation (os .path .split (recordname )[1 ], annotator , annsamp , anntype ,
646
673
subtype , chan , num , aux , fs ,custom_anntypes )
@@ -689,7 +716,10 @@ def get_fs(filebytes):
689
716
# the file.
690
717
auxlen = testbytes [2 ]
691
718
testbytes = filebytes [:(12 + int (np .ceil (auxlen / 2. ))), :].flatten ()
692
- fs = int ("" .join ([chr (char ) for char in testbytes [24 :auxlen + 4 ]]))
719
+ fs = float ("" .join ([chr (char ) for char in testbytes [24 :auxlen + 4 ]]))
720
+ # fs may be int
721
+ if round (fs , 8 ) == float (int (fs )):
722
+ fs = int (fs )
693
723
# byte pair index to start reading actual annotations.
694
724
bpi = int (0.5 * (auxlen + 12 + (auxlen & 1 )))
695
725
return (fs , bpi )
0 commit comments