1414and available here:
1515http://www.mathworks.com/matlabcentral/fileexchange/22114-abf2load
1616
17+
18+ The StringsSection parsing (parse_axon_soup) now relies on an idea
19+ presented in pyABF MIT License Copyright (c) 2018 Scott W Harden
20+ written by Scott Harden. His unofficial documentation for the formats
21+ is here:
22+ https://swharden.com/pyabf/abf2-file-format/
23+ strings section:
24+ [uModifierNameIndex, uCreatorNameIndex, uProtocolPathIndex, lFileComment, lADCCChannelNames, lADCUnitsIndex
25+ lDACChannelNameIndex, lDACUnitIndex, lDACFilePath, nLeakSubtractADC]
26+ ['', 'Clampex', '', 'C:/path/protocol.pro', 'some comment', 'IN 0', 'mV', 'IN 1', 'mV', 'Cmd 0', 'pA',
27+ 'Cmd 1', 'pA', 'Cmd 2', 'mV', 'Cmd 3', 'mV']
28+
1729Information on abf 1 and 2 formats is available here:
1830http://www.moleculardevices.com/pages/software/developer_info.html
1931
@@ -461,21 +473,21 @@ def parse_axon_soup(filename):
461473 # strings sections
462474 # hack for reading channels names and units
463475 # this section is not very detailed and so the code
464- # not very robust. The idea is to remove the first
465- # part by finding one of th following KEY
466- # unfortunately the later part contains a the file
467- # that can contain by accident also one of theses keys...
476+ # not very robust.
468477 f .seek (sections ["StringsSection" ]["uBlockIndex" ] * BLOCKSIZE )
469478 big_string = f .read (sections ["StringsSection" ]["uBytes" ])
470- goodstart = - 1
471- for key in [b"AXENGN" , b"clampex" , b"Clampex" , b"EDR3" , b"CLAMPEX" , b"axoscope" , b"AxoScope" , b"Clampfit" ]:
472- # goodstart = big_string.lower().find(key)
473- goodstart = big_string .find (b"\x00 " + key )
474- if goodstart != - 1 :
475- break
476- assert goodstart != - 1 , "This file does not contain clampex, axoscope or clampfit in the header"
477- big_string = big_string [goodstart + 1 :]
478- strings = big_string .split (b"\x00 " )
479+ # this idea comes from pyABF https://github.com/swharden/pyABF
480+ # previously we searched for clampex, Clampex etc, but this was
481+ # brittle. pyABF believes that looking for the \x00\x00 is more
482+ # robust. We find these values, replace mu->u, then split into
483+ # a set of strings
484+ indexed_string = big_string [big_string .rfind (b'\x00 \x00 ' ):]
485+ # replace mu -> u for easy display
486+ indexed_string = indexed_string .replace (b'\xb5 ' , b'\x75 ' )
487+ # we need to remove one of the \x00 to have the indices be
488+ # the correct order
489+ indexed_string = indexed_string .split (b'\x00 ' )[1 :]
490+ strings = indexed_string
479491
480492 # ADC sections
481493 header ["listADCInfo" ] = []
@@ -489,8 +501,8 @@ def parse_axon_soup(filename):
489501 ADCInfo [key ] = val [0 ]
490502 else :
491503 ADCInfo [key ] = np .array (val )
492- ADCInfo ["ADCChNames" ] = strings [ADCInfo ["lADCChannelNameIndex" ] - 1 ]
493- ADCInfo ["ADCChUnits" ] = strings [ADCInfo ["lADCUnitsIndex" ] - 1 ]
504+ ADCInfo ["ADCChNames" ] = strings [ADCInfo ["lADCChannelNameIndex" ]]
505+ ADCInfo ["ADCChUnits" ] = strings [ADCInfo ["lADCUnitsIndex" ]]
494506 header ["listADCInfo" ].append (ADCInfo )
495507
496508 # protocol sections
@@ -503,7 +515,7 @@ def parse_axon_soup(filename):
503515 else :
504516 protocol [key ] = np .array (val )
505517 header ["protocol" ] = protocol
506- header ["sProtocolPath" ] = strings [header ["uProtocolPathIndex" ] - 1 ]
518+ header ["sProtocolPath" ] = strings [header ["uProtocolPathIndex" ]]
507519
508520 # tags
509521 listTag = []
@@ -532,8 +544,8 @@ def parse_axon_soup(filename):
532544 DACInfo [key ] = val [0 ]
533545 else :
534546 DACInfo [key ] = np .array (val )
535- DACInfo ["DACChNames" ] = strings [DACInfo ["lDACChannelNameIndex" ] - 1 ]
536- DACInfo ["DACChUnits" ] = strings [DACInfo ["lDACChannelUnitsIndex" ] - 1 ]
547+ DACInfo ["DACChNames" ] = strings [DACInfo ["lDACChannelNameIndex" ]]
548+ DACInfo ["DACChUnits" ] = strings [DACInfo ["lDACChannelUnitsIndex" ]]
537549
538550 header ["listDACInfo" ].append (DACInfo )
539551
0 commit comments