DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
HardwareAdapterSwabianInstrumentsPulseStreamer.cpp
Go to the documentation of this file.
1// This file is part of DynExp.
2
3#include "stdafx.h"
5
6namespace DynExpHardware
7{
25
38
48
49 std::unique_ptr<pulse_streamer::PulseMessage> SIPulseStreamerHardwareAdapter::PulseType::ToPulseMessage() const
50 {
51 auto Message = std::make_unique<pulse_streamer::PulseMessage>();
52
53 Message->set_ticks(ticks);
54 Message->set_digi(digi);
55 Message->set_ao0(ao0);
56 Message->set_ao1(ao1);
57
58 return Message;
59 }
60
62 {
63 auto ClippedVoltage = std::min(Voltage, 1.0);
64 ClippedVoltage = std::max(ClippedVoltage, -1.0);
65 ClippedVoltage *= static_cast<double>(0x7ff);
66
67 bool IsNeg = ClippedVoltage < 0.0;
68 int16_t Value = static_cast<int16_t>(std::floor(std::abs(ClippedVoltage)));
69 Value *= 16; // Shift 4 bits to the left.
70
71 return Value * (IsNeg ? int16_t(-1) : int16_t(1));
72 }
73
75 {
76 uint8_t DOValue = 0;
77
78 if (static_cast<std::underlying_type_t<SIPulseStreamerHardwareAdapterParams::OutputChannelType>>(Channel) <= 7)
79 DOValue |= (static_cast<bool>(Value) << static_cast<std::underlying_type_t<SIPulseStreamerHardwareAdapterParams::OutputChannelType>>(Channel));
80
81 return DOValue;
82 }
83
88
90 {
91 try
92 {
93 // Not locking, since the object is being destroyed. This should be inherently thread-safe.
96 }
97 catch (...)
98 {
99 // Swallow any exception in order not to cause abort on error.
100 }
101 }
102
109
116
123
125 const std::vector<SampleType>& NewSamples) const
126 {
128
129 SetSamplesUnsafe(OutputChannel, NewSamples);
130 }
131
133 {
135
137 }
138
146
153
160
162 {
164
165 return IsStreamingUnsafe();
166 }
167
169 {
171
172 return HasSequenceUnsafe();
173 }
174
176 {
178
179 return HasFinishedUnsafe();
180 }
181
183 {
184 // auto lock = AcquireLock(); not necessary here, since DynExp ensures that Object::Reset() can only
185 // be called if respective object is not in use.
186
187 Samples.clear();
188 NumRuns = -1; // By default, repeat pulse sequence forever.
189
190 if (IsOpenedUnsafe())
192
194 }
195
196 std::vector<SIPulseStreamerHardwareAdapter::PulseType> SIPulseStreamerHardwareAdapter::ComposePulseSequence() const
197 {
198 std::vector<PulseType> Pulses;
199
200 for (auto SampleIt = Samples.cbegin(); SampleIt != Samples.cend(); ++SampleIt)
201 {
202 // Duration does not matter for final pulse, thus set to zero in that case.
203 uint32_t Duration = (SampleIt + 1) != Samples.cend() ? Util::NumToT<uint32_t>(((SampleIt + 1)->Timestamp - SampleIt->Timestamp).count()) : 0;
204
205 if (Pulses.empty())
206 {
207 int16_t AO0Value = SampleIt->Channel == SIPulseStreamerHardwareAdapterParams::OutputChannelType::AO0 ? SampleIt->Value : 0;
208 int16_t AO1Value = SampleIt->Channel == SIPulseStreamerHardwareAdapterParams::OutputChannelType::AO1 ? SampleIt->Value : 0;
209
210 Pulses.emplace_back(Duration, SampleIt->ComposeDOValue(), AO0Value, AO1Value);
211 }
212 else
213 {
214 int16_t AO0Value = SampleIt->Channel == SIPulseStreamerHardwareAdapterParams::OutputChannelType::AO0 ? SampleIt->Value : Pulses.back().ao0;
215 int16_t AO1Value = SampleIt->Channel == SIPulseStreamerHardwareAdapterParams::OutputChannelType::AO1 ? SampleIt->Value : Pulses.back().ao1;
216
217 uint8_t DOValue = Pulses.back().digi;
218 if (static_cast<std::underlying_type_t<SIPulseStreamerHardwareAdapterParams::OutputChannelType>>(SampleIt->Channel) <= 7)
219 DOValue = (DOValue & ~(1 << static_cast<std::underlying_type_t<SIPulseStreamerHardwareAdapterParams::OutputChannelType>>(SampleIt->Channel))) | SampleIt->ComposeDOValue();
220
221 if (Pulses.back().ao0 == AO0Value && Pulses.back().ao1 == AO1Value && Pulses.back().digi == DOValue && (SampleIt + 1) != Samples.cend())
222 Pulses.back().ticks += Duration;
223 else
224 Pulses.emplace_back(Duration, DOValue, AO0Value, AO1Value);
225 }
226 }
227
228 return Pulses;
229 }
230
232 {
233 auto DerivedParams = dynamic_Params_cast<SIPulseStreamerHardwareAdapter>(GetParams());
234
235 SetTriggerUnsafe(DerivedParams->TriggerEdge, DerivedParams->TriggerMode);
236 SetNumRunsUnsafe(DerivedParams->NumRuns);
237 }
238
240 {
241 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::reset);
242 }
243
245 {
246 Samples.clear();
247
248 auto Message = Pulse.ToPulseMessage();
249
250 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::constant, *Message);
251 }
252
254 {
255 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::forceFinal);
256 }
257
259 const std::vector<SampleType>& NewSamples) const
260 {
261 if (NewSamples.empty())
262 ThrowExceptionUnsafe(std::make_exception_ptr(Util::EmptyException("NewSamples must not be empty.")));
263 if (std::find_if(NewSamples.cbegin(), NewSamples.cend(), [OutputChannel](const SampleType& Sample) { return Sample.Channel != OutputChannel; }) !=
264 NewSamples.cend())
265 ThrowExceptionUnsafe(std::make_exception_ptr(Util::InvalidDataException(
266 "The channel of at least one sample in NewSamples does not match the specified OutputChannel.")));
267
268 std::erase_if(Samples, [OutputChannel](const SampleType& Sample) { return Sample.Channel == OutputChannel; });
269 Samples.insert(Samples.end(), NewSamples.cbegin(), NewSamples.cend());
270 std::sort(Samples.begin(), Samples.end());
271
272 auto Pulses = ComposePulseSequence();
273 const auto FinalPulse = Pulses.back();
274 Pulses.pop_back();
275
276 if (!Pulses.empty())
277 {
278 pulse_streamer::SequenceMessage SequenceMessage;
279 for (const auto& Pulse : Pulses)
280 {
281 auto PulseMessage = SequenceMessage.add_pulse();
282 *PulseMessage = *Pulse.ToPulseMessage(); // TODO: Maybe some potential for speed optimization.
283 }
284
285 SequenceMessage.set_n_runs(NumRuns);
286 SequenceMessage.set_allocated_final(FinalPulse.ToPulseMessage().release());
287
288 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::stream, SequenceMessage);
289 }
290 else
291 SetConstantOutputUnsafe(FinalPulse);
292 }
293
295 {
296 this->NumRuns = NumRuns;
297 }
298
301 {
302 pulse_streamer::TriggerMessage Message;
303
304 switch (TriggerEdge)
305 {
306 case SIPulseStreamerHardwareAdapterParams::TriggerEdgeType::Software: Message.set_start(pulse_streamer::TriggerMessage_Start::TriggerMessage_Start_SOFTWARE); break;
307 case SIPulseStreamerHardwareAdapterParams::TriggerEdgeType::RisingEdge: Message.set_start(pulse_streamer::TriggerMessage_Start::TriggerMessage_Start_HARDWARE_RISING); break;
308 case SIPulseStreamerHardwareAdapterParams::TriggerEdgeType::FallingEdge: Message.set_start(pulse_streamer::TriggerMessage_Start::TriggerMessage_Start_HARDWARE_FALLING); break;
309 case SIPulseStreamerHardwareAdapterParams::TriggerEdgeType::RisingAndFallingEdge: Message.set_start(pulse_streamer::TriggerMessage_Start::TriggerMessage_Start_HARDWARE_RISING_AND_FALLING); break;
310 default: Message.set_start(pulse_streamer::TriggerMessage_Start::TriggerMessage_Start_IMMEDIATE);
311 }
312
313 switch (TriggerMode)
314 {
315 case SIPulseStreamerHardwareAdapterParams::TriggerModeType::Normal: Message.set_mode(pulse_streamer::TriggerMessage_Mode::TriggerMessage_Mode_NORMAL); break;
316 default: Message.set_mode(pulse_streamer::TriggerMessage_Mode::TriggerMessage_Mode_SINGLE);
317 }
318
319 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::setTrigger, Message);
320
321 }
322
324 {
325 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::startNow);
326 }
327
329 {
330 InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::rearm);
331 }
332
334 {
335 return InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::isStreaming);
336 }
337
339 {
340 return InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::hasSequence);
341 }
342
344 {
345 return InvokeStubFunc(&pulse_streamer::PulseStreamer::Stub::hasFinished);
346 }
347
349 {
350 if (lhs.Timestamp == rhs.Timestamp)
351 return std::strong_ordering::equal;
352 else if (lhs.Timestamp > rhs.Timestamp)
353 return std::strong_ordering::greater;
354 else
355 return std::strong_ordering::less;
356 }
357}
Implementation of a hardware adapter to control Swabian Instruments Pulse Streamer 8/2 hardware.
static Util::TextValueListType< OutputChannelType > OutputChannelTypeStrList()
void SetTrigger(SIPulseStreamerHardwareAdapterParams::TriggerEdgeType TriggerEdge, SIPulseStreamerHardwareAdapterParams::TriggerModeType TriggerMode=SIPulseStreamerHardwareAdapterParams::TriggerModeType::Normal) const
virtual void OpenUnsafeChild() override
Override to add additional initialization steps. Gets executed after the gRPC connection has been est...
void SetSamples(SIPulseStreamerHardwareAdapterParams::OutputChannelType OutputChannel, const std::vector< SampleType > &NewSamples) const
std::vector< SampleType > Samples
Combined sample vector of all channels. Assumed to be always sorted.
void SetSamplesUnsafe(SIPulseStreamerHardwareAdapterParams::OutputChannelType OutputChannel, const std::vector< SampleType > &NewSamples) const
SIPulseStreamerHardwareAdapter(const std::thread::id OwnerThreadID, DynExp::ParamsBasePtrType &&Params)
int64_t NumRuns
How often to repeat the sample sequence. -1 means indefinitely.
void SetTriggerUnsafe(SIPulseStreamerHardwareAdapterParams::TriggerEdgeType TriggerEdge, SIPulseStreamerHardwareAdapterParams::TriggerModeType TriggerMode=SIPulseStreamerHardwareAdapterParams::TriggerModeType::Normal) const
void ResetImpl(dispatch_tag< gRPCHardwareAdapter >) override final
uint32_t InvokeStubFunc(StubFuncPtrType< MessageType > Func, const MessageType &Message) const
This template class provides basic functionality to design hardware adapters for instruments which co...
bool IsOpenedUnsafe() const noexcept
Checks whether the gRPCHardwareAdapter is connected to a server.
static constexpr auto HardwareOperationTimeout
Default timeout used to lock the mutex provided by the base class Util::ILockable to synchronize acce...
void ThrowExceptionUnsafe(std::exception_ptr Exception) const
Stores Exception in LastException, wraps it in a Util::ForwardedException and throws the wrapped exce...
auto GetExceptionUnsafe() const
Getter for LastException.
ParamsConstTypeSyncPtrType GetParams(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Locks the mutex of the parameter class instance Params assigned to this Object instance and returns a...
Definition Object.cpp:436
const std::thread::id OwnerThreadID
Thread id of the thread which has constructed (and owns) this Object instance.
Definition Object.h:2302
const ParamsBasePtrType Params
Pointer to the parameter class instance belonging to this Object instance.
Definition Object.h:2303
Refer to ParamsBase::dispatch_tag.
Definition Object.h:2018
Thrown when a list is expected to contain entries and when a query results in an empty answer or an e...
Definition Exception.h:224
LockType AcquireLock(const std::chrono::milliseconds Timeout=DefaultTimeout) const
Locks the internal mutex. Blocks until the mutex is locked or until the timeout duration is exceeded.
Definition Util.cpp:8
Data to operate on is invalid for a specific purpose. This indicates a corrupted data structure or fu...
Definition Exception.h:163
DynExp's hardware namespace contains the implementation of DynExp hardware adapters which extend DynE...
std::strong_ordering operator<=>(const SIPulseStreamerHardwareAdapter::SampleType &lhs, const SIPulseStreamerHardwareAdapter::SampleType &rhs)
std::unique_ptr< ParamsBase > ParamsBasePtrType
Alias for a pointer to the parameter system base class ParamsBase.
Definition Object.h:1807
std::vector< std::pair< TextType, ValueType > > TextValueListType
Type of a list containing key-value pairs where key is a text of type Util::TextType.
Definition QtUtil.h:37
Accumulates include statements to provide a precompiled header.
Swabian Instruments Pulse Streamer 8/2's internal representation of a single pulse.
int16_t ao1
Analog out channel 1 (-0x7FFF is -1.0V, 0x7FFF is 1.0V)
uint8_t digi
Digital out bit mask (LSB is channel 0, MSB is channel 7)
int16_t ao0
Analog out channel 0 (-0x7FFF is -1.0V, 0x7FFF is 1.0V)