ezmsg.blackrock.channel_map#

Attach Blackrock .cmp channel-map metadata to an AxisArray’s ch axis.

The output ch axis is a structured CoordinateAxis with fields x, y, size, label, bank, elec, headstage for every input channel. x/y/size are in micrometers; headstage is the 1-based headstage id (0 = none/auto).

ChannelMapUnit takes the complete set of per-headstage overlays in one settings object (ChannelMapUnitSettings, a tuple of ChannelMapSettings) and rebuilds the ch axis from scratch on each reset. One settings push = the whole map, applied deterministically — there is no cross-push accumulation that could coalesce if pushes aren’t separated by a data message. An empty tuple clears the map (pure auto-grid).

Each reset proceeds in three phases:

  1. Base layer — labels are pulled from the incoming ch axis. When the incoming axis already carries structured geometry (e.g. a CereLink source that read it from device chaninfo), its x/y/size/bank/ elec/headstage are copied through verbatim, so a map already present upstream needs no .cmp file at all. A channel counts as positioned (and so is skipped by the auto-grid) when it has a non-origin coordinate, or it is the first channel sitting at the (0, 0) origin — a lone origin electrode is a legitimate corner, but the device parks every unmapped channel at the origin, so origin pile-ups beyond the first fall through to the auto-grid. A companion src_mask records the positioned indices.

  2. CMP overlays — for each ChannelMapSettings in cmp_configs, entries from pycbsdk.cmp.parse_cmp() are written at their channel index, overriding any source geometry there. parse_cmp (CerebusOSS/CereLink#184) returns entries keyed by device (bank, term) with flat x/y/size/headstage fields (x/y in micrometers) and verbatim labels; the channel index is (bank - 1) * 32 + (term - 1)start_chan is already folded into bank via its // 32 offset. A companion cmp_mask records which indices were set so the auto-grid pass can avoid them.

  3. Auto-grid fill — positions/bank/elec for indices covered by neither a CMP overlay nor a source position, laid out below and to the right of the placed geometry so they don’t collide with it. The grid step matches the placed electrode pitch (inferred from its coordinates), so auto-laid channels share the same micrometer scale.

The same ChannelMapSettings record is also used as a per-headstage entry in CereLinkSignalSettings.cmp_configs.

Classes

class ChannelMapProcessor(*args, **kwargs)[source]#

Bases: BaseStatefulTransformer[ChannelMapUnitSettings, AxisArray, AxisArray, ChannelMapState]

Stateful transformer that attaches CMP-derived channel metadata.

Each reset rebuilds the axis in full from settings.cmp_configs: a base layer from incoming labels, every CMP overlay applied in order, then the auto-grid fills indices no CMP claimed. Reset fires on a channel-count change or any cmp_configs change (the latter via BaseProcessor.update_settings_request_reset), so there is no cross-push state to coalesce. An empty cmp_configs yields a pure auto-grid.

class ChannelMapSettings(filepath: str | None = None, start_chan: int = 1, hs_id: int = 0)[source]#

Bases: Settings

Parameters:
  • filepath (str | None)

  • start_chan (int)

  • hs_id (int)

filepath: str | None = None#

Path to the .cmp file. None (or an empty path) means no CMP — the auto-grid fallback generates coordinates for every channel.

start_chan: int = 1#

1-based channel ID assigned to the first sorted CMP row. Mirrors pycbsdk.Session.load_channel_map().

hs_id: int = 0#

Headstage identifier, passed through to pycbsdk.cmp.parse_cmp(), where it sets each entry’s headstage field. Labels are taken verbatim (no "hs{hs_id}-" prefix); bank/elec disambiguate channels that reuse a label across headstages. Pass 0 for single-headstage rigs.

__init__(filepath=None, start_chan=1, hs_id=0)#
Parameters:
  • filepath (str | None)

  • start_chan (int)

  • hs_id (int)

Return type:

None

class ChannelMapState[source]#

Bases: object

channel_axis: CoordinateAxis | None = None#
cmp_mask: ndarray | None = None#
src_mask: ndarray | None = None#
class ChannelMapUnit(*args, settings=None, **kwargs)[source]#

Bases: BaseTransformerUnit[ChannelMapUnitSettings, AxisArray, AxisArray, ChannelMapProcessor]

Parameters:

settings (Settings | None)

SETTINGS#

alias of ChannelMapUnitSettings

class ChannelMapUnitSettings(cmp_configs: tuple[ChannelMapSettings, ...] = ())[source]#

Bases: Settings

Parameters:

cmp_configs (tuple[ChannelMapSettings, ...])

cmp_configs: tuple[ChannelMapSettings, ...] = ()#

Per-headstage overlays, applied in order on each reset. Empty (the default) means no CMP — the auto-grid lays out every channel.

__init__(cmp_configs=())#
Parameters:

cmp_configs (tuple[ChannelMapSettings, ...])

Return type:

None

class ChannelMapSettings(filepath: str | None = None, start_chan: int = 1, hs_id: int = 0)[source]#

Bases: Settings

Parameters:
  • filepath (str | None)

  • start_chan (int)

  • hs_id (int)

filepath: str | None = None#

Path to the .cmp file. None (or an empty path) means no CMP — the auto-grid fallback generates coordinates for every channel.

start_chan: int = 1#

1-based channel ID assigned to the first sorted CMP row. Mirrors pycbsdk.Session.load_channel_map().

hs_id: int = 0#

Headstage identifier, passed through to pycbsdk.cmp.parse_cmp(), where it sets each entry’s headstage field. Labels are taken verbatim (no "hs{hs_id}-" prefix); bank/elec disambiguate channels that reuse a label across headstages. Pass 0 for single-headstage rigs.

__init__(filepath=None, start_chan=1, hs_id=0)#
Parameters:
  • filepath (str | None)

  • start_chan (int)

  • hs_id (int)

Return type:

None

class ChannelMapUnitSettings(cmp_configs: tuple[ChannelMapSettings, ...] = ())[source]#

Bases: Settings

Parameters:

cmp_configs (tuple[ChannelMapSettings, ...])

cmp_configs: tuple[ChannelMapSettings, ...] = ()#

Per-headstage overlays, applied in order on each reset. Empty (the default) means no CMP — the auto-grid lays out every channel.

__init__(cmp_configs=())#
Parameters:

cmp_configs (tuple[ChannelMapSettings, ...])

Return type:

None

class ChannelMapState[source]#

Bases: object

channel_axis: CoordinateAxis | None = None#
cmp_mask: ndarray | None = None#
src_mask: ndarray | None = None#
class ChannelMapProcessor(*args, **kwargs)[source]#

Bases: BaseStatefulTransformer[ChannelMapUnitSettings, AxisArray, AxisArray, ChannelMapState]

Stateful transformer that attaches CMP-derived channel metadata.

Each reset rebuilds the axis in full from settings.cmp_configs: a base layer from incoming labels, every CMP overlay applied in order, then the auto-grid fills indices no CMP claimed. Reset fires on a channel-count change or any cmp_configs change (the latter via BaseProcessor.update_settings_request_reset), so there is no cross-push state to coalesce. An empty cmp_configs yields a pure auto-grid.

class ChannelMapUnit(*args, settings=None, **kwargs)[source]#

Bases: BaseTransformerUnit[ChannelMapUnitSettings, AxisArray, AxisArray, ChannelMapProcessor]

Parameters:

settings (Settings | None)

SETTINGS#

alias of ChannelMapUnitSettings