Source code for psj_lib.devices.base.capabilities.static_waveform_generator

"""Static waveform generator for periodic signal generation."""

from .piezo_capability import PiezoCapability


[docs] class StaticWaveformGenerator(PiezoCapability): """Generate periodic waveforms for actuator modulation. The static waveform generator produces continuous periodic signals (sine, square, triangle, etc.) that can be used for: - Scanning applications - Vibration testing - Frequency response characterization - Dynamic positioning Configurable parameters: - Frequency: Rate of oscillation (Hz) - Amplitude: Peak-to-peak magnitude - Offset: DC bias level - Duty cycle: Pulse width for square/pulse waveforms The generated waveform can typically be used as the primary control signal. Example: >>> wfg = channel.static_waveform_generator >>> # Generate 10 Hz sine wave, 20µm amplitude, centered at 50µm >>> await wfg.set( ... frequency=10.0, ... amplitude=20.0, ... offset=50.0 ... ) >>> # Create square wave with 30% duty cycle >>> await wfg.set( ... frequency=5.0, ... duty_cycle=30.0 # 30% high time ... ) >>> # Query current settings >>> freq = await wfg.get_frequency() >>> amp = await wfg.get_amplitude() >>> print(f"Generating {freq} Hz, ±{amp/2} µm") Note: - May require modulation source selection to use waveform output - Waveform type (sine/square/triangle) is device-specific - Frequency is limited by device output current and actuator resonance frequency - Amplitude is limited by actuator travel range """ CMD_FREQUENCY = "STATIC_WAVEFORM_FREQUENCY" CMD_AMPLITUDE = "STATIC_WAVEFORM_AMPLITUDE" CMD_OFFSET = "STATIC_WAVEFORM_OFFSET" CMD_DUTY_CYCLE = "STATIC_WAVEFORM_DUTY_CYCLE"
[docs] async def set( self, frequency: float | None = None, amplitude: float | None = None, offset: float | None = None, duty_cycle: float | None = None ) -> None: """Configure waveform generator parameters. Args: frequency: Oscillation frequency in Hz amplitude: Amplitude in position units (typically µm or V) offset: DC offset/center position in position units (typically µm or V) duty_cycle: Pulse width percentage (0-100%) Example: >>> # Slow sine wave scan >>> await wfg.set( ... frequency=0.5, # 0.5 Hz (2 second period) ... amplitude=100.0, # 100µm amplitude ... offset=50.0 # Centered at 50µm ... ) >>> >>> # Fast square wave with asymmetric duty cycle >>> await wfg.set( ... frequency=100.0, ... amplitude=10.0, ... duty_cycle=25.0 # 25% high, 75% low ... ) Note: - Only provided parameters are updated - Amplitude is amplitude - Actual range: [offset - amplitude, offset + amplitude] - Frequency is limited by device output current and actuator resonance frequency - Amplitude is limited by actuator travel range """ if frequency is not None: await self._write(self.CMD_FREQUENCY, [frequency]) if amplitude is not None: await self._write(self.CMD_AMPLITUDE, [amplitude]) if offset is not None: await self._write(self.CMD_OFFSET, [offset]) if duty_cycle is not None: await self._write(self.CMD_DUTY_CYCLE, [duty_cycle])
[docs] async def get_frequency(self) -> float: """Get waveform frequency. Returns: Frequency in Hz Example: >>> freq = await wfg.get_frequency() >>> period = 1.0 / freq >>> print(f"Period: {period*1000:.1f} ms") """ result = await self._write(self.CMD_FREQUENCY) return float(result[0])
[docs] async def get_amplitude(self) -> float: """Get waveform amplitude. Returns: Amplitude in position units (typically µm or V) Example: >>> amp = await wfg.get_amplitude() >>> print(f"Oscillating ±{amp:.1f} µm from center") """ result = await self._write(self.CMD_AMPLITUDE) return float(result[0])
[docs] async def get_offset(self) -> float: """Get waveform DC offset (center position). Returns: Offset in position units (typically µm or V) Example: >>> offset = await wfg.get_offset() >>> amp = await wfg.get_amplitude() >>> print(f"Range: {offset-amp:.1f} to {offset+amp:.1f} µm") """ result = await self._write(self.CMD_OFFSET) return float(result[0])
[docs] async def get_duty_cycle(self) -> float: """Get pulse duty cycle percentage. Returns: Duty cycle in percent (0-100%) Example: >>> duty = await wfg.get_duty_cycle() >>> print(f"Pulse: {duty:.0f}% high, {100-duty:.0f}% low") Note: - 50% = symmetric wave """ result = await self._write(self.CMD_DUTY_CYCLE) return float(result[0])