@@ -1349,7 +1349,8 @@ def check_np_array(item, field_name, ndim, parent_class, channel_num=None):
1349
1349
raise TypeError (error_msg )
1350
1350
1351
1351
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 ):
1353
1354
"""
1354
1355
Convert EDF formatted files to MIT format.
1355
1356
@@ -1384,6 +1385,13 @@ def edf2mit(record_name, pn_dir=None, delete_file=True, record_only=False):
1384
1385
record_only : bool, optional
1385
1386
Whether to only return the record information (True) or not (False).
1386
1387
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).
1387
1395
1388
1396
Returns
1389
1397
-------
@@ -1412,6 +1420,186 @@ def edf2mit(record_name, pn_dir=None, delete_file=True, record_only=False):
1412
1420
r = requests .get (file_url , allow_redirects = False )
1413
1421
open (record_name , 'wb' ).write (r .content )
1414
1422
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
+
1415
1603
edf_data = mne .io .read_raw_edf (record_name , preload = True )
1416
1604
1417
1605
if pn_dir is not None and delete_file :
0 commit comments