npc-sync 0.2.4__tar.gz → 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npc-sync
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Repackaging of AllenSDK.SyncDataset with extra functionality, compatible with data in the cloud.
5
5
  Author-email: Ben Hardcastle <ben.hardcastle@alleninstitue.org>
6
6
  License: MIT
@@ -16,7 +16,7 @@ dependencies = [
16
16
  "scipy>=1.13.1",
17
17
  "npc-stim>=0.1.14",
18
18
  ]
19
- version = "0.2.4"
19
+ version = "0.2.6"
20
20
  classifiers = [
21
21
  "Development Status :: 3 - Alpha",
22
22
  "Programming Language :: Python :: 3",
@@ -1104,6 +1104,31 @@ class SyncDataset:
1104
1104
  result[block_idx] = npc_stim.get_total_stim_frames(stim_data)
1105
1105
  return result
1106
1106
 
1107
+ def _get_diode_box_frame_interval(self, block_idx: int) -> int:
1108
+ """Return the number of frames between diode-box flips for a block.
1109
+
1110
+ Stim files created before ``diodeBoxFrameInterval`` was added always
1111
+ flipped the diode box every frame, so missing metadata defaults to 1.
1112
+ """
1113
+ path = self.block_index_to_stim_path[block_idx]
1114
+ if path is None:
1115
+ return 1
1116
+ stim_data = None
1117
+ try:
1118
+ stim_data = npc_stim.get_stim_data(path)
1119
+ value = stim_data.get("diodeBoxFrameInterval", 1)
1120
+ if isinstance(value, h5py.Dataset):
1121
+ value = value[()]
1122
+ return int(value)
1123
+ except Exception as exc:
1124
+ logger.warning(
1125
+ f"Failed to load diodeBoxFrameInterval for block {block_idx} at {path}: {exc}"
1126
+ )
1127
+ return 1
1128
+ finally:
1129
+ if isinstance(stim_data, h5py.File):
1130
+ stim_data.close()
1131
+
1107
1132
  @npc_io.cached_property
1108
1133
  def frame_display_time_blocks(self) -> tuple[npt.NDArray[np.floating], ...]:
1109
1134
  """Blocks of adjusted diode times: one block per stimulus."""
@@ -1252,12 +1277,7 @@ class SyncDataset:
1252
1277
  """Concatenate the rising and falling edges into a single array."""
1253
1278
  return np.sort(np.concatenate((rising, falling)))
1254
1279
 
1255
- # July 2025 we updated the photodiode interval from every frame to every 3 frames
1256
- vsyncs_per_diode_flip: int = (
1257
- 1
1258
- if self.start_time < datetime.datetime(2025, 7, 15)
1259
- else round(np.mean(np.diff(concat_flips(rising, falling))) * FRAME_RATE)
1260
- )
1280
+ vsyncs_per_diode_flip = self._get_diode_box_frame_interval(block_idx)
1261
1281
 
1262
1282
  if (
1263
1283
  vsyncs_per_diode_flip == 1
@@ -1312,6 +1332,14 @@ class SyncDataset:
1312
1332
 
1313
1333
  rising, falling = split_flips(flips, is_first_frame_on)
1314
1334
 
1335
+ if self.block_index_to_stim_path[block_idx] is None and os.getenv(
1336
+ "SYNC_ALLOW_MISSING_STIM_FILES"
1337
+ ):
1338
+ logger.debug(
1339
+ f"Block {block_idx} has no stim file available to verify number of vsyncs. Skipping."
1340
+ )
1341
+ frame_display_time_blocks.append(fallback_or_none)
1342
+ continue
1315
1343
  assert self.block_index_to_stim_path[block_idx] is not None
1316
1344
 
1317
1345
  # script_frame_intervals = h5py.File(self.block_index_to_stim_path[block_idx])['frameIntervals'][:]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npc-sync
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Repackaging of AllenSDK.SyncDataset with extra functionality, compatible with data in the cloud.
5
5
  Author-email: Ben Hardcastle <ben.hardcastle@alleninstitue.org>
6
6
  License: MIT
@@ -1,3 +1,7 @@
1
+ import pathlib
2
+
3
+ import h5py
4
+
1
5
  import npc_sync
2
6
 
3
7
 
@@ -28,3 +32,26 @@ def test_init_from_instance_and_meta_data() -> None:
28
32
  meta = s2.meta_data
29
33
  assert isinstance(meta, dict)
30
34
  assert "line_labels" in meta
35
+
36
+
37
+ def test_diode_box_frame_interval_is_read_from_stim_file(
38
+ tmp_path: pathlib.Path,
39
+ ) -> None:
40
+ current_stim = tmp_path / "current.hdf5"
41
+ with h5py.File(current_stim, "w") as stim_data:
42
+ stim_data["diodeBoxFrameInterval"] = 3
43
+
44
+ old_stim = tmp_path / "old.hdf5"
45
+ with h5py.File(old_stim, "w"):
46
+ pass
47
+
48
+ sync = object.__new__(npc_sync.SyncDataset)
49
+ sync.block_index_to_stim_path = {
50
+ 0: current_stim,
51
+ 1: old_stim,
52
+ 2: None,
53
+ }
54
+
55
+ assert sync._get_diode_box_frame_interval(0) == 3
56
+ assert sync._get_diode_box_frame_interval(1) == 1
57
+ assert sync._get_diode_box_frame_interval(2) == 1
File without changes
File without changes
File without changes
File without changes
File without changes