Source code for finesse.components.signal

"""Signal-type electrical component for producing signal inputs."""
from finesse.components.node import NodeType, Node, Port, SignalNode
from finesse.components.general import Connector
from finesse.parameter import float_parameter, info_parameter
from finesse.components.modal.signal import siggen_fill_rhs, SignalGeneratorWorkspace
from finesse.components.dof import DegreeOfFreedom
from finesse.exceptions import FinesseException


[docs]@float_parameter("amplitude", "Amplitude", units="arb") @float_parameter("phase", "Phase", units="degrees") @info_parameter("f", "Frequency", units="Hz") # IMPORTANT: renaming this class impacts the katscript spec and should be avoided! class SignalGenerator(Connector): """Represents a signal generator which produces a signal with a given amplitude and phase. Parameters ---------- name : str Name of newly created signal generator. node : .class:`finesse.components.node.Node` A node to inject a signal into. amplitude : float, optional Amplitude of the signal in volts. phase : float, optional Phase-offset of the signal from the default in degrees, defaults to zero. """ def __init__(self, name, node, amplitude=1, phase=0): Connector.__init__(self, name) node = self._input_to_node(node) self._add_port("port", node.type) self.port._add_node("o", None, node) node.has_signal_injection = True self.amplitude = amplitude self.phase = phase def _input_to_node(self, node): if isinstance(node, str) and self._model is not None: node = self._model.get(node) if isinstance(node, DegreeOfFreedom): node = node.AC.i elif isinstance(node, Port): port = node if len(port.nodes) == 1: # single node ports, just use the only one we have node = port.nodes[0] else: # Else try and get a singular input node. is_input_node = tuple(_.is_input for _ in port.nodes) if is_input_node.count(True) == 1: idx = is_input_node.index(True) node = port.nodes[idx] else: raise Exception( f"Signal generator ({self.name}): Port `{port}` has more than 1 input node, please specify which to use." ) elif not isinstance(node, Node): raise Exception( f"Signal generator ({self.name}) input `{node!r}` should be a SignalNode not {type(node)}" ) if not isinstance(node, SignalNode): raise FinesseException(f"{node!r} is not a SignalNode") return node def _on_add(self, model): if model is not self.node._model: raise Exception( f"{repr(self)} is using a node {self.node} from a different model" ) @property def node(self): """Change the node the signal generator injects into. :setter: Change the node the signal generator injects into. """ return self.port.o @node.setter def node(self, value): """Set which signal node this generator should inject into.""" if self._model.is_built: raise FinesseException("Cannot change whilst the model is being run.") # Get new node object and change it over new_node = self._input_to_node(value) self.port._replace_node("o", new_node) new_node.has_signal_injection = True @property def f(self): """Signal frequency being injected, is always the `fsig` of the model.""" return self._model.fsig.f def _get_workspace(self, sim): if sim.signal: ws = SignalGeneratorWorkspace(self, sim) ws.rhs_idx = ws.sim.signal.field(self.port.o, 0, 0) ws.signal.set_fill_rhs_fn(siggen_fill_rhs) if self.port.o.type is NodeType.MECHANICAL: ws.scaling = 1 / sim.model_settings.x_scale else: ws.scaling = 1 return ws else: return None