[docs]classPhysicalScenario(Generic[PDT],Scenario[PDT,DeviceState,Drop]):"""Scenario of physical device bindings. Managing physical devices by a scenario enables synchronized triggering and shared random seed configuration. """def__init__(self,seed:int|None=None,devices:Sequence[PDT]|None=None)->None:Scenario.__init__(self,seed,devices)@classmethod@overridedef_drop_type(cls)->type[Drop]:returnDrop@abstractmethoddef_trigger(self)->None:"""Trigger synchronzed transmission and reception for all managed devices."""...# pragma no cover@abstractmethoddef_trigger_direct(self,transmissions:list[Signal],devices:list[PDT],calibrate:bool=True)->list[Signal]:"""Trigger a synchronized transmission and reception for a selection of managed devices. Subroutine of :meth:`PhysicalScenario.trigger_direct<hermespy.hardware_loop.scenario.PhysicalScenario.trigger_direct>`. Args: transmissions: List of signals to be transmitted. devices: List of devices to be triggered. calibrate: Apply device calibrations during transmission and reception. Returns: List of signals received by all devices. """...# pragma: no cover
[docs]deftrigger_direct(self,transmissions:list[Signal],devices:list[PDT]|None=None,calibrate:bool=True)->list[Signal]:"""Trigger a synchronized transmission and reception for all managed devices. Args: transmissions: List of signals to be transmitted. devices: List of devices to be triggered. calibrate: Apply device calibrations during transmission and reception. Returns: List of signals received by all devices. """_devices=self.devicesifdevicesisNoneelsedevicesiflen(transmissions)!=len(_devices):raiseValueError(f"The number of transmissions does not match the number of devices ({len(transmissions)} != {len(_devices)})")returnself._trigger_direct(transmissions,_devices,calibrate)
[docs]defreceive_devices(self,impinging_signals:(Sequence[DeviceInput]|Sequence[Signal]|Sequence[Sequence[Signal]]|None)=None,states:Sequence[DeviceState|None]|None=None,notify:bool=True,)->Sequence[DeviceReception]:"""Receive over all scenario devices. Internally calls :meth:`Scenario.process_inputs<hermespy.core.scenario.Scenario.process_inputs>` and :meth:`Scenario.receive_devices<hermespy.core.scenario.Scenario.receive_devices>`. Args: impinging_signals: List of signals impinging onto the devices. If not specified, the device will download the signal samples from its binding. states: States of the transmitting devices. If not specified, the current device states will be queried by calling :meth:`Device.state<hermespy.core.device.Device.state>`. notify: Notify the receiving DSP layer's callbacks about the reception results. Enabled by default. Returns: List of the processed device input information. Raises: ValueError: If the number of `impinging_signals` does not match the number of registered devices. """_impinging_signals=([None]*self.num_devicesifimpinging_signalsisNoneelseimpinging_signals)_states=[None]*self.num_devicesifstatesisNoneelsestates# Generate inputsdevice_inputs=[d.process_input(i,s)ford,s,iinzip(self.devices,_states,_impinging_signals)]# type: ignore# Generate operator receptionsreceptions=self.receive_operators(device_inputs,_states,notify)# Generate device receptionsreturn[DeviceReception.From_ProcessedDeviceInput(i,r)fori,rinzip(device_inputs,receptions)]
@overridedef_drop(self)->Drop:# Generate device transmissionsdevice_transmissions=self.transmit_devices()# Trigger the full scenario for phyiscal transmission and receptiontimestamp=time()self._trigger()# Generate device receptionsdevice_receptions=self.receive_devices()returnDrop(timestamp,device_transmissions,device_receptions)