Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
3,763 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
from abc import ABC, abstractmethod | ||
from typing import Optional | ||
import weakref | ||
|
||
from qupulse.hardware.awgs.base import AWG | ||
from qupulse.hardware.feature_awg.base_features import Feature, FeatureAble | ||
from qupulse.utils.types import Collection | ||
|
||
__all__ = ["AWGDevice", "AWGChannelTuple", "AWGChannel", "AWGMarkerChannel", "AWGDeviceFeature", "AWGChannelFeature", | ||
"AWGChannelTupleFeature"] | ||
|
||
|
||
class AWGDeviceFeature(Feature): | ||
"""Base class for features that are used for `AWGDevice`s""" | ||
def __init__(self): | ||
super().__init__(AWGDevice) | ||
|
||
|
||
class AWGChannelFeature(Feature): | ||
"""Base class for features that are used for `AWGChannel`s""" | ||
def __init__(self): | ||
super().__init__(_BaseAWGChannel) | ||
|
||
|
||
class AWGChannelTupleFeature(Feature): | ||
"""Base class for features that are used for `AWGChannelTuple`s""" | ||
def __init__(self, channel_tuple: 'AWGChannelTuple'): | ||
super().__init__(AWGChannelTuple) | ||
self._channel_tuple = weakref.proxy(channel_tuple) | ||
|
||
|
||
class AWGDevice(FeatureAble[AWGDeviceFeature], ABC): | ||
"""Base class for all drivers of all arbitrary waveform generators""" | ||
|
||
def __init__(self, name: str): | ||
""" | ||
Args: | ||
name: The name of the device as a String | ||
""" | ||
super().__init__() | ||
self._name = name | ||
|
||
@property | ||
def name(self) -> str: | ||
"""Returns the name of a Device as a String""" | ||
return self._name | ||
|
||
@abstractmethod | ||
def cleanup(self) -> None: | ||
"""Function for cleaning up the dependencies of the device""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def channels(self) -> Collection["AWGChannel"]: | ||
"""Returns a list of all channels of a Device""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def marker_channels(self) -> Collection["AWGMarkerChannel"]: | ||
"""Returns a list of all marker channels of a device. The collection may be empty""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def channel_tuples(self) -> Collection["AWGChannelTuple"]: | ||
"""Returns a list of all channel tuples of a list""" | ||
raise NotImplementedError() | ||
|
||
|
||
class AWGChannelTuple(FeatureAble[AWGChannelTupleFeature], ABC): | ||
"""Base class for all groups of synchronized channels of an AWG""" | ||
|
||
def __init__(self, idn: int): | ||
""" | ||
Args: | ||
idn: The identification number of a channel tuple | ||
""" | ||
super().__init__() | ||
|
||
self._idn = idn | ||
|
||
@property | ||
@abstractmethod | ||
def channel_tuple_adapter(self) -> AWG: | ||
"""Return old interface adapter object. See channel_tuple_wrapper for details.""" | ||
|
||
@property | ||
def idn(self) -> int: | ||
"""Returns the identification number of a channel tuple""" | ||
return self._idn | ||
|
||
@property | ||
def name(self) -> str: | ||
"""Returns the name of a channel tuple""" | ||
return "{dev}_CT{idn}".format(dev=self.device.name, idn=self.idn) | ||
|
||
@property | ||
@abstractmethod | ||
def sample_rate(self) -> float: | ||
"""Returns the sample rate of a channel tuple as a float""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def device(self) -> AWGDevice: | ||
"""Returns the device which the channel tuple belong to""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def channels(self) -> Collection["AWGChannel"]: | ||
"""Returns a list of all channels of the channel tuple""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def marker_channels(self) -> Collection["AWGMarkerChannel"]: | ||
"""Returns a list of all marker channels of the channel tuple. The collection may be empty""" | ||
raise NotImplementedError() | ||
|
||
|
||
class _BaseAWGChannel(FeatureAble[AWGChannelFeature], ABC): | ||
"""Base class for a single channel of an AWG""" | ||
|
||
def __init__(self, idn: int): | ||
""" | ||
Args: | ||
idn: The identification number of a channel | ||
""" | ||
super().__init__() | ||
self._idn = idn | ||
|
||
@property | ||
def idn(self) -> int: | ||
"""Returns the identification number of a channel""" | ||
return self._idn | ||
|
||
@property | ||
@abstractmethod | ||
def device(self) -> AWGDevice: | ||
"""Returns the device which the channel belongs to""" | ||
raise NotImplementedError() | ||
|
||
@property | ||
@abstractmethod | ||
def channel_tuple(self) -> Optional[AWGChannelTuple]: | ||
"""Returns the channel tuple which a channel belongs to""" | ||
raise NotImplementedError() | ||
|
||
@abstractmethod | ||
def _set_channel_tuple(self, channel_tuple) -> None: | ||
""" | ||
Sets the channel tuple which a channel belongs to | ||
Args: | ||
channel_tuple: reference to the channel tuple | ||
""" | ||
raise NotImplementedError() | ||
|
||
|
||
class AWGChannel(_BaseAWGChannel, ABC): | ||
"""Base class for a single channel of an AWG""" | ||
@property | ||
def name(self) -> str: | ||
"""Returns the name of a channel""" | ||
return "{dev}_C{idn}".format(dev=self.device.name, idn=self.idn) | ||
|
||
|
||
class AWGMarkerChannel(_BaseAWGChannel, ABC): | ||
"""Base class for a single marker channel of an AWG""" | ||
@property | ||
def name(self) -> str: | ||
"""Returns the name of a marker channel""" | ||
return "{dev}_M{idn}".format(dev=self.device.name, idn=self.idn) | ||
|
||
|
||
class ProgramOverwriteException(RuntimeError): | ||
|
||
def __init__(self, name) -> None: | ||
super().__init__() | ||
self.name = name | ||
|
||
def __str__(self) -> str: | ||
return "A program with the given name '{}' is already present on the device." \ | ||
" Use force to overwrite.".format(self.name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
from types import MappingProxyType | ||
from typing import Callable, Generic, Mapping, Optional, Type, TypeVar | ||
from abc import ABC | ||
|
||
__all__ = ["Feature", "FeatureAble"] | ||
|
||
|
||
class Feature: | ||
""" | ||
Base class for features of `FeatureAble`s. | ||
""" | ||
def __init__(self, target_type: Type["FeatureAble"]): | ||
self._target_type = target_type | ||
|
||
@property | ||
def target_type(self) -> Type["FeatureAble"]: | ||
return self._target_type | ||
|
||
|
||
FeatureType = TypeVar("FeatureType", bound=Feature) | ||
GetItemFeatureType = TypeVar("GetItemFeatureType", bound=Feature) | ||
|
||
|
||
class FeatureAble(Generic[FeatureType]): | ||
""" | ||
Base class for all types that are able to handle features. The features are saved in a dictionary and the methods | ||
can be called with the __getitem__-operator. | ||
""" | ||
|
||
def __init__(self): | ||
super().__init__() | ||
self._features = {} | ||
|
||
def __getitem__(self, feature_type: Type[GetItemFeatureType]) -> GetItemFeatureType: | ||
if isinstance(feature_type, str): | ||
return self._features[feature_type] | ||
if not isinstance(feature_type, type): | ||
raise TypeError("Expected type-object as key, got \"{ftt}\" instead".format( | ||
ftt=type(feature_type).__name__)) | ||
key_type = _get_base_feature_type(feature_type) | ||
if key_type is None: | ||
raise TypeError("Unexpected type of feature: {ft}".format(ft=feature_type.__name__)) | ||
if key_type not in self._features: | ||
raise KeyError("Could not get feature for type: {ft}".format(ft=feature_type.__name__)) | ||
return self._features[key_type] | ||
|
||
def add_feature(self, feature: FeatureType) -> None: | ||
""" | ||
The method adds the feature to a Dictionary with all features | ||
Args: | ||
feature: A certain feature which functions should be added to the dictionary _features | ||
""" | ||
feature_type = _get_base_feature_type(type(feature)) | ||
if feature_type is None: | ||
raise TypeError("Unexpected type of feature: {ft}".format(ft=type(feature).__name__)) | ||
if not isinstance(self, feature.target_type): | ||
raise TypeError("Features with type \"{ft}\" belong to \"{tt}\"-objects".format( | ||
ft=type(feature).__name__, tt=feature.target_type.__name__)) | ||
if feature_type in self._features: | ||
raise KeyError(self, "Feature with type \"{ft}\" already exists".format(ft=feature_type.__name__)) | ||
self._features[feature_type] = feature | ||
# Also adding the feature with the string as the key. With this you can you the name as a string for __getitem__ | ||
self._features[feature_type.__name__] = feature | ||
|
||
@property | ||
def features(self) -> Mapping[FeatureType, Callable]: | ||
"""Returns the dictionary with all features of a FeatureAble""" | ||
return MappingProxyType(self._features) | ||
|
||
|
||
def _get_base_feature_type(feature_type: Type[Feature]) -> Type[Optional[Feature]]: | ||
""" | ||
This function searches for the second inheritance level under `Feature` (i.e. level under `AWGDeviceFeature`, | ||
`AWGChannelFeature` or `AWGChannelTupleFeature`). This is done to ensure, that nobody adds the same feature | ||
twice, but with a type of a different inheritance level as key. | ||
Args: | ||
feature_type: Type of the feature | ||
Returns: | ||
Base type of the feature_type, two inheritance levels under `Feature` | ||
""" | ||
if not issubclass(feature_type, Feature): | ||
return type(None) | ||
|
||
# Search for base class on the inheritance line of Feature | ||
for base in feature_type.__bases__: | ||
if issubclass(base, Feature): | ||
result_type = base | ||
break | ||
else: | ||
return type(None) | ||
|
||
if Feature in result_type.__bases__: | ||
return feature_type | ||
else: | ||
return _get_base_feature_type(result_type) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from typing import Tuple, Optional, Callable, Set | ||
|
||
from qupulse import ChannelID | ||
from qupulse._program._loop import Loop | ||
from qupulse.hardware.feature_awg.base import AWGChannelTuple | ||
from qupulse.hardware.feature_awg.features import ProgramManagement, VolatileParameters | ||
from qupulse.hardware.awgs.base import AWG | ||
|
||
|
||
class ChannelTupleAdapter(AWG): | ||
""" | ||
This class serves as an adapter between the old Class AWG and the new driver abstraction. It routes all the methods | ||
the AWG class to the corresponding methods of the new driver. | ||
""" | ||
def __copy__(self) -> None: | ||
pass | ||
|
||
def __init__(self, channel_tuple: AWGChannelTuple): | ||
super().__init__(channel_tuple.name) | ||
self._channel_tuple = channel_tuple | ||
|
||
@property | ||
def num_channels(self) -> int: | ||
return len(self._channel_tuple.channels) | ||
|
||
@property | ||
def num_markers(self) -> int: | ||
return len(self._channel_tuple.marker_channels) | ||
|
||
def upload(self, name: str, | ||
program: Loop, | ||
channels: Tuple[Optional[ChannelID], ...], | ||
markers: Tuple[Optional[ChannelID], ...], | ||
voltage_transformation: Tuple[Optional[Callable], ...], | ||
force: bool = False) -> None: | ||
return self._channel_tuple[ProgramManagement].upload(name=name, program=program, | ||
channels=channels, | ||
marker_channels=markers, | ||
voltage_transformation=voltage_transformation, | ||
repetition_mode=None, | ||
force=force) | ||
|
||
def remove(self, name: str) -> None: | ||
return self._channel_tuple[ProgramManagement].remove(name) | ||
|
||
def clear(self) -> None: | ||
return self._channel_tuple[ProgramManagement].clear() | ||
|
||
def arm(self, name: Optional[str]) -> None: | ||
return self._channel_tuple[ProgramManagement].arm(name) | ||
|
||
@property | ||
def programs(self) -> Set[str]: | ||
return self._channel_tuple[ProgramManagement].programs | ||
|
||
@property | ||
def sample_rate(self) -> float: | ||
return self._channel_tuple.sample_rate | ||
|
||
def set_volatile_parameters(self, program_name: str, parameters): | ||
self._channel_tuple[VolatileParameters].set_volatile_parameters(program_name, parameters) | ||
|
Oops, something went wrong.