"""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