Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/wirer external mixers #253

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions qualang_tools/wirer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ instruments.add_octave(indices=1)
instruments.add_lf_fem(controller=1, slots=[1])
instruments.add_mw_fem(controller=1, slots=[2])
```
#### Setups with External Mixers
Note: An **external mixer** is defined as abstractly as a combined, IQ-upconverter and IQ-downconverter instrument.
```python
# Single LF-FEM and 2x External Mixers
instruments.add_lf_fem(controller=1, slots=[1])
instruments.add_external_mixer(indices=[1, 2])
```
```python
# Single OPX+ and 2x External Mixers
instruments.add_opx_plus(controllers=[1, 2])
instruments.add_external_mixer(indices=[1, 2])
```
<details>
<summary>Image</summary>
<img alt="Empty OPX1000" src=".img/empty_opx1000.png">
Expand Down
39 changes: 35 additions & 4 deletions qualang_tools/wirer/instruments/instrument_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,32 @@ class InstrumentChannelAnalog:
signal_type = "analog"


InstrumentIdType = Literal["lf-fem", "mw-fem", "opx+", "octave", "external-mixer"]


@dataclass(eq=False)
class InstrumentChannelLfFem:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "lf-fem"
instrument_id: InstrumentIdType = "lf-fem"


@dataclass(eq=False)
class InstrumentChannelMwFem:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "mw-fem"
instrument_id: InstrumentIdType = "mw-fem"


@dataclass(eq=False)
class InstrumentChannelOpxPlus:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "opx+"
instrument_id: InstrumentIdType = "opx+"


@dataclass(eq=False)
class InstrumentChannelOctave:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "octave"
instrument_id: InstrumentIdType = "octave"


@dataclass(eq=False)
class InstrumentChannelExternalMixer:
instrument_id: InstrumentIdType = "external-mixer"


@dataclass(eq=False)
Expand Down Expand Up @@ -122,6 +130,20 @@ class InstrumentChannelOpxPlusDigitalOutput(
pass


@dataclass(eq=False)
class InstrumentChannelExternalMixerInput(
InstrumentChannelAnalog, InstrumentChannelExternalMixer, InstrumentChannelInput, InstrumentChannel
):
pass


@dataclass(eq=False)
class InstrumentChannelExternalMixerOutput(
InstrumentChannelAnalog, InstrumentChannelExternalMixer, InstrumentChannelOutput, InstrumentChannel
):
pass


@dataclass(eq=False)
class InstrumentChannelOctaveInput(
InstrumentChannelAnalog, InstrumentChannelOctave, InstrumentChannelInput, InstrumentChannel
Expand All @@ -143,6 +165,13 @@ class InstrumentChannelOctaveDigitalInput(
pass


@dataclass(eq=False)
class InstrumentChannelExternalMixerDigitalInput(
InstrumentChannelDigital, InstrumentChannelExternalMixer, InstrumentChannelInput, InstrumentChannel
):
pass


AnyInstrumentChannel = Union[
InstrumentChannelLfFemInput,
InstrumentChannelLfFemOutput,
Expand All @@ -152,4 +181,6 @@ class InstrumentChannelOctaveDigitalInput(
InstrumentChannelOpxPlusOutput,
InstrumentChannelOctaveInput,
InstrumentChannelOctaveOutput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
]
23 changes: 23 additions & 0 deletions qualang_tools/wirer/instruments/instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
InstrumentChannelOctaveInput,
InstrumentChannelOctaveOutput,
InstrumentChannelOctaveDigitalInput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
InstrumentChannelExternalMixerDigitalInput,
)
from .instrument_channels import *
from .constants import *
Expand All @@ -20,6 +23,26 @@ def __init__(self):
self.used_channels = InstrumentChannels()
self.available_channels = InstrumentChannels()

def add_external_mixer(self, indices: Union[List[int], int]):
"""
Add an external mixer, which is defined abstractly as a combined, IQ-upconverter and
IQ-downconverter.

`indices` (List[int] | int): Can be one or more indices for one or more external mixers.
"""
if isinstance(indices, int):
indices = [indices]

for index in indices:
channel = InstrumentChannelExternalMixerInput(con=index, port=1)
self.available_channels.add(channel)

channel = InstrumentChannelExternalMixerOutput(con=index, port=1)
self.available_channels.add(channel)

channel = InstrumentChannelExternalMixerDigitalInput(con=index, port=1)
self.available_channels.add(channel)

def add_octave(self, indices: Union[List[int], int]):
if isinstance(indices, int):
indices = [indices]
Expand Down
34 changes: 30 additions & 4 deletions qualang_tools/wirer/visualizer/instrument_figure_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ def get_ax(self, con: int, slot: int, instrument_id: str) -> Axes:
if instrument_id == "OPX1000":
fig, axs = self._make_opx1000_figure()
self.figures[key] = {i + 1: ax for i, ax in enumerate(axs)}
fig.suptitle(f"con{con} - {instrument_id} Wiring Diagram", fontweight="bold", fontsize=14)
fig.suptitle(f"con{con} - {instrument_id} Wiring", fontweight="bold", fontsize=14)
elif instrument_id == "OPX+":
fig = self._make_opx_plus_figure()
self.figures[key] = fig.axes[0]
fig.suptitle(f"con{con} - {instrument_id} Wiring Diagram", fontweight="bold", fontsize=14)
fig.suptitle(f"con{con} - {instrument_id} Wiring", fontweight="bold", fontsize=14)
elif instrument_id == "Octave":
fig = self._make_octave_figure()
self.figures[key] = fig.axes[0]
fig.suptitle(f"oct{con} - {instrument_id} Wiring Diagram", fontweight="bold", fontsize=14)
fig.suptitle(f"oct{con} - {instrument_id} Wiring", fontweight="bold", fontsize=14)
else:
raise NotImplementedError()
fig = self._make_external_mixer_figure()
self.figures[key] = fig.axes[0]
fig.suptitle(f"Mixers {con} Wiring", fontweight="bold", fontsize=14)

return self.figures[key][slot] if slot is not None else self.figures[key]

Expand Down Expand Up @@ -84,3 +86,27 @@ def _make_opx_plus_figure() -> Figure:
@classmethod
def _make_octave_figure(cls) -> Figure:
return cls._make_opx_plus_figure()

@classmethod
def _make_external_mixer_figure(cls) -> Figure:
fig, ax = plt.subplots(
1,
1,
figsize=(
INSTRUMENT_FIGURE_DIMENSIONS["Mixers"]["width"] * 2,
INSTRUMENT_FIGURE_DIMENSIONS["Mixers"]["height"] * 2,
),
)
ax.text(0.25, 0.25, "Up. RF", ha="center", va="center")
ax.text(0.25, 0.65, "Up. Mkr", ha="center")
ax.text(0.75, 0.25, "Down. RF", ha="center", va="center")
ax.set_ylim([0.15, 1.15])
# ax.set_xlim([0.15 / 8 * 3, 1.15 / 8 * 3])
ax.set_facecolor("darkgrey")
ax.set_xticks([])
ax.set_yticks([])
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_aspect("equal")

return fig
11 changes: 11 additions & 0 deletions qualang_tools/wirer/visualizer/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"mw-fem": "OPX1000",
"opx+": "OPX+",
"octave": "Octave",
"external-mixer": "Mixers",
}

# Define the chassis dimensions
INSTRUMENT_FIGURE_DIMENSIONS = {
"OPX1000": {"width": 8, "height": 3},
"OPX+": {"width": 8, "height": 1},
"Octave": {"width": 3, "height": 1},
"Mixers": {"width": 1, "height": 1},
}

OPX_PLUS_ASPECT = INSTRUMENT_FIGURE_DIMENSIONS["OPX+"]["height"] / INSTRUMENT_FIGURE_DIMENSIONS["OPX+"]["width"]
Expand Down Expand Up @@ -57,4 +59,13 @@
"input": [((0.3 + j * 0.06) * 3, 0.18) for j in range(5)],
},
},
"external-mixer": {
"analog": {
"input": [(0.75, 0.45)],
"output": [(0.25, 0.45)],
},
"digital": {
"input": [(0.25, 0.85)],
},
},
}
18 changes: 17 additions & 1 deletion qualang_tools/wirer/visualizer/port_annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def draw(self, ax: Axes):
if self.signal_type == "digital" and self.instrument_id in ["lf-fem", "mw-fem"]:
port_size = PORT_SIZE / 2.4
bbox = dict(facecolor=fill_color, alpha=0.8, edgecolor="none")
else:
elif self.instrument_id in ["opx+", "octave"]:
port_size = PORT_SIZE
port_label_distance = PORT_SIZE * 1.3
ax.text(
Expand All @@ -46,6 +46,22 @@ def draw(self, ax: Axes):
color=outline_colour,
)
bbox = None
elif self.instrument_id in ["external-mixer"]:
port_size = PORT_SIZE * 2.4
port_label_distance = PORT_SIZE * 3.2
ax.text(
x - port_label_distance,
y,
str(self.port),
ha="center",
va="center",
fontsize=8,
fontweight="bold",
color=outline_colour,
)
bbox = None
else:
raise NotImplementedError(f"No port-annotation drawing for {self.instrument_id}")

ax.add_patch(patches.Circle((x, y), port_size, edgecolor=outline_colour, facecolor=fill_color))
labels = combine_labels_for_same_line_type(self.labels)
Expand Down
64 changes: 64 additions & 0 deletions qualang_tools/wirer/wirer/channel_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
InstrumentChannelOpxPlusOutput,
InstrumentChannelOctaveInput,
InstrumentChannelOctaveOutput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
InstrumentChannelOpxPlusDigitalOutput,
InstrumentChannelMwFemDigitalOutput,
InstrumentChannelLfFemDigitalOutput,
InstrumentChannelOctaveDigitalInput,
InstrumentChannelExternalMixerDigitalInput,
)

# A channel template is a partially filled InstrumentChannel object
Expand Down Expand Up @@ -99,6 +102,15 @@ def __init__(self, index: int = None, rf_in: int = None, rf_out: int = None):
]


class ChannelSpecExternalMixer(ChannelSpec):
def __init__(self, index: int = None, rf_in: int = None, rf_out: int = None):
super().__init__()
self.channel_templates = [
InstrumentChannelExternalMixerInput(con=index, port=rf_in),
InstrumentChannelExternalMixerOutput(con=index, port=rf_out),
]


class ChannelSpecLfFemBasebandAndOctave(ChannelSpec):
def __init__(
self,
Expand All @@ -123,6 +135,28 @@ def __init__(
]


class ChannelSpecLfFemBasebandAndExternalMixer(ChannelSpec):
def __init__(
self,
con: int = None,
slot: int = None,
in_port_i: int = None,
in_port_q: int = None,
out_port_i: int = None,
out_port_q: int = None,
mixer_index: int = None,
):
super().__init__()
self.channel_templates = [
InstrumentChannelLfFemInput(con=con, slot=slot, port=in_port_i),
InstrumentChannelLfFemInput(con=con, slot=slot, port=in_port_q),
InstrumentChannelLfFemOutput(con=con, slot=slot, port=out_port_i),
InstrumentChannelLfFemOutput(con=con, slot=slot, port=out_port_q),
InstrumentChannelExternalMixerInput(con=mixer_index, port=1),
InstrumentChannelExternalMixerOutput(con=mixer_index, port=1),
]


class ChannelSpecOpxPlusBasebandAndOctave(ChannelSpec):
def __init__(
self,
Expand All @@ -146,6 +180,27 @@ def __init__(
]


class ChannelSpecOpxPlusBasebandAndExternalMixer(ChannelSpec):
def __init__(
self,
con: int = None,
in_port_i: int = None,
in_port_q: int = None,
out_port_i: int = None,
out_port_q: int = None,
mixer_index: int = None,
):
super().__init__()
self.channel_templates = [
InstrumentChannelOpxPlusInput(con=con, port=in_port_i),
InstrumentChannelOpxPlusInput(con=con, port=in_port_q),
InstrumentChannelOpxPlusOutput(con=con, port=out_port_i),
InstrumentChannelOpxPlusOutput(con=con, port=out_port_q),
InstrumentChannelExternalMixerInput(con=mixer_index, port=1),
InstrumentChannelExternalMixerOutput(con=mixer_index, port=1),
]


class ChannelSpecOpxPlusDigital(ChannelSpec):
def __init__(self, con: int = None, out_port: int = None):
super().__init__()
Expand All @@ -170,12 +225,21 @@ def __init__(self, con: int = None, in_port: int = None):
self.channel_templates = [InstrumentChannelOctaveDigitalInput(con=con, port=in_port)]


class ChannelSpecExternalMixerDigital(ChannelSpec):
def __init__(self, con: int = None, in_port: int = None):
super().__init__()
self.channel_templates = [InstrumentChannelExternalMixerDigitalInput(con=con, port=in_port)]


mw_fem_spec = ChannelSpecMwFemSingle
lf_fem_spec = ChannelSpecLfFemSingle
lf_fem_iq_spec = ChannelSpecLfFemBaseband
lf_fem_iq_octave_spec = ChannelSpecLfFemBasebandAndOctave
lf_fem_iq_ext_mixer_spec = ChannelSpecLfFemBasebandAndExternalMixer
opx_spec = ChannelSpecOpxPlusSingle
opx_iq_spec = ChannelSpecOpxPlusBaseband
opx_iq_octave_spec = ChannelSpecOpxPlusBasebandAndOctave
opx_iq_ext_mixer_spec = ChannelSpecOpxPlusBasebandAndExternalMixer
octave_spec = ChannelSpecOctave
ext_mixer_spec = ChannelSpecExternalMixer
opx_dig_spec = ChannelSpecOpxPlusDigital
15 changes: 15 additions & 0 deletions qualang_tools/wirer/wirer/wirer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
ChannelSpecMwFemSingle,
ChannelSpecLfFemBaseband,
ChannelSpecOctave,
ChannelSpecExternalMixer,
ChannelSpecOpxPlusBaseband,
ChannelSpecMwFemDigital,
ChannelSpecLfFemDigital,
ChannelSpecOctaveDigital,
ChannelSpecOpxPlusDigital,
ChannelSpecExternalMixerDigital,
)
from .wirer_assign_channels_to_spec import assign_channels_to_spec
from .wirer_exceptions import ConstraintsTooStrictException, NotEnoughChannelsException
Expand Down Expand Up @@ -98,9 +100,22 @@ def allocate_rf_channels(spec: WiringSpec, instruments: Instruments):
channels.
"""
rf_specs = [
# MW-FEM, Single RF output
ChannelSpecMwFemSingle() & ChannelSpecMwFemDigital(),
# LF-FEM I/Q output with Octave for upconversion
ChannelSpecLfFemBaseband() & ChannelSpecLfFemDigital() & ChannelSpecOctave() & ChannelSpecOctaveDigital(),
# LF-FEM I/Q output with External Mixer for upconversion
ChannelSpecLfFemBaseband()
& ChannelSpecLfFemDigital()
& ChannelSpecExternalMixer()
& ChannelSpecExternalMixerDigital(),
# OPX+ I/Q output with Octave for upconversion
ChannelSpecOpxPlusBaseband() & ChannelSpecOpxPlusDigital() & ChannelSpecOctave() & ChannelSpecOctaveDigital(),
# OPX+ I/Q output with External Mixer for upconversion
ChannelSpecOpxPlusBaseband()
& ChannelSpecOpxPlusDigital()
& ChannelSpecExternalMixer()
& ChannelSpecExternalMixerDigital(),
]

allocate_channels(spec, rf_specs, instruments, same_con=True, same_slot=True)
Expand Down
Loading
Loading