@@ -1054,23 +1054,21 @@ def _read_nsx_dataheader_spec_v30_ptp(
10541054
10551055 data_header = {}
10561056 index = 0
1057-
1058- if offset is None :
1059- # This is read as an uint32 numpy scalar from the header so we transform it to python int
1060- offset = int (self ._nsx_basic_header [nsx_nb ]["bytes_in_headers" ])
1057+ # This is read as an uint32 numpy scalar from the header so we transform it to python int
1058+ header_size = offset or int (self ._nsx_basic_header [nsx_nb ]["bytes_in_headers" ])
10611059
10621060 ptp_dt = [
10631061 ("reserved" , "uint8" ),
10641062 ("timestamps" , "uint64" ),
10651063 ("num_data_points" , "uint32" ),
10661064 ("samples" , "int16" , (self ._nsx_basic_header [nsx_nb ]["channel_count" ],)),
10671065 ]
1068- npackets = int ((filesize - offset ) / np .dtype (ptp_dt ).itemsize )
1069- struct_arr = np .memmap (filename , dtype = ptp_dt , shape = npackets , offset = offset , mode = "r" )
1066+ npackets = int ((filesize - header_size ) / np .dtype (ptp_dt ).itemsize )
1067+ struct_arr = np .memmap (filename , dtype = ptp_dt , shape = npackets , offset = header_size , mode = "r" )
10701068
10711069 if not np .all (struct_arr ["num_data_points" ] == 1 ):
10721070 # some packets have more than 1 sample. Not actually ptp. Revert to non-ptp variant.
1073- return self ._read_nsx_dataheader_spec_v22_30 (nsx_nb , filesize = filesize , offset = offset )
1071+ return self ._read_nsx_dataheader_spec_v22_30 (nsx_nb , filesize = filesize , offset = header_size )
10741072
10751073
10761074 # Segment data, at the moment, we segment, where the data has gaps that are longer
@@ -1085,45 +1083,51 @@ def _read_nsx_dataheader_spec_v30_ptp(
10851083 timestamps_in_seconds = raw_timestamps / timestamps_sampling_rate
10861084
10871085 time_differences = np .diff (timestamps_in_seconds )
1088- gap_sample_indices = np .argwhere (time_differences > segmentation_threshold ).flatten ()
1089- segment_start_indices = np .hstack ((0 , 1 + gap_sample_indices ))
1086+ gap_indices = np .argwhere (time_differences > segmentation_threshold ).flatten ()
1087+ segment_starts = np .hstack ((0 , 1 + gap_indices ))
10901088
10911089 # Report gaps if any are found
1092- if len (gap_sample_indices ) > 0 :
1090+ if len (gap_indices ) > 0 :
10931091 import warnings
10941092 threshold_ms = segmentation_threshold * 1000
10951093
1094+ # Calculate all gap details in vectorized operations
1095+ gap_durations_seconds = time_differences [gap_indices ]
1096+ gap_durations_ms = gap_durations_seconds * 1000
1097+ gap_positions_seconds = timestamps_in_seconds [gap_indices ] - timestamps_in_seconds [0 ]
1098+
1099+ # Build gap detail lines all at once
1100+ gap_detail_lines = [
1101+ f"| { index :>15,} | { pos :>21.6f} | { dur :>21.3f} |\n "
1102+ for index , pos , dur in zip (gap_indices , gap_positions_seconds , gap_durations_ms )
1103+ ]
1104+
10961105 segmentation_report_message = (
1097- f"\n Found { len (gap_sample_indices )} gaps for nsx { nsx_nb } where samples are farther apart than { threshold_ms :.3f} ms.\n "
1098- f"Data will be segmented at these locations to create { len (segment_start_indices )} segments.\n \n "
1106+ f"\n Found { len (gap_indices )} gaps for nsx { nsx_nb } where samples are farther apart than { threshold_ms :.3f} ms.\n "
1107+ f"Data will be segmented at these locations to create { len (segment_starts )} segments.\n \n "
10991108 "Gap Details:\n "
11001109 "+-----------------+-----------------------+-----------------------+\n "
11011110 "| Sample Index | Sample at | Gap Jump |\n "
11021111 "| | (Seconds) | (Milliseconds) |\n "
11031112 "+-----------------+-----------------------+-----------------------+\n "
1113+ + '' .join (gap_detail_lines ) +
1114+ "+-----------------+-----------------------+-----------------------+\n "
11041115 )
1105-
1106- for sample_index in gap_sample_indices :
1107- gap_duration_seconds = time_differences [sample_index ] # Actual time difference between timestamps
1108- gap_duration_ms = gap_duration_seconds * 1000
1109- gap_position_seconds = timestamps_in_seconds [sample_index ] - timestamps_in_seconds [0 ] # Time position relative to start
1110-
1111- segmentation_report_message += (
1112- f"| { sample_index :>15,} | { gap_position_seconds :>21.6f} | { gap_duration_ms :>21.3f} |\n "
1113- )
1114-
1115- segmentation_report_message += "+-----------------+-----------------------+-----------------------+\n "
11161116 warnings .warn (segmentation_report_message )
11171117
1118- for seg_index , seg_start_index in enumerate (segment_start_indices ):
1119- if seg_index < (len (segment_start_indices ) - 1 ):
1120- seg_stop_index = segment_start_indices [seg_index + 1 ]
1121- else :
1122- seg_stop_index = len (struct_arr ) - 1
1123- seg_offset = offset + seg_start_index * struct_arr .dtype .itemsize
1124- num_data_pts = seg_stop_index - seg_start_index
1118+ # Calculate all segment boundaries and derived values in one operation
1119+ segment_boundaries = list (segment_starts ) + [len (struct_arr ) - 1 ]
1120+ segment_num_data_points = [segment_boundaries [i + 1 ] - segment_boundaries [i ] for i in range (len (segment_starts ))]
1121+
1122+ size_of_data_block = struct_arr .dtype .itemsize
1123+ segment_offsets = [header_size + pos * size_of_data_block for pos in segment_starts ]
1124+
1125+ num_segments = len (segment_starts )
1126+ for segment_index in range (num_segments ):
1127+ seg_offset = segment_offsets [segment_index ]
1128+ num_data_pts = segment_num_data_points [segment_index ]
11251129 seg_struct_arr = np .memmap (filename , dtype = ptp_dt , shape = num_data_pts , offset = seg_offset , mode = "r" )
1126- data_header [seg_index ] = {
1130+ data_header [segment_index ] = {
11271131 "header" : None ,
11281132 "timestamp" : seg_struct_arr ["timestamps" ], # Note, this is an array, not a scalar
11291133 "nb_data_points" : num_data_pts ,
0 commit comments