44This module expect default channel names from the AlphaOmega record system (RAW
55###, SPK ###, LFP ###, AI ###,…).
66
7- This module reads all *.lsx and *.mpx files in a directory (not recursively).
7+ This module reads all \ *.lsx and \ *.mpx files in a directory (not recursively).
88Listing files
99
1010The specifications are mostly extracted from the "AlphaRS User Manual V1.0.1.pdf"
1111manual provided with the AlphaRS hardware. The specifications are described in
1212the chapter 6: ALPHARS FILE FORMAT. See at the end of this file for file format
1313blocks description.
1414Some informations missing from the file specifications were kindly provided by
15- AlphaOmega engineers:
16- - strings are NULL terminated and encoded in ASCII
17- - m_ResourceVersion (4 bytes) is the C++ version used to compile the logging
18- (data recording) library: each byte is read separately as a char and then
19- concatenated to create the original value (e.g.: b"\x00 \x01 \x00 \x00 ' -> 100)
20- - m_ModeSpike (2 bytes) is read as hex data: 0xMCCC:
21- - M: 1=Master, 2=Slave
22- - CCC: linked channel
23- Be carefull here, the first byte cover MC and the second byte the last
24- part of the linked channel CC
25- - m_nSpikeCount: the total number of segments captured on this channel
26- - m_SpikeColor: follows the COLORREF convention from Windef.h:
27- hex value: 0x00bbggrr
28- - for Type E Stream Data Block:
29- - m_uTimeStamp is an unsigned int32 (unsigned long) counter that is set
30- to 0 at hardware boot and advances at the sampling rate of the system
31- - for analog/digital data channels:
32- - m_FirstSampleNumber: this is the same as m_uTimeStamp. Meaning the
33- value is the timestamp of the first sample in the same block which is
34- the index of the sample from the HW boot
35- - final block: the stop condition for reading MPX blocks is based on the
36- length of the block: if the block has length 65535 (or -1 in signed integer
37- value) we know we have reached the end of the file
38-
39- Still questions/need info:
40- - for digital data channels:
41- - m_SampleNumber and m_Value seems inverted in data compared to
42- specification
43- - m_Value seems to have wrong type: it looks like ushort in data
44- - for analog channels:
45- - what is the m_Duration value?
46- - for segmented channel:
47- - how do we extract the waveforms?
48- - for block type E:
49- - how do we decode the stream data (we need S(t)?reamFormat.h header)
50-
51-
52- Author: Thomas Perret <[email protected] > 15+ AlphaOmega engineers.
5316
5417.. note::
5518 Not a lot of memory optimization effort was put into this module. You should
5922 1. First search TODO in this file.
6023 2. add IO class with :py:class:`neo.io.basefromrawio.BaseFromRaw`
6124 3. Make TTL events -> epochs? We could describe TTL 0/1 events as epochs:
62- when the TTL goes to 1=beginning of epoch
63- 0=end of epoch
64- 4. Once we know how to decode Stream AlphaOmega events add them to an event
65- channel
25+ when the TTL goes to 1=beginning of epoch 0=end of epoch
26+ 4. decode stram data (block type E) using StreamFormat.h file (not provided
27+ with this code)
28+ 5. (4.bis) Once we know how to decode Stream AlphaOmega events add them to
29+ an event channel
30+
31+ Author: Thomas Perret <[email protected] > 6632"""
6733
6834import io
8955
9056class AlphaOmegaRawIO (BaseRawIO ):
9157 """
92- Handles several blocks and segments.
58+ AlphaOmega MPX file format 4 reader. Handles several blocks and segments.
59+
60+ A block is a recording defined in a \*.lsx file. AlphaOmega record system
61+ creates such files every time the software is opened.
9362
94- A block is a recording define in a *.lsx file. AlphaOmega record system
95- creates a new file each time the software is closed then re-opened.
9663 A segment is a continuous record (when record starts/stops).
97- If file are not referenced in a *.lsx file, they are put in the same block.
64+ If file are not referenced in a \*.lsx file, they are put in the same block.
65+
66+ :param dirname: folder from where to load the data
67+ :type dirname: str or Path-like
68+ :param prune_channels: if True removes the empty channels, defaults to True
69+ :type prune_channels: bool
9870
99- Because channels must be gathered into coherent streams, channels names MUST
100- be the default channel names in AlphaRS software (or Alpha LAB SNR).
71+ .. warning::
72+ Because channels must be gathered into coherent streams, channels names
73+ MUST be the default channel names in AlphaRS software (or Alpha LAB SNR).
10174 """
10275
10376 extensions = ["lsx" , "mpx" ]
@@ -274,6 +247,12 @@ def _read_file_blocks(self, filename, prune_channels=True):
274247
275248 length , block_type = HeaderType .unpack (header_bytes )
276249 if length == 65535 :
250+ # The stop condition for reading MPX blocks is base on the
251+ # length of the block: if the block has length 65535 (or -1
252+ # in signed integer value) we know we have reached the end
253+ # of the file
254+ # We could also check that we are at the end of the file
255+ # after this block
277256 break
278257
279258 if block_type == b"h" :
@@ -313,11 +292,10 @@ def _read_file_blocks(self, filename, prune_channels=True):
313292 metadata ["max_sample_rate" ] = max (
314293 metadata ["max_sample_rate" ], sample_rate * 1000
315294 )
316- if (
317- amplitude <= 5
318- and metadata ["application_name" ] == "ALab SNR"
319- ):
320- self .logger .warning ("This should be checked!" )
295+ if amplitude <= 5 :
296+ # This is true for any logging software for map
297+ # version >4 (specs say only for ALab SNR but AO
298+ # engineer says it's true for any software)
321299 amplitude = 1250000 / 2 ** 15
322300 if mode == 0 :
323301 # continuous analog channel definition block
@@ -609,11 +587,12 @@ def _merge_segments(self, factor_period=1.5):
609587 - The two segment must also have the same record date
610588 (YEAR-MONTH-DAY). This could potentially lead to errors if
611589 recordings are longer than a day or run over the night
612- - The next segment must have a datetime greater or equal thant the
613- previous segment (datetime : YEAR-MONTH-DAT -HOUR-MINUTE-SECOND)
590+ - The next segment must have a datetime greater or equal than the
591+ previous segment (datetime : YEAR-MONTH-DAY -HOUR-MINUTE-SECOND)
614592
615593 :param factor_period: how many sample period to consider the next segment
616- is part of the previous one. This should be 1 <= `factor_period` < 2
594+ is part of the previous one. This should be 1 <= `factor_period` < 2,
595+ defaults to 1.5
617596 :type factor_period: float
618597 """
619598 for block in self ._blocks :
@@ -1090,7 +1069,8 @@ def _rescale_epoch_duration(self, raw_duration, dtype, event_channel_index):
10901069
10911070
10921071def decode_string (encoded_string ):
1093- """All AlphaOmega strings are NULL terminated and ASCII encoded"""
1072+ """According to AlphaOmega engineers, all strings are NULL terminated and
1073+ ASCII encoded"""
10941074 return encoded_string [: encoded_string .find (b"\x00 " )].decode ("ascii" )
10951075
10961076
@@ -1113,6 +1093,8 @@ def get_name(f, name_length):
11131093Other blocks exist in the data but are ignored in this implementation as per the
11141094specification: "Any block type other than the ones described below should be
11151095ignored."
1096+
1097+ All data is little-endian (hence the '<' in Struct calls).
11161098"""
11171099
11181100SDataHeader = struct .Struct ("<xlhBBBBBBHBxddlB10s4sxl" )
@@ -1136,7 +1118,8 @@ def get_name(f, name_length):
11361118 - application_name (10-char string): name of the recording application.
11371119 Should be "ARS" for AlphaRS hardware or "ALab SNR" for AlphaLab SnR hardware
11381120 - resource_version (4-char string): C++ version used to compile recording
1139- software
1121+ software: each byte is read separately as a char and concatenated and cast
1122+ into an int to create the original value (e.g.: b"\x00 \x01 \x00 \x00 " -> 100)
11401123 - alignment byte: ignore
11411124 - reserved (long): not used
11421125"""
@@ -1155,7 +1138,8 @@ def get_name(f, name_length):
11551138 - is_analog (short): 0=Digital, 1=Analog
11561139 - is_input (short): 0=Output, 1=Input
11571140 - channel_number: the (unique) channel number identifier from the recording software
1158- - alignment byte: ignore
1141+ - alignment byte: ignore, this and the following bytes are COLORREF
1142+ convention from Windef.h: hex value: 0x00bbggrr
11591143 - spike_color_blue (unsigned char)
11601144 - spike_color_green (unsigned char)
11611145 - spike_color_red (unsigned char)
@@ -1164,10 +1148,15 @@ def get_name(f, name_length):
11641148"""
11651149Then if is_analog and is_input:
11661150 - mode (short): 0=Continuous, 1=(Level or Segmented)
1167- - amplitude (float): bit resolution
1151+ - amplitude (float): bit resolution. For MAP file version 4 if amplitude < 5
1152+ amplitude = 1_250_000/2**15
11681153 - sample_rate (float): in kHz (or more precisely in kilosample per seconds)
1169- - spike_count (short): total number of segments captured in this channel
1170- - mode_spike: see top docstring of this module
1154+ - spike_count (short): size of each data block (short) + timestamp (unsigned long)
1155+ - mode_spike (2 bytes): read as hex data 0xMCCC:
1156+ - M: 1=Master, 2=Slave
1157+ - CCC: linked channel
1158+ Be carefull here, the first byte cover MC and the second byte the last
1159+ part of the linked channel CC
11711160"""
11721161SDefContinAnalog = struct .Struct ("<fh" )
11731162"""
@@ -1233,7 +1222,8 @@ def get_name(f, name_length):
12331222Then for analog channels:
12341223 - sample_value (n-short): array of data values
12351224 - first_sample_number (ulong): for continuous channels: first sample number
1236- in the channel records
1225+ in the channel records. This is the timestamp (see :attr:`SAOEvent`) of
1226+ the first sample in the data blocks
12371227"""
12381228SDataChannelDigital = struct .Struct ("<Lh" )
12391229"""
@@ -1258,7 +1248,8 @@ def get_name(f, name_length):
12581248SAOEvent = struct .Struct ("<cL" )
12591249"""Type E: stream data block:
12601250 - type_event (char): event type only b"S" for now
1261- - timestamp (ulong): see above
1251+ - timestamp (ulong): a counter initialized at 0 at hardware boot and that
1252+ advances at the sampling rate of the system
12621253 - stream_data (n-char): Stream Data - spec says: "Refer to SreamFormat.h or
12631254 use dll to decipher this stream of data"; n=length-8
12641255"""
0 commit comments