1 """
2 Reads Pulse and Patchmaster files.
3
4 Copyright 2008-2013 Research Foundation State University of New York
5 This file is part of QUB Express.
6
7 QUB Express is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 QUB Express is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License,
18 named LICENSE.txt, in the QUB Express program directory. If not, see
19 <http://www.gnu.org/licenses/>.
20
21 """
22
23 import struct
24 import sys
25
26 from qubx.util_types import Anon, memoize
27 from qubx.data_types import *
28 from qubx.fast.fast_utils import *
29 from ctypes import *
30
31 c_uint_p = POINTER(c_uint)
32 c_short_p = POINTER(c_short)
33
34 TREE_MAGIC = 0x054726565
35 DAT2_MAGIC = 0x044415432
36
37 SizeByte = 1
38 SizeChar = 1
39 SizeEnum = 1
40 SizeBoolean = 1
41 SizeInt16 = 2
42 SizeCard16 = 2
43 SizeSet16 = 2
44 SizeInt32 = 4
45 SizeCard32 = 4
46 SizeReal = 4
47 SizeLongReal = 8
48
49 String8Size = 8
50 String32Size = 32
51 String80Size = 80
52 String400Size = 400
53
54 ParamDescrSize = 24
55 SwUserParamNo = 4
56 SeUserParamNo = 4
57 GrStimParams = 10
58
59 SizeStateVersion = 8
60 SizeSerialNumber = 8
61 SizeCalibDate = 16
62
63
64 RootLevel = 0
65 GroupLevel = 1
66 SeriesLevel = 2
67 SweepLevel = 3
68 TraceLevel = 4
69
70
71 LittleEndianBit = 0
72 IsLeak = 1
73 IsVirtual = 2
74 IsImon = 3
75 IsVmon = 4
76 Clip = 5
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 ( InOut,
99 OnCell,
100 OutOut,
101 WholeCell,
102 CClamp,
103 VClamp,
104 NoMode ) = range(7)
105
106
107 ( int16,
108 int32,
109 real32,
110 real64 ) = range(4)
111
112 BothEndian = [ Anon(), Anon() ]
113
114 for iS, Sbase in enumerate([LittleEndianStructure, BigEndianStructure]):
115
118 lines = (["%s:" % self.__class__]
119 + ["%20s:\t%s" % (field, self.__getattribute__(field)) for field, typ in self._fields_])
120 return "\n".join(lines)
121
127
129 _pack_ = 1
130 _fields_ = [('oSignature', c_char*8),
131 ('oVersion', c_char*32),
132 ('oTime', c_double),
133 ('oItems', c_int),
134 ('oIsLittleEndian', c_char),
135 ('oReserved', c_char*11),
136 ('oBundleItems', 12*(c_char*16))]
137
138 BothEndian[iS].BundleHeader = BundleHeader
139
140
145
147 _pack_ = 1
148 _fields_ = [('mark', c_int),
149 ('label', c_char*String32Size),
150 ('traceCount', c_int),
151 ('data', c_int),
152 ('dataPoints', c_int),
153 ('internalSolution', c_int),
154 ('averageCount', c_int),
155 ('leakCount', c_int),
156 ('leakTraces', c_int),
157 ('dataKind', c_short),
158 ('useXStart', c_byte),
159 ('filler1', c_byte),
160 ('recordingMode', c_byte),
161 ('amplIndex', c_byte),
162 ('dataFormat', c_byte),
163 ('dataAbscissa', c_byte),
164 ('dataScaler', c_double),
165 ('timeOffset', c_double),
166 ('zeroData', c_double),
167 ('yUnit', c_char*String8Size),
168 ('xInterval', c_double),
169 ('xStart', c_double),
170 ('xUnit', c_char*String8Size),
171 ('yRange', c_double),
172 ('yOffset', c_double),
173 ('bandwidth', c_double),
174 ('pipetteResistance', c_double),
175 ('cellPotential', c_double),
176 ('sealResistance', c_double),
177 ('cSlow', c_double),
178 ('gSeries', c_double),
179 ('rsValue', c_double),
180 ('gLeak', c_double),
181 ('mConductance', c_double),
182 ('linkDAChannel', c_int),
183 ('validYrange', c_byte),
184 ('adcMode', c_byte),
185 ('adcChannel', c_short),
186 ('yMin', c_double),
187 ('yMax', c_double),
188 ('sourceChannel', c_int),
189 ('externalSolution', c_int),
190 ('CM', c_double),
191 ('GM', c_double),
192 ('phase', c_double),
193 ('dataCRC', c_int),
194 ('CRC', c_int),
195 ('GS', c_double),
196 ('selfChannel', c_int),
197 ('interleaveSize', c_int),
198 ('interleaveSkip', c_int),
199 ('imageIndex', c_int),
200 ('markers', c_double*10),
201 ('SECM_X', c_double),
202 ('SECM_Y', c_double),
203 ('SECM_Z', c_double)]
204
206 _pack_ = 1
207 _fields_ = [('mark', c_int),
208 ('label', c_char*String32Size),
209 ('auxDataFileOffset', c_int),
210 ('stimCount', c_int),
211 ('sweepCount', c_int),
212 ('time', c_double),
213 ('timer', c_double),
214 ('userParams', c_double*4),
215 ('temperature', c_double),
216 ('oldIntSol', c_int),
217 ('oldExtSol', c_int),
218 ('digitalIn', c_short),
219 ('sweepKind', c_short),
220 ('digitalOut', c_short),
221 ('filler1', c_short),
222 ('markers', c_double*4),
223 ('filler2', c_int),
224 ('CRC', c_int)]
225
227 _pack_ = 1
228 _fields_ = [('mark', c_int),
229 ('label', c_char*String32Size),
230 ('comment', c_char*String80Size),
231 ('seriesCount', c_int),
232 ('numberSweeps', c_int),
233 ('amplStateOffset', c_int),
234 ('amplStateSeries', c_int),
235 ('seriesType', c_byte),
236 ('useXStart', c_byte),
237 ('filler1', c_byte),
238 ('filler2', c_byte),
239 ('time', c_double),
240 ('pageWidth', c_double),
241
242 ('UP0_0_name', c_char*String32Size),
243 ('UP0_0_aUnit', c_char*String8Size),
244 ('UP0_1_name', c_char*String32Size),
245 ('UP0_1_aUnit', c_char*String8Size),
246 ('UP0_2_name', c_char*String32Size),
247 ('UP0_2_aUnit', c_char*String8Size),
248 ('UP0_3_name', c_char*String32Size),
249 ('UP0_3_aUnit', c_char*String8Size),
250
251 ('filler3', c_char*32),
252 ('userParams1', c_double*4),
253 ('lockInParams', 96*c_char),
254 ('amplifierState', 400*c_char),
255 ('username', c_char*String80Size),
256
257 ('UP1_0_name', c_char*String32Size),
258 ('UP1_0_aUnit', c_char*String8Size),
259 ('UP1_1_name', c_char*String32Size),
260 ('UP1_1_aUnit', c_char*String8Size),
261 ('UP1_2_name', c_char*String32Size),
262 ('UP1_2_aUnit', c_char*String8Size),
263 ('UP1_3_name', c_char*String32Size),
264 ('UP1_3_aUnit', c_char*String8Size),
265 ('filler4', c_int),
266 ('CRC', c_int),
267 ('userParams2', c_double*4),
268
269 ('UP2_0_name', c_char*String32Size),
270 ('UP2_0_aUnit', c_char*String8Size),
271 ('UP2_1_name', c_char*String32Size),
272 ('UP2_1_aUnit', c_char*String8Size),
273 ('UP2_2_name', c_char*String32Size),
274 ('UP2_2_aUnit', c_char*String8Size),
275 ('UP2_3_name', c_char*String32Size),
276 ('UP2_3_aUnit', c_char*String8Size),
277 ('scanParams', 96*c_char)]
278
280 _pack_ = 1
281 _fields_ = [('mark', c_int),
282 ('label', c_char*String32Size),
283 ('text', c_char*String80Size),
284 ('experimentNumber', c_int),
285 ('groupCount', c_int),
286 ('CRC', c_int)]
287
289 _pack_ = 1
290 _fields_ = [('version', c_int),
291 ('mark', c_int),
292 ('versionName', c_char*String32Size),
293 ('auxFileName', c_char*String80Size),
294 ('rootText', c_char*String400Size),
295 ('startTime', c_double),
296 ('maxSamples', c_int),
297 ('CRC', c_int),
298 ('features', c_short),
299 ('filler1', c_short),
300 ('filler2', c_int)]
301
302 BothEndian[iS].LevelRecords9 = [RootRecord, GroupRecord, SeriesRecord, SweepRecord, TraceRecord]
303
304
305
306
307
308
309
310 iLCycle = 0
311 iLAmplitude = 1
312 iLVReversal = 2
313
314
315 iLSkip = 0
316 iMaxCycles = 1
317 iSegmentNo = 2
318
319
320 iAdapt = 0
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346 ( SegmentConstant,
347 SegmentRamp,
348 SegmentContinuous,
349 SegmentConstSine,
350 SegmentSquarewave,
351 SegmentChirpwave ) = range(6)
352
353
354 ( ModeInc,
355 ModeDec,
356 ModeIncInterleaved,
357 ModeDecInterleaved,
358 ModeAlternate,
359 ModeLogInc,
360 ModeLogDec,
361 ModeLogIncInterleaved,
362 ModeLogDecInterleaved,
363 ModeLogAlternate ) = range(10)
364
365
366 ( TrigNone,
367 TrigSeries,
368 TrigSweep,
369 TrigSweepNoLeak ) = range(4)
370
371
372 ( AnyAmplMode,
373 VCAmplMode,
374 CCAmplMode,
375 IDensityMode ) = range(4)
376
377
378 ( AutoRangingOff,
379 AutoRangingPeak,
380 AutoRangingMean,
381 AutoRangingRelSeg ) = range(4)
382
383
384 ( AdcOff,
385 Analog,
386 Digitals,
387 Digital,
388 AdcVirtual ) = range(5)
389
390
391 ( LNone,
392 LStoreAvg,
393 LStoreEach,
394 LNoStore ) = range(4)
395
396
397 ( Labs,
398 Lrel,
399 LabsLH,
400 LrelLH ) = range(4)
401
402
404 _pack_ = 1
405 _fields_ = [('seMark', c_int),
406 ('seClass', c_byte),
407 ('seDoStore', c_byte),
408 ('seVoltageIncMode', c_byte),
409 ('seDurationIncMode', c_byte),
410 ('seVoltage', c_double),
411 ('seVoltageSource', c_int),
412 ('seDeltaVFactor', c_double),
413 ('seDeltaVIncrement', c_double),
414 ('seDuration', c_double),
415 ('seDurationSource', c_int),
416 ('seDeltaTFactor', c_double),
417 ('seDeltaTIncrement', c_double),
418 ('seFiller1', c_int),
419 ('seCRC', c_uint),
420 ('seScanRate', c_double)]
421
423 _pack_ = 1
424 _fields_ = [('chMark', c_int),
425 ('chLinkedChannel', c_int),
426 ('chCompressionFactor', c_int),
427 ('chYUnit', c_char*8),
428 ('chAdcChannel', c_short),
429 ('chAdcMode', c_byte),
430 ('chDoWrite', c_byte),
431 ('stLeakStore', c_byte),
432 ('chAmplMode', c_byte),
433 ('chOwnSegTime', c_byte),
434 ('chSetLastSegVMemb', c_byte),
435 ('chDacDhannel', c_short),
436 ('chDacMode', c_byte),
437 ('chFiller1', c_byte),
438 ('chRelevantXSegment', c_int),
439 ('chRelevantYSegment', c_int),
440 ('chDacUnit', c_char*8),
441 ('chHolding', c_double),
442 ('chLeakHolding', c_double),
443 ('chLeakSize', c_double),
444 ('chLeakHoldMode', c_byte),
445 ('chLeakAlternate', c_byte),
446 ('chAltLeakAveraging', c_byte),
447 ('chLeakPulseOn', c_byte),
448 ('chStimToDacID', c_ushort),
449 ('chCompressionMode', c_ushort),
450 ('chCompressionSkip', c_int),
451 ('chDacBit', c_short),
452 ('chHasSinewaves', c_byte),
453 ('chBreakMode', c_byte),
454 ('chZeroSeg', c_int),
455 ('chFiller2', c_int),
456 ('chInfoLReal', c_double*8),
457 ('chInfoLInt', c_int*8),
458 ('chInfoIChar', c_char*8),
459 ('chDacOffset', c_double),
460 ('chAdcOffset', c_double),
461 ('chTraceMathFormat', c_byte),
462 ('chHasChirp', c_byte),
463 ('chFiller3', c_byte*30),
464 ('chCompressionOffset', c_int),
465 ('chPhotoMode', c_int),
466 ('chBreakLevel', c_double),
467 ('chTraceMath', c_char*128),
468 ('chOldCRC', c_uint),
469 ('chFiller4', c_int),
470 ('chCRC', c_uint)]
471
473 _pack_ = 1
474 _fields_ = [('stMark', c_int),
475 ('stEntryName', c_char*32),
476 ('stFileName', c_char*32),
477 ('stAnalName', c_char*32),
478 ('stDataStartSegment', c_int),
479 ('stDataStartTime', c_double),
480 ('stSampleInterval', c_double),
481 ('stSweepInterval', c_double),
482 ('stLeakDelay', c_double),
483 ('stFilterFactor', c_double),
484 ('stNumberSweeps', c_int),
485 ('stNumberLeaks', c_int),
486 ('stNumberAverages', c_int),
487 ('stActualAdcChannels', c_int),
488 ('stActualDacChannels', c_int),
489 ('stExtTrigger', c_byte),
490 ('stNoStartWait', c_byte),
491 ('stUseScanRates', c_byte),
492 ('stNoContAq', c_byte),
493 ('stHasSinewaves', c_byte),
494 ('stOldStartMacKind', c_byte),
495 ('stOldEndMacKind', c_byte),
496 ('stAutoRange', c_byte),
497 ('stBreakNext', c_byte),
498 ('stIsExpanded', c_byte),
499 ('stLeakCompMode', c_byte),
500 ('stHasChirp', c_byte),
501 ('stOldStartMacro', c_char*32),
502 ('stOldEndMacro', c_char*32),
503 ('stIsGapFree', c_byte),
504 ('sHandledExternally', c_byte),
505 ('stFiller1', c_byte),
506 ('stFiller2', c_byte),
507 ('stCRC', c_uint),
508 ('stTag', c_char*32)]
509
511 _pack_ = 1
512 _fields_ = [('roVersion', c_int),
513 ('roMark', c_int),
514 ('roVersionName', c_char*32),
515 ('roMaxSamples', c_int),
516 ('roFiller1', c_int),
517 ('roParams', c_double*10),
518 ('roParamText', c_char*320),
519 ('roReserved', c_char*128),
520 ('roFiller2', c_int),
521 ('roCRC', c_uint)]
522
523 BothEndian[iS].StimRecords9 = [StimRootRecord, StimulationRecord, StimChannelRecord, StimSegmentRecord]
524
525
531 BothEndian[iS].Header = Header
532 ActualHeaderSize = lambda levels: (2+levels)*sizeof(c_int)
533
537 BothEndian[iS].LevelFooter = LevelFooter
538
539
540
542 _pack_ = 1
543 _fields_ = [('time', c_double),
544 ('stimCount', c_int),
545 ('sweepCount', c_int),
546 ('averageCount', c_int),
547 ('leak', c_byte),
548 ('secondTrace', c_byte),
549 ('lbl', c_char*14),
550 ('dataPoints', c_int),
551 ('data', c_int),
552 ('dataPointer', c_int),
553
554 ('dataFactor1', c_double),
555 ('dataFactor2', c_double),
556 ('cSlow', c_double),
557 ('gSeries', c_double),
558 ('rsValue', c_double),
559 ('rsFraction', c_double),
560
561 ('zeroCurrent', c_double),
562 ('onlineYResult', c_double),
563 ('onlineXResult', c_double),
564
565 ('totalPoints', c_int),
566 ('offset', c_int),
567
568 ('sweepKind', c_short),
569
570
571
572
573
574
575
576 ('furaPoints', c_int),
577 ('furaData', c_int),
578 ('furaPointer', c_int),
579 ('onlineYResult2', c_double),
580 ('onlineXResult2', c_double),
581
582 ('dispFactor1', c_double),
583 ('dispFactor2', c_double),
584
585
586
587
588
589
590 ('dataFormat', c_byte),
591 ('dataAbscissa', c_byte),
592
593
594
595
596 ('timer', c_double),
597 ('spares', c_char*10)]
598
600 _pack_ = 1
601 _fields_ = [('time', c_double),
602
603 ('bandwidth', c_double),
604 ('pipettePotential', c_double),
605 ('cellPotential', c_double),
606 ('pipetteResistance', c_double),
607 ('sealResistance', c_double),
608 ('backgroundNoise', c_double),
609 ('temperature', c_double),
610 ('pipettePressure', c_double),
611 ('userParam1Value', c_double),
612 ('userParam1Name', c_char*14),
613 ('userParam1Unit', c_char*2),
614 ('userParam2Value', c_double),
615 ('userParam2Name', c_char*14),
616 ('userParam2Unit', c_char*2),
617 ('recordingMode', c_byte),
618
619
620
621
622
623
624
625
626
627 ('filler1', c_byte),
628 ('comment', c_char*80),
629 ('epc9State', c_byte*104),
630 ('internalSolution', c_int),
631 ('externalSolution', c_int),
632 ('extraYUnit1', c_char*2),
633 ('extraYUnit2', c_char*2),
634 ('dispYUnit1', c_char*4),
635 ('dispYUnit2', c_char*4),
636 ('furaK', c_double),
637 ('furaMin', c_double),
638 ('furaMax', c_double),
639 ('lockInExtPhase', c_double),
640 ('timer', c_double),
641
642 ('extraLongReal', c_double*4)]
643
645 """ A GroupRecord describes a group of series, such as patch and
646 whole cell currents obtained simultanuously, or groups of series
647 obtained in sequence under different sets of conditions
648 """
649 _pack_ = 1
650 _fields_ = [('label', c_char*14),
651 ('text', c_char*80),
652 ('experimentNumber', c_int),
653 ('extraLongReal', c_double)]
654
656 _pack_ = 1
657 _fields_ = [('version', c_short),
658 ('versionName', c_char*14),
659 ('fileName', c_char*14),
660 ('comments', c_char*400),
661 ('startTime', c_double)]
662
663 BothEndian[iS].LevelRecords7 = [RootRecord, GroupRecord, SeriesRecord, SweepRecord, TraceRecord]
664
666 _pack_ = 1
667 _fields_ = [('cls', c_byte),
668 ('isHolding', c_byte),
669 ('voltage', c_double),
670 ('duration', c_double),
671 ('deltaVFactor', c_double),
672 ('deltaVIncrement', c_double),
673 ('deltaTFactor', c_double),
674 ('deltaTIncrement', c_double)]
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
713 _pack_ = 1
714 _fields_ = [('fileName', c_char*14),
715 ('entryName', c_char*14),
716 ('sampleInterval', c_double),
717 ('filterFactor', c_double),
718 ('sweepInterval', c_double),
719 ('numberSweeps', c_int),
720 ('numberRepeats', c_int),
721 ('repeatWait', c_int),
722 ('linkedSequence', c_char*14),
723 ('linkedWait', c_double),
724
725 ('leakCount', c_int),
726 ('leakSize', c_double),
727
728 ('leakHolding', c_double),
729 ('leakAlternate', c_byte),
730 ('altLeakAveraging', c_byte),
731 ('leakDelay', c_double),
732 ('trig1Segment', c_short),
733 ('trig1Time', c_double),
734 ('trig1Length', c_double),
735 ('trig1Amplitude', c_double),
736 ('trig1Dac', c_short),
737 ('trig2Segment', c_short),
738 ('trig2Time', c_double),
739 ('trig2Length', c_double),
740 ('trig2Amplitude', c_double),
741 ('trig2Dac', c_short),
742 ('trig3Segment', c_short),
743 ('trig3Time', c_double),
744 ('trig3Length', c_double),
745 ('trig3Amplitude', c_double),
746 ('trig3Dac', c_short),
747 ('numberOfTriggers', c_short),
748 ('relevantXSegment', c_short),
749 ('relevantYSegment', c_short),
750
751 ('writeMode', c_byte),
752 ('incrementMode', c_byte),
753
754 ('totalSweepLength', c_int),
755
756
757 ('maxSweepLength', c_int),
758
759
760
761
762 ('inputChannels', c_short),
763 ('gUpdate', c_byte),
764
765 ('relAbsPot', c_byte),
766 ('hasContinuous', c_byte),
767 ('logIncrement', c_byte),
768 ('stimDac', c_short),
769 ('adc1', c_short),
770 ('adc2', c_short),
771 ('yUnit1', c_char*2),
772 ('yUnit2', c_char*2),
773 ('VmembIncrement', c_float),
774 ('extTrigger', c_byte),
775 ('fileTemplate', c_byte),
776 ('stimKind', c_short),
777
778
779
780
781
782
783 ('lockInCycle', c_double),
784 ('lockInAmplitude', c_double),
785 ('furaOn', c_byte),
786 ('VmembMode', c_byte),
787 ('furaTotLength', c_double),
788 ('furaDelay', c_double),
789 ('furaLength1', c_double),
790 ('furaLength2', c_double),
791 ('furaWaveLength0', c_double),
792 ('furaWaveLength1', c_double),
793 ('furaWaveLength2', c_double),
794 ('furaRepeats', c_short),
795 ('lockInSkip', c_int),
796 ('lockInVReversal', c_double),
797 ('lockInMode', c_byte),
798 ('lockInShow', c_byte),
799 ('configMacro', c_char*16),
800 ('endMacro', c_char*16),
801 ('amplModeKind', c_byte),
802 ('filler1', c_byte)]
803
807
808 BothEndian[iS].StimRecords7 = [StimRootRecord, StimulationRecord, StimSegmentRecord]
809
810
811
820
825
826 -def LoadOneLevel(pulmap, header, LevelRecords, LevelFooter, offset, level):
827 rec = ReadStruct(LevelRecords[level], pulmap, offset)
828 result = TreeLevel(rec)
829 offset += header.levelSize[level]
830 footer = ReadStruct(LevelFooter, pulmap, offset)
831 offset += sizeof(LevelFooter)
832 for i in xrange(footer.childCount):
833 child, offset = LoadOneLevel(pulmap, header, LevelRecords, LevelFooter, offset, level+1)
834 result.append(child)
835 return result, offset
836
837 -def LoadTree(path, GetLevels=lambda header, endian: None):
838 fileno = pulmap = None
839 try:
840 size = os.path.exists(path) and os.stat(path)[6] or 0
841 if size:
842 fileno = os.open(path, os.O_RDONLY)
843 pulmap = mmap.mmap(fileno, size, access=mmap.ACCESS_READ)
844 if mmap is None:
845 size = 0
846 if not size:
847 raise Exception("Can't open %s" % path)
848 dat_header = None
849 pul_offset = 0
850 pgf_offset = 0
851 if pulmap[:4] == 'DAT2':
852 dat_header = BothEndian[0].BundleHeader.from_buffer_copy(pulmap, 0)
853 elif pulmap[:4] == '2TAD':
854 dat_header = BothEndian[1].BundleHeader.from_buffer_copy(pulmap, 0)
855 if not (dat_header is None):
856 for i in xrange(dat_header.oItems):
857 if dat_header.oBundleItems[i][8:12].lower() == '.pul':
858 pul_offset = struct.unpack('i', dat_header.oBundleItems[i][:4])[0]
859 elif dat_header.oBundleItems[i][8:12].lower() == '.pgf':
860 pgf_offset = struct.unpack('i', dat_header.oBundleItems[i][:4])[0]
861 endian = BothEndian[0]
862 header = endian.Header.from_buffer_copy(pulmap, pul_offset)
863 if header.magicNumber != TREE_MAGIC:
864 endian = BothEndian[1]
865 header = endian.Header.from_buffer_copy(pulmap, pul_offset)
866 if header.magicNumber != TREE_MAGIC:
867 raise Exception('Bad magic')
868 levelRecords = GetLevels(header, endian)
869 offset = pul_offset + ActualHeaderSize(header.levels)
870 root, offset = LoadOneLevel(pulmap, header, levelRecords, endian.LevelFooter, offset, 0)
871 pgf_root = None
872 if False:
873 pgf_header = endian.Header.from_buffer_copy(pulmap, pgf_offset)
874 offset = pgf_offset + ActualHeaderSize(pgf_header.levels)
875 pgf_root, pgf_offset = LoadOneLevel(pulmap, pgf_header, endian.StimRecords9, endian.LevelFooter, offset, 0)
876 print pgf_header
877 for i in xrange(pgf_header.levels):
878 print pgf_header.levelSize[i],
879 print
880 print pgf_root.rec
881 for stim in pgf_root:
882 print stim.rec
883 for chan in stim:
884 print chan.rec
885 for sgm in chan:
886 print sgm.rec
887 print ' /chan'
888 print ' /stim'
889 return header, endian, root
890 finally:
891 if pulmap:
892 pulmap.close()
893 del pulmap
894 if fileno:
895 os.close(fileno)
896
897
899 def GetLevels(header, endian):
900 if header.levels == 5:
901 return endian.LevelRecords9
902 else:
903 return endian.LevelRecords7
904 return LoadTree(path, GetLevels)
905
907 return LoadTree(path, lambda header, endian: endian.StimRecords7)
908
910 if units == 'A':
911 return 'pA', 1e12*scale
912 elif units == 'V':
913 return 'mV', 1e3*scale
914 else:
915 return units, scale
916
924 - def read(self, first, last, latency=0, skip=1):
925 ifirst, ilast = [self.data.segmentation.index_at(x) for x in (first, last)]
926 if ifirst != ilast:
927 raise Exception("PUL: can't read() from multiple segments at once")
928 segfirst = self.data.segmentation.segments[ifirst][0]
929 seglast = self.data.segmentation.segments[ifirst][1]
930 return read_with_latency(self.read_in_seg, first, last, latency, skip, ifirst, segfirst, seglast)
931
932
942 samples = numpy.zeros(dtype='float32', shape=(last-first+1,))
943 try:
944 sweep = self.data.sweeps[iseg][self.channel]
945 except IndexError:
946 if qubx.global_namespace.DEBUG:
947 print 'missing sweep: segment %i, signal %i, points %i-%i' % (iseg, self.channel, first, last)
948 return samples
949 start = 0
950 for ichunk, f, l in self.data.sweep_segm[iseg, self.channel].split(first, last):
951 n = l-f+1
952 samples[start:start+n] = sweep[ichunk][f:l+1]
953 samples = samples[::skip]
954 samples *= self.data.trace_scale[self.channel]
955 scale = self.data.signals.get(self.channel, 'Scale')
956 if scale != 1.0:
957 samples *= scale
958 offset = self.data.signals.get(self.channel, 'Offset')
959 if offset != 0.0:
960 samples += offset
961 return samples
962
964 """One open Patchmaster file.
965
966 """
968 QubData.__init__(self)
969 self.path = path
970
971 base, ext = os.path.splitext(path)
972 sess = qubx.tree.Open(base+'.qsf', True)
973 sess.close()
974
975 self.sampling = ((sess.find('Sampling') and sess['Sampling'].data[0])
976 or 1.0e-4)
977
978 header, endian, self.root = LoadPUL(path)
979 datapath = os.path.splitext(path)[0]+'.dat'
980 size = os.path.exists(datapath) and os.stat(datapath)[6] or 0
981 if size:
982 self.fileno = os.open(datapath, os.O_RDONLY)
983 self.mmap = mmap.mmap(self.fileno, size, access=mmap.ACCESS_READ)
984 if mmap is None:
985 os.close(self.fileno)
986 self.fileno = size = 0
987 if not size:
988 raise Exception("Can't open %s" % datapath)
989
990 self.sweeps = []
991 self.sweep_segm = {}
992 self.trace_scale = []
993
994 if header.levels == 5:
995 sweep0 = self.root[0][0][0]
996 self.sampling = sweep0[0].rec.xInterval
997 for c, trace in enumerate(sweep0):
998 units, scale = adjust_units(trace.rec.yUnit, trace.rec.dataScaler)
999 self.signals.append({'Name' : trace.rec.label,
1000 'Units' : units})
1001 self.trace_scale.append(scale)
1002 self.set_analog(c, QubData_PUL_Analog(self, c))
1003
1004 if sweep0[0].rec.dataKind & 1:
1005 typ = '<'
1006 else:
1007 typ = '>'
1008 typ += {0 : 'i2',
1009 1 : 'i4',
1010 2 : 'f4',
1011 3 : 'f8'}[sweep0[0].rec.dataFormat]
1012 dataSize = int(typ[2])
1013 last = -1
1014 i = 0
1015 nSeries = 0
1016 iSeg = 0
1017
1018 for group in self.root:
1019
1020 for series in group:
1021
1022 nSeries += 1
1023 series_list = self.lists.show_list(series.rec.label or ("Series %i" % nSeries))
1024 for sweep in series:
1025
1026 seglen = sweep[0].rec.dataPoints
1027 first = last + 1
1028 last = first + seglen - 1
1029 self.segmentation.add_seg(first, last, first*self.sampling*1e3)
1030 series_list.insert_selection(first, last, sweep.rec.label)
1031
1032
1033
1034 arrays = []
1035 self.sweeps.append(arrays)
1036 for itrace, trace in enumerate(sweep):
1037 offset = trace.rec.data
1038 self.sweep_segm[iSeg, itrace] = trace_segm = Segmentation()
1039 if trace.rec.interleaveSize:
1040 ilSize = sweep[0].rec.interleaveSize
1041 ilSkip = sweep[0].rec.interleaveSkip
1042 chunks = []
1043 remain = seglen
1044 chunkoff = offset
1045 segm_f = 0
1046 while remain:
1047 chunkn = min(remain, ilSize/dataSize)
1048 chunks.append(numpy.frombuffer(self.mmap, typ, chunkn, chunkoff))
1049 chunkoff += ilSize + ilSkip
1050 remain -= chunkn
1051 trace_segm.add_seg(segm_f, segm_f+chunkn-1, 0.0)
1052 segm_f += chunkn
1053 arrays.append(chunks)
1054 else:
1055 arrays.append([numpy.frombuffer(self.mmap, typ, seglen, offset)])
1056 trace_segm.add_seg(0, seglen-1, 0.0)
1057 iSeg += 1
1058
1059
1060 elif header.levels == 4:
1061 try:
1062 stimHeader, stimEndian, stimRoot = LoadStim(os.path.splitext(path)[0]+'.pgf')
1063 self.sampling = stimRoot[0].rec.sampleInterval
1064 except:
1065 self.sampling = RequestSampling(self.sampling)
1066
1067 hasLeak = hasSecond = False
1068 for group in self.root:
1069 for series in group:
1070 if series:
1071 if series[0].rec.leak:
1072 hasLeak = True
1073 if series[0].rec.secondTrace:
1074 hasSecond = (series.rec.extraYUnit2, series[0].rec.dataFactor2)
1075 signals = [Anon(units = self.root[0][0].rec.extraYUnit1,
1076 scale = self.root[0][0][0].rec.dataFactor1)]
1077 if hasSecond:
1078 signals.append(Anon(units = hasSecond[0], scale = hasSecond[1], second = True))
1079 if hasLeak:
1080 signals.append(signals[0].clone(leak = True))
1081
1082 for c, signal in enumerate(signals):
1083 units, scale = adjust_units(signal.units, signal.scale)
1084 self.signals.append({'Name' : signal.leak and 'Leak' or (signal.second and 'Ch 1' or 'Ch 0'),
1085 'Units' : units})
1086 self.trace_scale.append(scale)
1087 self.set_analog(c, QubData_PUL_Analog(self, c))
1088
1089 sweep0 = self.root[0][0][0].rec
1090 dataFormat = (sweep0.sweepKind & 8) and sweep0.dataFormat or 0
1091 if sweep0.sweepKind & 16:
1092 typ = '<'
1093 else:
1094 typ = '>'
1095 typ += {0 : 'i2',
1096 1 : 'i4',
1097 2 : 'f4',
1098 3 : 'f8'}[dataFormat]
1099 dataSize = int(typ[2])
1100 last = -1
1101 i = 0
1102 iSeg = 0
1103 shared_zeros = memoize(lambda count: numpy.zeros(dtype='float32', shape=(count,)))
1104 for group in self.root:
1105 for series in group:
1106 for sweep in series:
1107 seglen = sweep.rec.totalPoints
1108 first = last + 1
1109 last = first + seglen - 1
1110 self.segmentation.add_seg(first, last, first*self.sampling*1e3)
1111 offLeak = sweep.rec.data + (sweep.rec.leak and (seglen*dataSize) or 0)
1112 offSecond = offLeak + seglen*dataSize
1113 arrays = []
1114 for c, signal in enumerate(signals):
1115 self.sweep_segm[iSeg, c] = trace_segm = Segmentation()
1116 if signal.leak:
1117 if sweep.rec.leak:
1118 offset = offLeak
1119 else:
1120 offset = -1
1121 elif signal.second:
1122 if sweep.rec.secondTrace:
1123 offset = offSecond
1124 else:
1125 offset = -1
1126 else:
1127 offset = sweep.rec.data
1128 if offset >= 0:
1129 arrays.append([numpy.frombuffer(self.mmap, typ, seglen, offset)])
1130 else:
1131 arrays.append([shared_zeros(seglen)])
1132 self.sweeps.append(arrays)
1133 trace_segm.add_seg(0, seglen-1, 0.0)
1134 iSeg += 1
1135
1136
1137 for chan in qubx.tree.children(sess.find('DataChannels'), 'Channel'):
1138 chan['Scaling'].data = 1.0
1139 chan['Offset'].data = 0.0
1140
1141 self.read_session(sess, progressf)
1142
1143 if not self.lists.lists:
1144 last = -1
1145 if header.levels == 5:
1146 for g, group in enumerate(self.root):
1147 for s, series in enumerate(group):
1148 lbl = ('%i: ' % g) if (len(self.root) > 1) else ''
1149 lbl = '%sSeries %i' % (lbl, s+1)
1150 if series.rec.label:
1151 lbl = '%s: %s' % (lbl, series.rec.label)
1152 list = self.lists.show_list(lbl)
1153 for sweep in series:
1154 seglen = sweep[0].rec.dataPoints
1155 first = last + 1
1156 last = first + seglen - 1
1157 list.insert_selection(first, last, sweep.rec.label)
1158 elif header.levels == 4:
1159 for group in self.root:
1160 for s, series in enumerate(group):
1161 lbl = ('%i: ' % g) if (len(self.root) > 1) else ''
1162 lbl = '%sSeries %i' % (lbl, s+1)
1163 list = self.lists.show_list(lbl)
1164 for sweep in series:
1165 seglen = sweep.rec.totalPoints
1166 first = last + 1
1167 last = first + seglen - 1
1168 list.insert_selection(first, last, sweep.rec.lbl)
1169
1171 if not (self.mmap is None):
1172 self.mmap.close()
1173 if self.fileno:
1174 os.close(self.fileno)
1175
1176
1177 fallback_reader = None
1178
1187
1188 fallback_reader = SetReader('.dat', 'DAT files', QubData_PUL_Open)
1189 SetReader('.pul', 'Patchmaster/Pulse files', QubData_PUL_Open)
1190
1191
1196
1197 if __name__ == '__main__':
1198 header, endian, root = LoadPUL(sys.argv[1])
1199 print_struct(header)
1200 print_levels(root)
1201