@@ -125,14 +125,219 @@ import_nsp <- function(path, prefix = NULL, exclude_events = "spike",
125125 nsx <- substring(path , nchar(path ) - 2 )
126126 if (file.exists(path )) {
127127 inc_progress(sprintf(" Parsing %s" , nsx ), " Importing NSx" )
128- results [[nsx ]] <- read_nsx(path = path , prefix = prefix , partition_prefix = partition_prefix )
128+ if (isTRUE(getOption(" ieegio.legacy.read_nsx" , FALSE ))) {
129+ results [[nsx ]] <- read_nsx_legacy(path = path , prefix = prefix , partition_prefix = partition_prefix )
130+ } else {
131+ results [[nsx ]] <- read_nsx(path = path , prefix = prefix , partition_prefix = partition_prefix )
132+ }
129133 } else {
130134 inc_progress(sprintf(" Skipping %s" , nsx ), " Importing NSx" )
131135 }
132136 }
133137 return (results )
134138}
135139
140+
141+ # ' Compare new chunked read_nsx with legacy version
142+ # ' @description Helper function to validate the new chunked reading implementation
143+ # ' by comparing results with the legacy version that reads all data at once.
144+ # ' @param path path to 'NEV' or 'NSx' files
145+ # ' @param exclude_events passed to \code{\link{import_nsp}}
146+ # ' @param exclude_nsx passed to \code{\link{import_nsp}}
147+ # ' @param partition_prefix passed to \code{\link{import_nsp}}
148+ # ' @param tolerance numeric tolerance for comparing signal values (default 1e-10)
149+ # ' @param verbose logical, print progress messages
150+ # ' @return A list with comparison results including:
151+ # ' \describe{
152+ # ' \item{identical}{logical, TRUE if all results match}
153+ # ' \item{new_result}{result from new chunked method}
154+ # ' \item{legacy_result}{result from legacy method}
155+ # ' \item{differences}{list of any differences found}
156+ # ' }
157+ # ' @keywords internal
158+ compare_nsx_methods <- function (path , exclude_events = " spike" ,
159+ exclude_nsx = NULL ,
160+ partition_prefix = " /part" ,
161+ tolerance = 1e-10 ,
162+ verbose = TRUE ) {
163+
164+ # Get the base pattern for all NSx/NEV files
165+
166+ path_pattern <- gsub(" \\ .(nev|ns[1-9])[\\\\ /]{0,}$" , " " , path , ignore.case = TRUE )
167+
168+ # Find all related files
169+ all_files <- c(
170+ paste0(path_pattern , " .nev" ),
171+ paste0(path_pattern , sprintf(" .ns%d" , seq_len(9 )))
172+ )
173+ all_files <- all_files [file.exists(all_files )]
174+
175+ if (length(all_files ) == 0 ) {
176+ stop(" No .nev or .nsx files found at path: " , path )
177+ }
178+
179+ # Create two temporary directories
180+ temp_base <- tempdir()
181+ temp_new <- file.path(temp_base , " nsx_compare_new" )
182+ temp_legacy <- file.path(temp_base , " nsx_compare_legacy" )
183+
184+ # Clean up old temp dirs if they exist
185+ if (dir.exists(temp_new )) unlink(temp_new , recursive = TRUE )
186+ if (dir.exists(temp_legacy )) unlink(temp_legacy , recursive = TRUE )
187+
188+ dir.create(temp_new , recursive = TRUE )
189+ dir.create(temp_legacy , recursive = TRUE )
190+
191+ if (verbose ) message(" Copying files to temporary directories..." )
192+
193+ # Copy files to both temp directories
194+ for (f in all_files ) {
195+ fname <- basename(f )
196+ file.copy(f , file.path(temp_new , fname ))
197+ file.copy(f , file.path(temp_legacy , fname ))
198+ }
199+
200+ # Get the path to one of the files in temp dirs
201+ first_file <- basename(all_files [1 ])
202+ path_new <- file.path(temp_new , first_file )
203+ path_legacy <- file.path(temp_legacy , first_file )
204+
205+ prefix_new <- file.path(temp_new , " output" )
206+ prefix_legacy <- file.path(temp_legacy , " output" )
207+
208+ # Run new method
209+ if (verbose ) message(" Running new chunked method..." )
210+ old_option <- getOption(" ieegio.legacy.read_nsx" )
211+ on.exit(options(ieegio.legacy.read_nsx = old_option ), add = TRUE )
212+
213+ options(ieegio.legacy.read_nsx = FALSE )
214+ result_new <- import_nsp(
215+ path = path_new ,
216+ prefix = prefix_new ,
217+ exclude_events = exclude_events ,
218+ exclude_nsx = exclude_nsx ,
219+ verbose = FALSE ,
220+ partition_prefix = partition_prefix
221+ )
222+
223+ # Run legacy method
224+ if (verbose ) message(" Running legacy method..." )
225+ options(ieegio.legacy.read_nsx = TRUE )
226+ result_legacy <- import_nsp(
227+ path = path_legacy ,
228+ prefix = prefix_legacy ,
229+ exclude_events = exclude_events ,
230+ exclude_nsx = exclude_nsx ,
231+ verbose = FALSE ,
232+ partition_prefix = partition_prefix
233+ )
234+
235+ # Compare results
236+ if (verbose ) message(" Comparing results..." )
237+
238+ differences <- list ()
239+ all_identical <- TRUE
240+
241+ # Compare each NSx
242+ nsx_names <- grep(" ^ns[1-9]$" , names(result_new ), value = TRUE )
243+
244+ for (nsx_name in nsx_names ) {
245+ nsx_new <- result_new [[nsx_name ]]
246+ nsx_legacy <- result_legacy [[nsx_name ]]
247+
248+ if (is.null(nsx_new ) && is.null(nsx_legacy )) next
249+
250+ if (is.null(nsx_new ) || is.null(nsx_legacy )) {
251+ differences [[nsx_name ]] <- " One result is NULL"
252+ all_identical <- FALSE
253+ next
254+ }
255+
256+ # Compare number of partitions
257+ if (nsx_new $ nparts != nsx_legacy $ nparts ) {
258+ differences [[nsx_name ]] <- sprintf(
259+ " Different number of partitions: new=%d, legacy=%d" ,
260+ nsx_new $ nparts , nsx_legacy $ nparts
261+ )
262+ all_identical <- FALSE
263+ next
264+ }
265+
266+ # Compare channel data for each partition
267+ n_channels <- nsx_new $ header_basic $ channel_count
268+ channel_diffs <- list ()
269+
270+ for (part in seq_len(nsx_new $ nparts )) {
271+ for (ch in seq_len(n_channels )) {
272+ channel_id <- nsx_new $ header_extended $ CC $ electrode_id [ch ]
273+ channel_label <- nsx_new $ header_extended $ CC $ electrode_label [ch ]
274+
275+ fname <- channel_filename(channel_id = channel_id , channel_label = channel_label )
276+
277+ path_data_new <- file.path(
278+ sprintf(" %s_ieeg%s%d" , prefix_new , partition_prefix , part ),
279+ fname
280+ )
281+ path_data_legacy <- file.path(
282+ sprintf(" %s_ieeg%s%d" , prefix_legacy , partition_prefix , part ),
283+ fname
284+ )
285+
286+ if (! file.exists(path_data_new ) || ! file.exists(path_data_legacy )) {
287+ channel_diffs [[sprintf(" part%d_ch%d" , part , channel_id )]] <- " File missing"
288+ all_identical <- FALSE
289+ next
290+ }
291+
292+ data_new <- load_h5(path_data_new , " data" , ram = TRUE )
293+ data_legacy <- load_h5(path_data_legacy , " data" , ram = TRUE )
294+
295+ if (length(data_new ) != length(data_legacy )) {
296+ channel_diffs [[sprintf(" part%d_ch%d" , part , channel_id )]] <- sprintf(
297+ " Different lengths: new=%d, legacy=%d" ,
298+ length(data_new ), length(data_legacy )
299+ )
300+ all_identical <- FALSE
301+ next
302+ }
303+
304+ max_diff <- max(abs(data_new - data_legacy ))
305+ if (max_diff > tolerance ) {
306+ channel_diffs [[sprintf(" part%d_ch%d" , part , channel_id )]] <- sprintf(
307+ " Max difference: %g" , max_diff
308+ )
309+ all_identical <- FALSE
310+ }
311+ }
312+ }
313+
314+ if (length(channel_diffs ) > 0 ) {
315+ differences [[nsx_name ]] <- channel_diffs
316+ }
317+ }
318+
319+ if (verbose ) {
320+ if (all_identical ) {
321+ message(" SUCCESS: All results are identical (within tolerance " , tolerance , " )" )
322+ } else {
323+ message(" DIFFERENCES FOUND:" )
324+ print(differences )
325+ }
326+ }
327+
328+ # Clean up
329+ unlink(temp_new , recursive = TRUE )
330+ unlink(temp_legacy , recursive = TRUE )
331+
332+ list (
333+ identical = all_identical ,
334+ new_result = result_new ,
335+ legacy_result = result_legacy ,
336+ differences = differences
337+ )
338+ }
339+
340+
136341# ' @title Load 'NEV' information from path prefix
137342# ' @param x path \code{prefix} specified in \code{\link{import_nsp}}, or
138343# ' \code{'nev/nsx'} object
0 commit comments