@@ -1349,7 +1349,8 @@ def check_np_array(item, field_name, ndim, parent_class, channel_num=None):
13491349 raise TypeError (error_msg )
13501350
13511351
1352- def edf2mit (record_name , pn_dir = None , delete_file = True , record_only = False ):
1352+ def edf2mit (record_name , pn_dir = None , delete_file = True , record_only = False ,
1353+ header_only = False , verbose = False ):
13531354 """
13541355 Convert EDF formatted files to MIT format.
13551356
@@ -1384,6 +1385,13 @@ def edf2mit(record_name, pn_dir=None, delete_file=True, record_only=False):
13841385 record_only : bool, optional
13851386 Whether to only return the record information (True) or not (False).
13861387 If false, this function will generate both a .dat and .hea file.
1388+ header_only : bool, optional
1389+ Whether to only return the header information (True) or not (False).
1390+ If true, this function will only return `['fs', 'sig_len', 'n_sig',
1391+ 'base_date', 'base_time', 'units', 'sig_name', 'comments']`.
1392+ verbose : bool, optional
1393+ Whether to print all the information read about the file (True) or
1394+ not (False).
13871395
13881396 Returns
13891397 -------
@@ -1412,6 +1420,186 @@ def edf2mit(record_name, pn_dir=None, delete_file=True, record_only=False):
14121420 r = requests .get (file_url , allow_redirects = False )
14131421 open (record_name , 'wb' ).write (r .content )
14141422
1423+ # Temporary to return only the EDF header.. will later replace the
1424+ # current MNE package approach
1425+ if header_only :
1426+ # Open the desired file
1427+ edf_file = open (record_name , mode = 'rb' )
1428+
1429+ # Remove the file if the `delete_file` flag is set
1430+ if pn_dir is not None and delete_file :
1431+ os .remove (record_name )
1432+
1433+ # Version of this data format (8 bytes)
1434+ version = struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()
1435+
1436+ # Check to see that the input is an EDF file. (This check will detect
1437+ # most but not all other types of files.)
1438+ if version != '0 ' :
1439+ raise Exception ('Input does not appear to be EDF -- no conversion attempted' )
1440+ else :
1441+ if verbose :
1442+ print ('EDF version number: {}' .format (version .strip ()))
1443+
1444+ # Local patient identification (80 bytes)
1445+ patient_id = struct .unpack ('<80s' , edf_file .read (80 ))[0 ].decode ()
1446+ if verbose :
1447+ print ('Patient ID: {}' .format (patient_id ))
1448+
1449+ # Local recording identification (80 bytes)
1450+ # Bob Kemp recommends using this field to encode the start date
1451+ # including an abbreviated month name in English and a full (4-digit)
1452+ # year, as is done here if this information is available in the input
1453+ # record. EDF+ requires this.
1454+ record_id = struct .unpack ('<80s' , edf_file .read (80 ))[0 ].decode ()
1455+ if verbose :
1456+ print ('Recording ID: {}' .format (record_id ))
1457+
1458+ # Start date of recording (dd.mm.yy) (8 bytes)
1459+ start_date = struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()
1460+ if verbose :
1461+ print ('Recording Date: {}' .format (start_date ))
1462+ start_day , start_month , start_year = [int (i ) for i in start_date .split ('.' )]
1463+ # This should work for a while
1464+ if start_year < 1970 :
1465+ start_year += 1900
1466+ if start_year < 1970 :
1467+ start_year += 100
1468+
1469+ # Start time of recording (hh.mm.ss) (8 bytes)
1470+ start_time = struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()
1471+ if verbose :
1472+ print ('Recording Time: {}' .format (start_time ))
1473+ start_hour , start_minute , start_second = [int (i ) for i in start_time .split ('.' )]
1474+
1475+ # Number of bytes in header (8 bytes)
1476+ header_bytes = int (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ())
1477+ if verbose :
1478+ print ('Number of bytes in header record: {}' .format (header_bytes ))
1479+
1480+ # Reserved (44 bytes)
1481+ reserved_notes = struct .unpack ('<44s' , edf_file .read (44 ))[0 ].decode ().strip ()
1482+ if reserved_notes != '' :
1483+ if verbose :
1484+ print ('Free Space: {}' .format (reserved_notes ))
1485+
1486+ # Number of blocks (-1 if unknown) (8 bytes)
1487+ num_blocks = int (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ())
1488+ if verbose :
1489+ print ('Number of data records: {}' .format (num_blocks ))
1490+
1491+ # Duration of a block, in seconds (8 bytes)
1492+ block_duration = float (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ())
1493+ if verbose :
1494+ print ('Duration of each data record in seconds: {}' .format (block_duration ))
1495+ if block_duration <= 0.0 :
1496+ block_duration = 1.0
1497+
1498+ # Number of signals (4 bytes)
1499+ n_sig = int (struct .unpack ('<4s' , edf_file .read (4 ))[0 ].decode ())
1500+ if verbose :
1501+ print ('Number of signals: {}' .format (n_sig ))
1502+ if n_sig < 1 :
1503+ raise Exception ('Done: not any signals left to read' )
1504+
1505+ # Label (e.g., EEG FpzCz or Body temp) (16 bytes each)
1506+ sig_labels = []
1507+ for _ in range (n_sig ):
1508+ sig_labels .append (struct .unpack ('<16s' , edf_file .read (16 ))[0 ].decode ().strip ())
1509+ if verbose :
1510+ print ('Signal Labels: {}' .format (sig_labels ))
1511+
1512+ # Transducer type (e.g., AgAgCl electrode) (80 bytes each)
1513+ transducer_types = []
1514+ for _ in range (n_sig ):
1515+ transducer_types .append (struct .unpack ('<80s' , edf_file .read (80 ))[0 ].decode ().strip ())
1516+ if verbose :
1517+ print ('Transducer Types: {}' .format (transducer_types ))
1518+
1519+ # Physical dimension (e.g., uV or degreeC) (8 bytes each)
1520+ physical_dims = []
1521+ for _ in range (n_sig ):
1522+ physical_dims .append (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ().strip ())
1523+ if verbose :
1524+ print ('Physical Dimensions: {}' .format (physical_dims ))
1525+
1526+ # Physical minimum (e.g., -500 or 34) (8 bytes each)
1527+ physical_min = np .array ([])
1528+ for _ in range (n_sig ):
1529+ physical_min = np .append (physical_min , float (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()))
1530+ if verbose :
1531+ print ('Physical Minimums: {}' .format (physical_min ))
1532+
1533+ # Physical maximum (e.g., 500 or 40) (8 bytes each)
1534+ physical_max = np .array ([])
1535+ for _ in range (n_sig ):
1536+ physical_max = np .append (physical_max , float (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()))
1537+ if verbose :
1538+ print ('Physical Maximums: {}' .format (physical_max ))
1539+
1540+ # Digital minimum (e.g., -2048) (8 bytes each)
1541+ digital_min = np .array ([])
1542+ for _ in range (n_sig ):
1543+ digital_min = np .append (digital_min , float (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()))
1544+ if verbose :
1545+ print ('Digital Minimums: {}' .format (digital_min ))
1546+
1547+ # Digital maximum (e.g., 2047) (8 bytes each)
1548+ digital_max = np .array ([])
1549+ for _ in range (n_sig ):
1550+ digital_max = np .append (digital_max , float (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()))
1551+ if verbose :
1552+ print ('Digital Maximums: {}' .format (digital_max ))
1553+
1554+ # Prefiltering (e.g., HP:0.1Hz LP:75Hz) (80 bytes each)
1555+ prefilter_info = []
1556+ for _ in range (n_sig ):
1557+ prefilter_info .append (struct .unpack ('<80s' , edf_file .read (80 ))[0 ].decode ().strip ())
1558+ if verbose :
1559+ print ('Prefiltering Information: {}' .format (prefilter_info ))
1560+
1561+ # Number of samples per block (8 bytes each)
1562+ samps_per_block = []
1563+ for _ in range (n_sig ):
1564+ samps_per_block .append (int (struct .unpack ('<8s' , edf_file .read (8 ))[0 ].decode ()))
1565+ if verbose :
1566+ print ('Number of Samples per Record: {}' .format (samps_per_block ))
1567+
1568+ # The last 32*nsig bytes in the header are unused
1569+ for _ in range (n_sig ):
1570+ struct .unpack ('<32s' , edf_file .read (32 ))[0 ].decode ()
1571+
1572+ # Pre-process the acquired data before creating the record
1573+ sample_rate = [int (i / block_duration ) for i in samps_per_block ]
1574+ fs = functools .reduce (math .gcd , sample_rate )
1575+ sig_len = int (num_blocks * block_duration * fs )
1576+ base_time = datetime .time (start_hour , start_minute , start_second )
1577+ base_date = datetime .date (start_year , start_month , start_day )
1578+ comments = []
1579+
1580+ units = n_sig * ['' ]
1581+ for i ,f in enumerate (physical_dims ):
1582+ if f == 'n/a' :
1583+ label = sig_labels [i ].lower ().split ()[0 ]
1584+ if label in list (SIG_UNITS .keys ()):
1585+ units [i ] = SIG_UNITS [label ]
1586+ else :
1587+ units [i ] = 'n/a'
1588+ else :
1589+ f = f .replace ('µ' ,'u' ) # Maybe more weird symbols to check for?
1590+ units [i ] = f
1591+
1592+ return {
1593+ 'fs' : fs ,
1594+ 'sig_len' : sig_len ,
1595+ 'n_sig' : n_sig ,
1596+ 'base_date' : base_date ,
1597+ 'base_time' : base_time ,
1598+ 'units' : physical_dims ,
1599+ 'sig_name' : sig_labels ,
1600+ 'comments' : comments
1601+ }
1602+
14151603 edf_data = mne .io .read_raw_edf (record_name , preload = True )
14161604
14171605 if pn_dir is not None and delete_file :
0 commit comments