THM1176InstrumentManager 1.2
Qt Object abstraction for Metrolab THM1176
Loading...
Searching...
No Matches
THM1176.cpp
Go to the documentation of this file.
1// Copyright (c) 2025 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
2// See the included file LICENSE.txt for the licensing conditions.
3
7
8// Standard includes
9#include <regex>
10#include <thread>
11#include <chrono>
12#include <set>
13#include <iostream>
14
15// Personal includes
16#include "THM1176.h"
18#include "OSDefines.h"
19#include "Helpers.h"
20#include "Exception.h"
21#include "SCPIParsing.h"
22
23//----------------------------------------------------------------------//
24// Definitions //
25//----------------------------------------------------------------------//
26#define DEBUG_MTL_INSTRUMENT_THM1176 1
27#define DEBUG_MTL_INSTRUMENT_THM1176_ERRORS_ONLY 0
28#if (defined(_DEBUG) && defined(DEBUG_MTL_INSTRUMENT_THM1176) && DEBUG_MTL_INSTRUMENT_THM1176)
29 #if (defined(DEBUG_MTL_INSTRUMENT_THM1176_ERRORS_ONLY) && DEBUG_MTL_INSTRUMENT_THM1176_ERRORS_ONLY)
30 #define MTL_INSTRUMENT_THM1176_DEBUG_COUT(__X__)
31 #else
32 #define MTL_INSTRUMENT_THM1176_DEBUG_COUT(__X__) std::cout << __X__
33 #endif
34#define MTL_INSTRUMENT_THM1176_DEBUG_CERR(__X__) std::cerr << __X__
35#else
36 #define MTL_INSTRUMENT_THM1176_DEBUG_COUT(__X__)
37 #define MTL_INSTRUMENT_THM1176_DEBUG_CERR(__X__)
38#endif
39
42static const U32 THM1176_FILE_ACCESS_TIMEOUT (20000); // ms
43static const U32 THM1176_CALIBRATION_TIMEOUT (30000); // ms
44static const std::string THM1176_CALIBRATION_FILE_NAME ("cal.dat");
45static const std::string THM1176_INFO_FILE_NAME ("info.dat");
46static const U32 THM1176_CAL_FILE_OFFSET_VERSION (32); // bytes
47static const U32 THM1176_CAL_FILE_OFFSET_MATRIX_V2 (116); // bytes
48static const F64 THM1176_IMMEDIATE_TIME_PER_ACQ_A (1.0281823091218700E-04); // s
49static const F64 THM1176_IMMEDIATE_TIME_PER_ACQ_B (0.0000116103073008506); // s
50static const F64 THM1176_IMMEDIATE_TIME_PER_MEAS_A (4.4532792007542600E-05); // s
51static const F64 THM1176_IMMEDIATE_TIME_PER_MEAS_B (0.0000285930870825438); // s
52
53static const char * STATUS_SET_CMDS[4][3] =
54{
55 { "", "", "*SRE" }, // kStatusByte
56 { "", "", "*ESE" }, // kStandardEventStatusRegister
57 { "", "", ":STAT:QUES:ENAB" }, // kStatusQuestionableStatusRegister
58 { "", "", ":STAT:OPER:ENAB" } // kStatusOperationStatusRegister
59};
60
61static const char * STATUS_GET_CMDS[4][3] =
62{
63 { "*STB?", "", "*SRE?" }, // kStatusByte
64 { "*ESR?", "", "*ESE?" }, // kStandardEventStatusRegister
65 { ":STAT:QUES:EVEN?", ":STAT:QUES:COND?", ":STAT:QUES:ENAB?" }, // kStatusQuestionableStatusRegister
66 { ":STAT:OPER:EVEN?", ":STAT:OPER:COND?", ":STAT:OPER:ENAB?" } // kStatusOperationStatusRegister
67};
68
69static const std::set<std::string> MODELS_NOT_TO_CALIBRATE =
70{
71 std::string("TFM1186")
72};
73
74using namespace MTL::Instrument;
75using namespace MTL::SCPI;
76using namespace MTL::Instrument::THM1176Types;
77
78//----------------------------------------------------------------------//
79// Utilities: //
80// Replacements for std::to_string(float) and ...(double). //
81// These maintain full precision and always use period as seperator. //
82//----------------------------------------------------------------------//
83#include <sstream>
84#include <iomanip>
85#include <locale>
86
87static std::string l_ToString(F32 number, int precision = 7, const char * locale = "C")
88{
89 std::locale l_locale = std::locale(locale);
90 std::ostringstream l_oss;
91
92 l_oss.imbue(l_locale);
93 l_oss << std::scientific << std::setprecision(precision);
94 l_oss << number;
95
96 return l_oss.str();
97}
98
99static std::string l_ToString(F64 number, int precision = 15, const char * locale = "C")
100{
101 std::locale l_locale = std::locale(locale);
102 std::ostringstream l_oss;
103
104 l_oss.imbue(l_locale);
105 l_oss << std::scientific << std::setprecision(precision);
106 l_oss << number;
107
108 return l_oss.str();
109}
110
111//----------------------------------------------------------------------//
112// Utilities //
113//----------------------------------------------------------------------//
114static void l_ParseErrorString(std::string & rErrStr, const std::string & rContext, sError & rError)
115{
116 // Get code
117 size_t l_Comma = rErrStr.find_first_of(',');
118 rError.Code = std::stoi(rErrStr.substr(0, l_Comma));
119 // Get description
120 size_t l_OpenQuote = rErrStr.find_first_of('"', l_Comma + 1);
121 size_t l_CloseQuote = rErrStr.find_last_of('"');
122 rError.Description = rErrStr.substr(l_OpenQuote + 1, l_CloseQuote - (l_OpenQuote + 1));
123 rError.Context = rContext;
124}
125
126static std::string l_ParseRegexError (std::regex_error & rE)
127{
128 std::vector<std::string> lErrorList;
129 if (rE.code() & std::regex_constants::error_badrepeat)
130 lErrorList.push_back ("error_badrepeat");
131 if (rE.code() & std::regex_constants::error_ctype)
132 lErrorList.push_back ("error_ctype");
133 if (rE.code() & std::regex_constants::error_escape)
134 lErrorList.push_back ("error_escape");
135 if (rE.code() & std::regex_constants::error_backref)
136 lErrorList.push_back ("error_backref");
137 if (rE.code() & std::regex_constants::error_brack)
138 lErrorList.push_back ("error_brack");
139 if (rE.code() & std::regex_constants::error_paren)
140 lErrorList.push_back ("error_paren");
141 if (rE.code() & std::regex_constants::error_brace)
142 lErrorList.push_back ("error_brace");
143 if (rE.code() & std::regex_constants::error_badbrace)
144 lErrorList.push_back ("error_badbrace");
145 if (rE.code() & std::regex_constants::error_range)
146 lErrorList.push_back ("error_range");
147 if (rE.code() & std::regex_constants::error_space)
148 lErrorList.push_back ("error_space");
149 if (rE.code() & std::regex_constants::error_badrepeat)
150 lErrorList.push_back ("error_badrepeat");
151 if (rE.code() & std::regex_constants::error_complexity)
152 lErrorList.push_back ("error_complexity");
153 if (rE.code() & std::regex_constants::error_stack)
154 lErrorList.push_back ("error_stack");
155
156 std::string lErrors = lErrorList.front();
157 for (auto lpError = lErrorList.begin() + 1; lpError < lErrorList.end(); lpError++)
158 lErrors += " | " + *lpError;
159 return lErrors;
160}
161
162//----------------------------------------------------------------------//
163// Methods //
164//----------------------------------------------------------------------//
165template <class InstrType, class RsrcMgrType>
166bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadIdentification(std::string & rIdentification)
167{
168 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
169 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
170
171 try
172 {
173 if (!WriteAndRead("*IDN?", m_ReadBuffer))
174 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
176 rIdentification = std::string(m_ReadBuffer.begin(), m_ReadBuffer.end());
177 } // try
179 {
181 MTL_Unused(rE);
182 rIdentification.clear();
183 return false;
184 }
185 return true;
187
188template <class InstrType, class RsrcMgrType>
189bool CTHM1176Instrument<InstrType, RsrcMgrType>::ParseIdentification(struct sIdentifier & rIdentification)
190{
191 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
192 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
193 try
194 {
195 std::regex l_Regex("([^,]+),([^,]+),([^,]+),([^,]+)");
196 std::smatch l_Match;
197 if (!std::regex_match(m_Identification, l_Match, l_Regex))
198 throw MTL::CException<CTHM1176Instrument>("Failed to match ID string", MTL__LOCATION__);
199
200 rIdentification.Manufacturer = l_Match[1].str();
201 rIdentification.Model = l_Match[2].str();
202 rIdentification.SerialNumber = static_cast<U32>(stoul(l_Match[3].str()));
203
204 std::string l_Versions = l_Match[4].str();
205 l_Regex = "el(([A-Z])([0-9]+))-pr(([A-Z]?)([0-9]+))-fw(([0-9]+)\\.([0-9]+))\\n";
206 if (!std::regex_match(l_Versions, l_Match, l_Regex))
207 throw MTL::CException<CTHM1176Instrument>("Failed to match version string", MTL__LOCATION__);
208
209 rIdentification.ElectronicsVersion.Name = l_Match[1].str();
210 rIdentification.ElectronicsVersion.Major = static_cast<U8>(l_Match[2].str()[0] - '@');
211 rIdentification.ElectronicsVersion.Minor = static_cast<U8>(stoul(l_Match[3].str()));
212
213 rIdentification.ProbeVersion.Name = l_Match[4].str();
214 rIdentification.ProbeVersion.Major = static_cast<U8>(l_Match[5].str()[0] - '@');
215 rIdentification.ProbeVersion.Minor = static_cast<U8>(stoul(l_Match[6].str()));
216
217 rIdentification.FirmwareVersion.Name = l_Match[7].str();
218 rIdentification.FirmwareVersion.Major = static_cast<U8>(stoul(l_Match[8].str()));
219 rIdentification.FirmwareVersion.Minor = static_cast<U8>(stoul(l_Match[9].str()));
220 rIdentification.ModelRevision = rIdentification.SerialNumber < 3000 ? eModelRevision::kA : eModelRevision::kB;
221
222 //Instrument model enum:
223 std::map<std::string, eInstrModel> l_InstModel =
224 {
225 { "THM1176-LF", eInstrModel::kTHM1176LF },
226 { "THM1176-MF", eInstrModel::kTHM1176MF },
227 { "THM1176-HF", eInstrModel::kTHM1176HF },
228 { "THM1176-HFC", eInstrModel::kTHM1176HFC },
229 { "TFM1186", eInstrModel::kTFM1186 },
230 };
231 auto l_pInstrModel = l_InstModel.find(rIdentification.Model);
232 if (l_pInstrModel == l_InstModel.cend())
233 {
235 throw MTL::CException<CTHM1176Instrument>("Failed to parse instrument model." , MTL__LOCATION__);
236 }
237 else
238 {
239 rIdentification.InstrModel = l_pInstrModel->second;
241 }
243 {
246 return false;
247 }
248 catch (std::regex_error& rE)
249 {
250 MTL_INSTRUMENT_THM1176_DEBUG_CERR(std::string(rE.what()) + " (" + l_ParseRegexError(rE) + ") at " + MTL__LOCATION__ + '\n');
252 return false;
253 }
254 return true;
255}
257template <class InstrType, class RsrcMgrType>
258bool CTHM1176Instrument<InstrType, RsrcMgrType>::GetErrorList(CErrorList & rList, const std::string & rContext)
259{
260 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
261
262 rList.clear();
263 try
264 {
265 uStatusByte l_STB;
266 // Read STB
267 if (!InstrType::ReadSTB(l_STB.RawSTB))
268 throw MTL::CException<CTHM1176Instrument>("Failed reading STB", MTL__LOCATION__);
269
270 // While there is an error
271 CSCPIBuffer l_Error(4096);
272 while (l_STB.StatusByte.EAV)
273 {
274 // Try to read error string
275 enum eRetry { kRetryErrorRetrieving };
276 try
277 {
278 if (!InstrType::Write(":SYST:ERR?"))
279 throw kRetryErrorRetrieving;
280 if (!InstrType::Read(l_Error))
281 throw kRetryErrorRetrieving;
282 }
283 catch (eRetry & rRetry)
284 {
285 if (rRetry == kRetryErrorRetrieving)
286 {
287 // Clear
288 if (!InstrType::Clear())
289 throw MTL::CException<CTHM1176Instrument>("Could not clear device", MTL__LOCATION__);
290 // Retry reading error
291 if (!InstrType::Write(":SYST:ERR?"))
292 throw MTL::CException<CTHM1176Instrument>("Failed getting error list", MTL__LOCATION__);
293 if (!InstrType::Read(l_Error))
294 throw MTL::CException<CTHM1176Instrument>("Failed getting error list", MTL__LOCATION__);
295 }
296 }
297 // Parse Error
298 std::string l_ErrStr = std::string(l_Error.data(), l_Error.size());
299 sError l_Err;
300 l_ParseErrorString(l_ErrStr, rContext, l_Err);
301 rList.push_back(l_Err);
302 // Read STB
303 if (!InstrType::ReadSTB(l_STB.RawSTB))
304 throw MTL::CException<CTHM1176Instrument>("Failed reading STB", MTL__LOCATION__);
305 }
306 }
308 {
310 MTL_Unused(rE);
311 rList.clear();
312 return false;
313 }
314#if (defined(_DEBUG))
315 for (auto it = rList.begin(); it != rList.end(); it++)
316 {
317 CERR("THM1176Error " << it->Code << " : " << it->Description << " in " << it->Context << std::endl);
318 }
319#endif
320 return true;
321}
322
323template <class InstrType, class RsrcMgrType>
324bool CTHM1176Instrument<InstrType, RsrcMgrType>::WriteAndRead(const std::string & rWriteStr, CSCPIBuffer & rReadBuffer)
325{
326 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << rWriteStr << std::endl);
327 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
328
329 bool l_InitialLockState = InstrType::LockedExclusive();
330 try
331 {
332 rReadBuffer.clear();
333 m_ErrorList.clear();
334
335 // If we don't already have the exclusive lock on this session, acquire it for this write&read cycle.
336 if (!l_InitialLockState && !InstrType::LockExclusive(m_Timeout))
337 throw MTL::CException<CTHM1176Instrument>("Failed acquiring lock", MTL__LOCATION__);
338
339 // Append *OPC ? if command does not include "*IDN?" (which is the only command to return an indefinite - length response, and can therefore not be followed by another query).
340 // Match a string which contains "*IDN?"
341 bool l_OPCAppended = !std::regex_match(rWriteStr, std::regex("\\*IDN\\?"));
342 std::string l_Query;
343 if (l_OPCAppended)
344 l_Query = rWriteStr + ";*OPC?";
345 else
346 l_Query = rWriteStr;
347
348 // Write
349 if (!InstrType::Write(l_Query))
351
352 // Check errors
353 uStatusByte l_STB;
354 if (!InstrType::ReadSTB(l_STB.RawSTB))
355 throw MTL::CException<CTHM1176Instrument>("Failed reading STB", MTL__LOCATION__);
356 if (l_STB.StatusByte.EAV)
358
359 // Repeat while there is a message available
360 // There is always at least the *IDN? or the *OPC? answer
361 bool l_Timeout = false;
362 do
363 {
364 // Check the abort flag.
365 if (m_AbortRead)
366 throw MTL::CException<CTHM1176Instrument>("Read aborted by user", MTL__LOCATION__);
367 // Read and append. Tolerate time-outs.
368 if (InstrType::Read(rReadBuffer, true))
369 {
370 l_Timeout = false;
371 }
372 else
373 {
374 l_Timeout = InstrType::Timeout();
375 if (!l_Timeout)
377 }
378 // Read STB
379 if (!InstrType::ReadSTB(l_STB.RawSTB))
380 throw MTL::CException<CTHM1176Instrument>("Failed reading STB", MTL__LOCATION__);
381 // Check EAV (Error Available) bit.
382 if (l_STB.StatusByte.EAV)
384
385 } while (l_STB.StatusByte.MAV || l_Timeout);
386
387 // Remove *OPC? result if given
388 if (l_OPCAppended)
389 {
390 std::size_t l_MaxSizeToMatch = std::strlen(";1\n"); // It is unnecessary to perform a regex on more characters than this
391 std::match_results<std::vector<char>::iterator> m;
392 // Match a string that ends with an optional ';', a '1', and an optional '\n'
393 if (std::regex_match((rReadBuffer.size() <= l_MaxSizeToMatch) ? rReadBuffer.begin() : rReadBuffer.end() - l_MaxSizeToMatch, rReadBuffer.end(), m, std::regex(";?1\\n?$")))
394 rReadBuffer.resize(rReadBuffer.size() - m.length());
395 }
396
397 // If we did not have an exclusive lock on the session initially, release the lock.
398 if (!l_InitialLockState && !InstrType::Unlock())
399 throw MTL::CException<CTHM1176Instrument>("Could not unlock", MTL__LOCATION__);
400 }
402 {
404 MTL_Unused(rE);
405
406 // If we landed here because of an AbortRead, try to abort and clear the I/O buffers.
407 if (m_AbortRead)
408 {
409 InstrType::Write(":ABOR");
410 InstrType::Clear();
412
413 // Try to read whatever data is available.
414 uStatusByte l_STB;
415 while (InstrType::ReadSTB(l_STB.RawSTB) && l_STB.StatusByte.MAV)
416 InstrType::Read(rReadBuffer, true);
417
418 // Try to get errors
419 GetErrorList(m_ErrorList, rWriteStr);
420
421 // Try to release the lock
422 if (!l_InitialLockState) InstrType::Unlock();
423
424 // Reset the abort flag.
425 m_AbortRead = false;
426
427 return false;
428 }
429 catch (std::regex_error& rE)
430 {
431 MTL_INSTRUMENT_THM1176_DEBUG_CERR(std::string(rE.what()) + " (" + l_ParseRegexError(rE) + ") at " + MTL__LOCATION__ + '\n');
432 MTL_Unused(rE);
433 return false;
434 }
435 return true;
437
438template <class InstrType, class RsrcMgrType>
439bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadBootTime(CAbsoluteTimestamp & rBootTime)
440{
441 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
442 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
443
444 try
445 {
446 // Get the current timestamp (ns).
447 if (!WriteAndRead("*RST", m_ReadBuffer))
448 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
449 if(!WriteAndRead(":INIT", m_ReadBuffer)) //Initiate fake measurement to get timestampe (don't care about measurement overrange error)
450 {
451 if(!m_ErrorList.empty())
452 {
453 auto l_pError = m_ErrorList.begin();
454 for (; l_pError < m_ErrorList.end(); l_pError++)
455 {
456 if (l_pError->Code != THM1176_ERROR_CODE_MEAS_OVERRANGE)
457 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
458 }
459 }
460 }
461
462 if (!WriteAndRead(":FETC:TIM?", m_ReadBuffer))
463 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
464 std::string l_TimestampString(m_ReadBuffer.begin(), m_ReadBuffer.end());
465 U64 l_Timestamp = std::stoull(l_TimestampString, nullptr, 0);
466
467 // Get the current clock time.
468 // /!\ This is assumed to be in seconds since the Epoch.
469 std::time_t l_CurrentTime = std::time(nullptr);
470
471 // Compute the boot time.
472 rBootTime = CAbsoluteTimestamp(l_CurrentTime, 0) - CAbsoluteTimestamp(0, l_Timestamp);
473 }
475 {
477 MTL_Unused(rE);
478 rBootTime.clear();
479 return false;
480 }
481 return true;
482}
483
484template <class InstrType, class RsrcMgrType>
485bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadAllRanges(CFluxList & rRanges)
486{
487 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
488 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
489
490 try
491 {
492 if (!WriteAndRead(":SENS:ALL?", m_ReadBuffer))
493 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
494
495 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
496 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize(',');
497 if (l_Tokens.size() < 1)
498 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
499
500 // Loop to get the ranges.
501 rRanges.clear();
502 for (auto it = l_Tokens.begin(); it != l_Tokens.end(); it++)
503 {
504 std::string l_StrRange(it->begin, it->end - 2); // Throw away the " T" at the end.
505 try
506 {
507 rRanges.push_back(std::stof(l_StrRange));
508 }
509 catch (const std::invalid_argument& ia)
510 {
511 throw MTL::CException<CTHM1176Instrument>(std::string("Range has incorrect format: ") + ia.what(), MTL__LOCATION__);
512 }
513 }
514 }
516 {
518 MTL_Unused(rE);
519 rRanges.clear();
520 return false;
521 }
522 return true;
523}
524
525template <class InstrType, class RsrcMgrType>
526bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadAllUnits(std::map<eUnits,U32> & rAllUnits)
527{
528 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
529 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
530
531 try
532 {
533 if (!WriteAndRead(":UNIT:ALL?", m_ReadBuffer))
534 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
535
536 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
537 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize(',');
538 if (l_Tokens.size() < 2 || (l_Tokens.size() % 2) == 1)
539 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
540
541 // Loop to get the units and their associated divisors.
542 rAllUnits.clear();
543 for (auto it = l_Tokens.begin(); it != l_Tokens.end(); it++)
544 {
545 std::string l_StrUnit(it->begin, it->end);
546 it++;
547 std::string l_StrDivisor(it->begin, it->end);
548 eUnits l_Units;
549 if (!StringToUnits(l_StrUnit, l_Units))
551 U32 l_Divisor = static_cast<U32>(std::stoul(l_StrDivisor));
552 rAllUnits.insert(std::pair<eUnits, U32>(l_Units, l_Divisor));
553 }
554 }
556 {
558 MTL_Unused(rE);
559 rAllUnits.clear();
560 return false;
561 }
562 return true;
563}
564
565template <class InstrType, class RsrcMgrType>
566bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadUseCalibration(bool & rUseCal)
567{
568 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
569 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
570
571 try
572 {
573 if (!WriteAndRead(":CAL:STAT?", m_ReadBuffer))
574 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
575
576 std::string l_Answer(m_ReadBuffer.begin(), m_ReadBuffer.end());
577 if (l_Answer == "ON")
578 rUseCal = true;
579 else if (l_Answer == "OFF")
580 rUseCal = false;
581 else
582 throw MTL::CException<CTHM1176Instrument>("Invalid response format", MTL__LOCATION__);
583 }
585 {
587 MTL_Unused(rE);
588 return false;
589 }
590 return true;
591}
592
593template <class InstrType, class RsrcMgrType>
594bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadManufacturingDate(std::string & rDate)
595{
596 try
597 {
598 std::string l_CalFileContent;
599
600 // Read the information file.
601 if (!ReadFile(THM1176_INFO_FILE_NAME, l_CalFileContent))
602 throw MTL::CException<CTHM1176Instrument>("Cannot read calibration file", MTL__LOCATION__);
603
604 // Extract the Manufacturing Date.
605 rDate = std::string(l_CalFileContent, 100, 10);
606
607 }
609 {
611 MTL_Unused(rE);
612 rDate = "";
613 return false;
614 }
615 return true;
616}
617
618template <class InstrType, class RsrcMgrType>
619bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadRotationMatrix(Matrix3f & rMatrix, std::string & rCalibrationDate)
620{
621 try
622 {
623 std::string l_CalFileContent;
624
625 // Read the calibration file.
626 if (!ReadFile(THM1176_CALIBRATION_FILE_NAME, l_CalFileContent))
627 throw MTL::CException<CTHM1176Instrument>("Cannot read calibration file", MTL__LOCATION__);
628
629 // Extract the Calibration Date.
630 rCalibrationDate = std::string(l_CalFileContent, 64, 10);
631
632 // Get the appropriate offset, depending on the calibration file version.
633 char * l_pMatrix = const_cast<char *>(l_CalFileContent.c_str());
634 switch (l_CalFileContent[THM1176_CAL_FILE_OFFSET_VERSION])
635 {
636 case '2':
638 break;
639 default:
640 throw MTL::CException<CTHM1176Instrument>("Unknown calibration file version", MTL__LOCATION__);
641 }
642
643 // Copy the calibration data into a 2D integer array.
644 I32 l_IntMatrix[3][3];
645 for (int i = 0; i < 3; i++)
646 for (int j = 0; j < 3; j++)
647 {
648 l_IntMatrix[i][j] = BinaryToI32(l_pMatrix);
649 l_pMatrix += sizeof (I32);
650 }
651
652 // Convert to a floating-point matrix.
653 const F32 l_C = static_cast<F32>(0x7FFFFFFF);
654 rMatrix << l_IntMatrix[0][0]/l_C, l_IntMatrix[0][1]/l_C, l_IntMatrix[0][2]/l_C,
655 l_IntMatrix[1][0]/l_C, l_IntMatrix[1][1]/l_C, l_IntMatrix[1][2]/l_C,
656 l_IntMatrix[2][0]/l_C, l_IntMatrix[2][1]/l_C, l_IntMatrix[2][2]/l_C;
657 }
659 {
661 MTL_Unused(rE);
662 rCalibrationDate = "";
663 return false;
664 }
665 return true;
666}
667
668template <class InstrType, class RsrcMgrType>
669bool CTHM1176Instrument<InstrType, RsrcMgrType>::ApplyRotationMatrix(tFlux & rBx, tFlux & rBy, tFlux & rBz)
670{
671 try
672 {
673 // Check whether we're using calibration.
674 if (!m_UseCalibration)
675 throw MTL::CException<CTHM1176Instrument>("Not using calibration", MTL__LOCATION__);
676
677 // Apply the rotation.
678 Vector3f l_B(rBx, rBy, rBz);
679 l_B = m_RotationMatrix * l_B;
680
681 rBx = l_B(0);
682 rBy = l_B(1);
683 rBz = l_B(2);
684 }
686 {
688 MTL_Unused(rE);
689 return false;
690 }
691 return true;
692}
693
694template <class InstrType, class RsrcMgrType>
695bool CTHM1176Instrument<InstrType, RsrcMgrType>::ApplyRotationMatrix(CFluxList & rBx, CFluxList & rBy, CFluxList & rBz)
696{
697 try
698 {
699 // Check whether we're using calibration.
700 if (!m_UseCalibration)
701 throw MTL::CException<CTHM1176Instrument>("Not using calibration", MTL__LOCATION__);
702
703 // Check that the flux-list sizes are equal.
704 if (rBx.size() != rBy.size() || rBx.size() != rBz.size())
705 throw MTL::CException<CTHM1176Instrument>("Flux-lists not same size", MTL__LOCATION__);
706
707 // Loop through the flux-lists and rotate each vector.
708 // Note: could perhaps do this more efficiently by vector multiplication, [3x3][3xN];
709 // on the other hand, this requires more packing/unpacking – so maybe not. KISS for now.
710 for (auto l_pBx = rBx.begin(), l_pBy = rBy.begin(), l_pBz = rBz.begin();
711 l_pBx < rBx.end();
712 l_pBx++, l_pBy++, l_pBz++)
713 {
714 Vector3f l_B(*l_pBx, *l_pBy, *l_pBz);
715 l_B = m_RotationMatrix * l_B;
716 *l_pBx = l_B(0);
717 *l_pBy = l_B(1);
718 *l_pBz = l_B(2);
719 }
720 }
722 {
724 MTL_Unused(rE);
725 return false;
726 }
727 return true;
728}
729
730template <class InstrType, class RsrcMgrType>
731bool CTHM1176Instrument<InstrType, RsrcMgrType>::ParseMeasurements(CSCPIBufferParser & rBP, eUnits Units, CFluxList & rMeas, U32 NoMeasurements)
732{
733 rMeas.clear();
734 try
735 {
736 // Get next token.
737 std::vector<char>::const_iterator l_tokenbeg, l_tokenend;
738 if (!rBP.GetNext(l_tokenbeg, l_tokenend, ';'))
739 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
740
741 // Handle arbitrary-block data (INTeger and PACKed FORMats)
742 size_t l_off, l_len;
743 if (IsArbitraryBlock(l_tokenbeg, rBP.end(), l_off, l_len))
744 {
745 // Force the the correct offset in the Buffer Parser; it may have been misled by a ";" in the binary data.
746 rBP.SetNextOffset(l_tokenbeg + l_off + l_len + 1);
747
748 // Retrieve the divisor to convert from native units to the given units.
749 tFlux l_DivisorReal = 1.;
750 if (m_UseCalibration)
751 l_DivisorReal = static_cast<tFlux>(m_Units.at(Units));
752
753 // Handle INTeger format
754 if (l_tokenbeg[1] == '6')
755 {
756 for (;
757 l_len >= 4 && rMeas.size() < NoMeasurements;
758 l_len -= 4, l_off += 4)
759 {
760 I32 l_MeasI32 = BinaryToI32(&*l_tokenbeg + l_off);
761 rMeas.push_back(static_cast<tFlux>(l_MeasI32) / l_DivisorReal);
762 }
763 }
764
765 // Handle PACKed formats
766 else if (l_tokenbeg[1] == '5')
767 {
768 // Retrieve number of bytes per sample.
769 size_t l_Pack = (l_tokenbeg[l_off] == '1') ? 1 :
770 (l_tokenbeg[l_off] == '2') ? 2 : 0;
771 if (l_Pack == 0)
772 throw MTL::CException<CTHM1176Instrument>("Invalid PACKed reposnse", MTL__LOCATION__);
773 l_off += 1; l_len--;
774
775 // Convert initial value.
776 I32 l_MeasI32 = BinaryToI32(&*l_tokenbeg + l_off);
777 rMeas.push_back(static_cast<tFlux>(l_MeasI32) / l_DivisorReal);
778
779 // Reconstitute compressed values.
780 for (l_len -= 4, l_off += 4;
781 l_len >= l_Pack && rMeas.size() < NoMeasurements;
782 l_len -= l_Pack, l_off += l_Pack)
783 {
784 if (l_Pack == 1)
785 l_MeasI32 += static_cast<I8>(*(&*l_tokenbeg + l_off));
786 else // (l_Pack == 2)
787 l_MeasI32 += BinaryToI16(&*l_tokenbeg + l_off);
788 rMeas.push_back(static_cast<tFlux>(l_MeasI32) / l_DivisorReal);
789 }
790 }
791 }
792 // Handle text data (ASCii FORMat)
793 else
794 {
795 // Create a new parser object that covers just this token.
796 CSCPIBufferParser l_FluxParser(l_tokenbeg, l_tokenend);
797
798 // Loop over the measurements in the token.
799 std::vector<char>::const_iterator l_measbeg, l_measend;
800 bool l_GetNextRet;
801 for (l_GetNextRet = l_FluxParser.GetNext(l_measbeg, l_measend, ',');
802 l_GetNextRet && rMeas.size() < NoMeasurements;
803 l_GetNextRet = l_FluxParser.GetNext(l_measbeg, l_measend, ','))
804 {
805 std::string l_Measurement(l_measbeg, l_measend);
806 rMeas.push_back(std::stof(l_Measurement));
807 }
808 }
809
810 // Ensure that we got the requested number of measurements.
811 if (rMeas.size() != NoMeasurements)
812 throw MTL::CException<CTHM1176Instrument>("Failed to retrieve all measurements", MTL__LOCATION__);
813 }
815 {
817 MTL_Unused(rE);
818 rMeas.clear();
819 return false;
820 }
821 return true;
822}
823
824//----------------------------------------------------------------------//
825// Constructors / destructors //
826//----------------------------------------------------------------------//
827template <class InstrType, class RsrcMgrType>
829: InstrType(rResourceManager, ResourceName),
830 m_Timeout(0), m_ReadBuffer(65535), m_Sleep(false), m_UseCalibration(true), m_AbortRead(false)
831{
832}
833
834template <class InstrType, class RsrcMgrType>
836{
837 if (InstrType::IsOpen())
838 {
839 Abort();
840 Disconnect();
841 }
842}
843
844//----------------------------------------------------------------------//
845// General Information //
846//----------------------------------------------------------------------//
847template <class InstrType, class RsrcMgrType>
852
853template <class InstrType, class RsrcMgrType>
855{
856 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
857 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
858
859 m_ErrorList.clear();
860}
861
862//----------------------------------------------------------------------//
863// Connect / Disconnect //
864//----------------------------------------------------------------------//
865template <class InstrType, class RsrcMgrType>
866bool CTHM1176Instrument<InstrType, RsrcMgrType>::Connect(U32 InitialTimeout, bool Exclusive, std::string *pErrMsg)
867{
868 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " timeout=" << InitialTimeout << " exclusive=" << Exclusive << std::endl);
869 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
870
871 m_ErrorList.clear();
872
873 // Hack to silence a compiler warning, that FirmwareVersion.Minor < THM1176_SUPPORTED_VERSION_MIN_MINOR
874 // is always false (because FirmwareVersion.Minor is unsigned and THM1176_SUPPORTED_VERSION_MIN_MINOR is zero).
877
878 try
879 {
880 m_Timeout = InitialTimeout;
881 if (!InstrType::Open())
882 throw MTL::CException<CTHM1176Instrument>("Could not open", MTL__FUNCTION_NAME__);
883 if (Exclusive && !InstrType::LockExclusive(m_Timeout))
884 throw MTL::CException<CTHM1176Instrument>("Could not obtain exclusive lock", MTL__FUNCTION_NAME__);
885 if (!InstrType::Clear())
886 throw MTL::CException<CTHM1176Instrument>("Could not clear", MTL__FUNCTION_NAME__);
887 if (!WriteAndRead("*CLS", m_ReadBuffer))
888 throw MTL::CException<CTHM1176Instrument>("Failed W&R of *CLS", MTL__FUNCTION_NAME__);
889 if (!ReadIdentification(m_Identification))
890 throw MTL::CException<CTHM1176Instrument>("Could not get identification", MTL__FUNCTION_NAME__);
891 if (!ParseIdentification(m_IdentificationStruct))
892 throw MTL::CException<CTHM1176Instrument>("Could parse identification", MTL__FUNCTION_NAME__);
893 if ((m_IdentificationStruct.Model == "THM1176-A") && (m_IdentificationStruct.FirmwareVersion.Major < l_MinMajor || m_IdentificationStruct.FirmwareVersion.Minor < l_MinMinor))
894 throw MTL::CException<CTHM1176Instrument>("Unsupported firmware version", MTL__FUNCTION_NAME__);
895 if (!ReadBootTime(m_BootTime))
896 throw MTL::CException<CTHM1176Instrument>("Could not get boot time", MTL__FUNCTION_NAME__);
897 if (!ReadAllUnits(m_Units))
898 throw MTL::CException<CTHM1176Instrument>("Could not get units", MTL__FUNCTION_NAME__);
899 if (!ReadAllRanges(m_Ranges))
900 throw MTL::CException<CTHM1176Instrument>("Could not get ranges", MTL__FUNCTION_NAME__);
901 if (!ReadManufacturingDate(m_ManufacturingDate))
902 throw MTL::CException<CTHM1176Instrument>("Could not get manufacturing date", MTL__FUNCTION_NAME__);
903 if (!ReadRotationMatrix(m_RotationMatrix, m_CalibrationDate))
904 throw MTL::CException<CTHM1176Instrument>("Could not get rotation matrix", MTL__FUNCTION_NAME__);
905 if (!ReadUseCalibration(m_UseCalibration))
906 throw MTL::CException<CTHM1176Instrument>("Could not get Use Calibration parameter", MTL__FUNCTION_NAME__);
907 if (!ParmUnitsGet(m_UnitsParms))
908 throw MTL::CException<CTHM1176Instrument>("Could not get units", MTL__FUNCTION_NAME__);
909 if (!ParmAveragingGet(m_AveragingParms))
910 throw MTL::CException<CTHM1176Instrument>("Could not get averaging parameters", MTL__FUNCTION_NAME__);
911 if (!ParmTriggerInputGet(m_TriggerParms))
912 throw MTL::CException<CTHM1176Instrument>("Could not get trigger parameters", MTL__FUNCTION_NAME__);
913 if (!ParmRangeGet(m_RangeParms))
914 throw MTL::CException<CTHM1176Instrument>("Could not get range parameters", MTL__FUNCTION_NAME__);
915 if (!GetFormat(m_Format))
916 throw MTL::CException<CTHM1176Instrument>("Could not get communication format", MTL__FUNCTION_NAME__);
917
918 }
920 {
922 MTL_Unused(rE);
923 if (pErrMsg != nullptr)
924 *pErrMsg = rE.what();
925 if (InstrType::IsOpen())
926 InstrType::Close();
927 return false;
928 }
929 return true;
930}
931
932template <class InstrType, class RsrcMgrType>
934{
935 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
936 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
937
938 if (InstrType::IsOpen())
939 InstrType::Close();
940}
941
942//----------------------------------------------------------------------//
943// Parameters //
944//----------------------------------------------------------------------//
945template <class InstrType, class RsrcMgrType>
947{
948 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " " << rAvg << std::endl);
949 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
950
951 std::string l_Command = ":AVER:COUN " + std::to_string(rAvg.NoPoints);
952 bool l_Success = WriteAndRead(l_Command, m_ReadBuffer);
953 if (l_Success) m_AveragingParms = rAvg;
954 return l_Success;
955}
956
957template <class InstrType, class RsrcMgrType>
959{
960 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
961 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
962
963 try
964 {
965 if (!WriteAndRead(":AVER:COUN?", m_ReadBuffer))
966 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
967
968 rAvg.NoPoints = static_cast<U16>(std::stoi(std::string(m_ReadBuffer.begin(), m_ReadBuffer.end())));
969 }
971 {
973 MTL_Unused(rE);
974 return false;
975 }
976 return true;
977}
978
979template <class InstrType, class RsrcMgrType>
981{
982 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
983 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
984
985 try
986 {
987 if (!WriteAndRead(":AVER:COUN?;:AVER:COUN? MIN;:AVER:COUN? MAX;:AVER:COUN? DEF", m_ReadBuffer))
988 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
989
990 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
991 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize();
992 if (l_Tokens.size() != 4)
993 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
994
995 rAvg.NoPoints.Val = static_cast<U16>(std::stoi(std::string(l_Tokens[0].begin, l_Tokens[0].end)));
996 rAvg.NoPoints.Min = static_cast<U16>(std::stoi(std::string(l_Tokens[1].begin, l_Tokens[1].end)));
997 rAvg.NoPoints.Max = static_cast<U16>(std::stoi(std::string(l_Tokens[2].begin, l_Tokens[2].end)));
998 rAvg.NoPoints.Def = static_cast<U16>(std::stoi(std::string(l_Tokens[3].begin, l_Tokens[3].end)));
999 }
1001 {
1003 MTL_Unused(rE);
1004 return false;
1005 }
1006 return true;
1007}
1008
1009template <class InstrType, class RsrcMgrType>
1011{
1012 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " sleep=" << Sleep << std::endl);
1013 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1014
1015 m_Sleep = Sleep;
1016 return true;
1017}
1018
1019template <class InstrType, class RsrcMgrType>
1021{
1022 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1023 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1024
1025 rSleep = m_Sleep;
1026 return true;
1027}
1028
1029template <class InstrType, class RsrcMgrType>
1031{
1032 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " " << rInputTrig << std::endl);
1033 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1034
1035 std::string l_Command = ":TRIG:SOUR ";
1036 switch (rInputTrig.Source)
1037 {
1039 l_Command += "IMM";
1040 l_Command += ";COUN " + std::to_string(rInputTrig.Count);
1041 break;
1042 case kInputTrigSrcTimer:
1043 l_Command += "TIM";
1044 l_Command += ";COUN " + std::to_string(rInputTrig.Count);
1045 l_Command += ";TIM " + l_ToString(rInputTrig.Period_s);
1046 break;
1047 case kInputTrigSrcBus:
1048 l_Command += "BUS";
1049 l_Command += ";COUN " + std::to_string(rInputTrig.Count);
1050 break;
1051 }
1052
1053 if (WriteAndRead(l_Command, m_ReadBuffer))
1054 {
1055 m_TriggerParms = rInputTrig;
1056 return true;
1057 }
1058 else
1059 {
1060 CErrorList l_ErrorList = m_ErrorList;
1061 ParmTriggerInputGet(m_TriggerParms);
1062 m_ErrorList = l_ErrorList;
1063 return false;
1064 }
1065}
1066
1067template <class InstrType, class RsrcMgrType>
1069{
1070 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1071 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1072
1073 try
1074 {
1075 if (!WriteAndRead(":TRIG:SOUR?"
1076 ";TIM?"
1077 ";COUN?",
1078 m_ReadBuffer))
1079 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1080
1081 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1082 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize();
1083 if (l_Tokens.size() != 3)
1084 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
1085
1086 // Source
1087 std::string l_Source(l_Tokens[0].begin, l_Tokens[0].end);
1088 if (l_Source == "IMMEDIATE")
1089 rInputTrig.Source = kInputTrigSrcImmediate;
1090 else if (l_Source == "TIMER")
1091 rInputTrig.Source = kInputTrigSrcTimer;
1092 else if (l_Source == "BUS")
1093 rInputTrig.Source = kInputTrigSrcBus;
1094 else
1095 throw MTL::CException<CTHM1176Instrument>("Invalid answer format", MTL__LOCATION__);
1096
1097 // Period
1098 rInputTrig.Period_s = std::stod(std::string(l_Tokens[1].begin, l_Tokens[1].end));
1099
1100 // Trigger Count
1101 rInputTrig.Count = static_cast<U16>(std::stoi(std::string(l_Tokens[2].begin, l_Tokens[2].end)));
1102 }
1104 {
1106 MTL_Unused(rE);
1107 return false;
1108 }
1109 return true;
1110}
1111
1112template <class InstrType, class RsrcMgrType>
1114{
1115 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1116 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1117
1118 try
1119 {
1120 if (!WriteAndRead( ":TRIG:SOUR?"
1121 ";TIM?;TIM? MIN;TIM? MAX;TIM? DEF"
1122 ";COUN?;COUN? MIN;COUN? MAX;COUN? DEF",
1123 m_ReadBuffer))
1124 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1125
1126 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1127 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize();
1128 if (l_Tokens.size() != 9)
1129 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
1130
1131 // Source
1132 std::string l_Source(l_Tokens[0].begin, l_Tokens[0].end);
1133 if (l_Source == "IMMEDIATE")
1134 rInputTrig.Source = kInputTrigSrcImmediate;
1135 else if (l_Source == "TIMER")
1136 rInputTrig.Source = kInputTrigSrcTimer;
1137 else if (l_Source == "BUS")
1138 rInputTrig.Source = kInputTrigSrcBus;
1139 else
1140 throw MTL::CException<CTHM1176Instrument>("Invalid answer format", MTL__LOCATION__);
1141
1142 // Period
1143 rInputTrig.Period_s.Val = std::stod(std::string(l_Tokens[1].begin, l_Tokens[1].end));
1144 rInputTrig.Period_s.Min = std::stod(std::string(l_Tokens[2].begin, l_Tokens[2].end));
1145 rInputTrig.Period_s.Max = std::stod(std::string(l_Tokens[3].begin, l_Tokens[3].end));
1146 rInputTrig.Period_s.Def = std::stod(std::string(l_Tokens[4].begin, l_Tokens[4].end));
1147
1148 // Trigger Count
1149 rInputTrig.Count.Val = static_cast<U16>(std::stoi(std::string(l_Tokens[5].begin, l_Tokens[5].end)));
1150 rInputTrig.Count.Min = static_cast<U16>(std::stoi(std::string(l_Tokens[6].begin, l_Tokens[6].end)));
1151 rInputTrig.Count.Max = static_cast<U16>(std::stoi(std::string(l_Tokens[7].begin, l_Tokens[7].end)));
1152 rInputTrig.Count.Def = static_cast<U16>(std::stoi(std::string(l_Tokens[8].begin, l_Tokens[8].end)));
1153 }
1155 {
1157 MTL_Unused(rE);
1158 return false;
1159 }
1160 return true;
1161}
1162
1163template <class InstrType, class RsrcMgrType>
1165{
1166 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " units=" << Units << std::endl);
1167 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1168
1169 std::string l_Command = ":UNIT " + UnitsToString(Units);
1170
1171 if (WriteAndRead(l_Command, m_ReadBuffer))
1172 {
1173 m_UnitsParms = Units;
1174 return true;
1175 }
1176 else
1177 {
1178 CErrorList l_ErrorList = m_ErrorList;
1179 ParmUnitsGet(m_UnitsParms);
1180 m_ErrorList = l_ErrorList;
1181 return false;
1182 }
1183}
1184
1185template <class InstrType, class RsrcMgrType>
1187{
1188 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1189 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1190
1191 try
1192 {
1193 if (!WriteAndRead(":UNIT?", m_ReadBuffer))
1194 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1195
1196 std::string l_Answer(m_ReadBuffer.begin(), m_ReadBuffer.end());
1197 if (!StringToUnits(l_Answer, rUnits))
1198 throw MTL::CException<CTHM1176Instrument>("Invalid response format", MTL__LOCATION__);
1199 }
1201 {
1203 MTL_Unused(rE);
1204 return false;
1205 }
1206 return true;
1207}
1208
1209template <class InstrType, class RsrcMgrType>
1211{
1212 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " usecal=" << UseCal << std::endl);
1213 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1214
1215 std::string l_Command = std::string(":CAL:STAT ") + (UseCal ? "ON" : "OFF");
1216
1217 if (WriteAndRead(l_Command, m_ReadBuffer))
1218 {
1219 m_UseCalibration = UseCal;
1220 return true;
1221 }
1222 else
1223 return false;
1224}
1225
1226template <class InstrType, class RsrcMgrType>
1228{
1229 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1230 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1231
1232 rUseCal = m_UseCalibration;
1233 return true;
1234}
1235
1236template <class InstrType, class RsrcMgrType>
1238{
1239 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " " << rRange << std::endl);
1240 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1241
1242 std::string l_Command;
1243 if (rRange.Auto)
1244 l_Command = ":SENS:AUTO ON";
1245 else
1246 l_Command = ":SENS:AUTO OFF;:SENS " + l_ToString(rRange.Range, 1) + " T";
1247
1248 if (WriteAndRead(l_Command, m_ReadBuffer))
1249 {
1250 m_RangeParms = rRange;
1251 return true;
1252 }
1253 else
1254 {
1255 CErrorList l_ErrorList = m_ErrorList;
1256 ParmRangeGet(m_RangeParms);
1257 m_ErrorList = l_ErrorList;
1258 return false;
1259 }
1260}
1261
1262template <class InstrType, class RsrcMgrType>
1264{
1265 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1266 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1267
1268 try
1269 {
1270 if (!WriteAndRead(":SENS:AUTO?;:SENS?", m_ReadBuffer))
1271 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1272
1273 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1274 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize();
1275 if (l_Tokens.size() != 2)
1276 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
1277
1278 // Auto-ranging
1279 std::string l_Auto(l_Tokens[0].begin, l_Tokens[0].end);
1280 if (l_Auto == "ON")
1281 rRange.Auto = true;
1282 else if (l_Auto == "OFF")
1283 rRange.Auto = false;
1284 else
1285 throw MTL::CException<CTHM1176Instrument>("Invalid response", MTL__LOCATION__);
1286
1287 // Range
1288 std::string l_Range(l_Tokens[1].begin, l_Tokens[1].end - 2); // Ignore trailing " T"
1289 rRange.Range = std::stof(l_Range);
1290 }
1292 {
1294 MTL_Unused(rE);
1295 return false;
1296 }
1297 return true;
1298}
1299
1300template <class InstrType, class RsrcMgrType>
1302{
1303 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1304 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1305
1306 try
1307 {
1308 if (!WriteAndRead(":SENS:AUTO?;:SENS?;:SENS? MIN;:SENS? MAX;:SENS? DEF", m_ReadBuffer))
1309 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1310
1311 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1312 CSCPIBufferParser::tTokens l_Tokens = l_BP.Tokenize();
1313 if (l_Tokens.size() != 5)
1314 throw MTL::CException<CTHM1176Instrument>("Invalid number of tokens in answer", MTL__LOCATION__);
1315
1316 // Auto-ranging
1317 std::string l_Auto(l_Tokens[0].begin, l_Tokens[0].end);
1318 if (l_Auto == "ON")
1319 rRange.Auto = true;
1320 else if (l_Auto == "OFF")
1321 rRange.Auto = false;
1322 else
1323 throw MTL::CException<CTHM1176Instrument>("Invalid response", MTL__LOCATION__);
1324
1325 // Range
1326 std::string l_Val(l_Tokens[1].begin, l_Tokens[1].end - 2); // Ignore trailing " T"
1327 rRange.Range.Val = std::stof(l_Val);
1328
1329 // Minimum
1330 std::string l_Min(l_Tokens[2].begin, l_Tokens[2].end - 2); // Ignore trailing " T"
1331 rRange.Range.Min = std::stof(l_Min);
1332
1333 // Maximum
1334 std::string l_Max(l_Tokens[3].begin, l_Tokens[3].end - 2); // Ignore trailing " T"
1335 rRange.Range.Max = std::stof(l_Max);
1336
1337 // Default
1338 std::string l_Def(l_Tokens[4].begin, l_Tokens[4].end - 2); // Ignore trailing " T"
1339 rRange.Range.Def = std::stof(l_Def);
1340 }
1342 {
1344 MTL_Unused(rE);
1345 return false;
1346 }
1347 return true;
1348}
1349
1350//----------------------------------------------------------------------//
1351// Files //
1352//----------------------------------------------------------------------//
1353template <class InstrType, class RsrcMgrType>
1354bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadFileDirectory(U32 & rUsedBytes, U32 & rAvailableBytes, tFileList & rFileList)
1355{
1356 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1357 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1358
1359 rFileList.clear();
1360 m_ErrorList.clear();
1361
1362 try
1363 {
1364 // Extend timeout.
1365 if (!InstrType::SetTimeout(THM1176_FILE_ACCESS_TIMEOUT))
1366 throw MTL::CException<CTHM1176Instrument>(std::string("Could not set timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1367
1368 // Write the command and read the result.
1369 if (!WriteAndRead(":MMEM?", m_ReadBuffer))
1370 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1371
1372 // Parse results
1373 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1374 std::vector<char>::const_iterator l_tokenbeg, l_tokenend;
1375
1376 // Used Bytes
1377 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ','))
1378 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1379 rUsedBytes = static_cast<U32>(std::stoul(std::string(l_tokenbeg, l_tokenend)));
1380
1381 // Available Bytes
1382 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ','))
1383 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1384 rAvailableBytes = static_cast<U32>(std::stoul(std::string(l_tokenbeg, l_tokenend)));
1385
1386 // File List
1387 while (l_BP.GetNext(l_tokenbeg, l_tokenend, ','))
1388 {
1389 sFile l_File;
1390 // Path
1391 l_File.Path = std::string(l_tokenbeg, l_tokenend);
1392 // Size
1393 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ','))
1394 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1395 l_File.Size = std::stoul(std::string(l_tokenbeg, l_tokenend));
1396 // Type
1397 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ','))
1398 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1399 l_File.Type = std::string(l_tokenbeg, l_tokenend);
1400 // Add file to the list
1401 rFileList.push_back(l_File);
1402 }
1403
1404 // Reset timeout to default.
1405 if (!InstrType::SetTimeout(m_Timeout))
1406 throw MTL::CException<CTHM1176Instrument>(std::string("Could not reset timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1407 } // try
1409 {
1411 MTL_Unused(rE);
1412 InstrType::SetTimeout(m_Timeout);
1413 return false;
1414 }
1415 return true;
1416}
1417
1418template <class InstrType, class RsrcMgrType>
1419bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadFile(std::string Path, std::string & rContent)
1420{
1421 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " path=" << Path << std::endl);
1422 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1423
1424 m_ErrorList.clear();
1425 try
1426 {
1427 // Build the command.
1428 std::string l_Command;
1429 l_Command = ":MMEM:DATA? " "\"" + Path + "\"";
1430
1431 // Extend timeout.
1432 if (!InstrType::SetTimeout(THM1176_FILE_ACCESS_TIMEOUT))
1433 throw MTL::CException<CTHM1176Instrument>(std::string("Could not set timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1434
1435 // Write the command and read the result.
1436 if (!WriteAndRead(l_Command, m_ReadBuffer))
1437 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1438
1439 // Parse the result.
1440 size_t l_off, l_len;
1441 if (!IsArbitraryBlock(m_ReadBuffer.begin(), m_ReadBuffer.end(), l_off, l_len))
1442 throw MTL::CException<CTHM1176Instrument>(std::string("Missing or invalid arbitrary block in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1443 rContent = std::string(m_ReadBuffer.begin() + l_off, m_ReadBuffer.begin() + l_off + l_len);
1444
1445 // Reset timeout to default.
1446 if (!InstrType::SetTimeout(m_Timeout))
1447 throw MTL::CException<CTHM1176Instrument>(std::string("Could not reset timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1448 } // try
1450 {
1452 MTL_Unused(rE);
1453 InstrType::SetTimeout(m_Timeout);
1454 return false;
1455 }
1456 return true;
1457}
1458
1459//----------------------------------------------------------------------//
1460// Initiate / Abort / Trigger //
1461//----------------------------------------------------------------------//
1462template <class InstrType, class RsrcMgrType>
1464{
1465 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " cont=" << Continuous << std::endl);
1466 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1467
1468 // Set up the information needed to reconstruct the timing.
1469 m_AveragingParmsAtInit = m_AveragingParms;
1470 m_TriggerParmsAtInit = m_TriggerParms;
1471 m_TriggerTimes.clear();
1472
1473 // Initiate the measurement.
1474 std::string l_Command = Continuous ? ":INIT:CONT 1;:INIT" : ":INIT:CONT 0;:INIT";
1475 return WriteAndRead(l_Command, m_ReadBuffer);
1476}
1477
1478template <class InstrType, class RsrcMgrType>
1480{
1481 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1482 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1483
1484 return WriteAndRead(":ABOR", m_ReadBuffer);
1485}
1486
1487template <class InstrType, class RsrcMgrType>
1489{
1490 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1491 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1492
1493 m_ErrorList.clear();
1494
1495 // Save the time point.
1496 std::chrono::high_resolution_clock::time_point l_T = std::chrono::high_resolution_clock::now();
1497 m_TriggerTimes.push_back(l_T);
1498
1499 // Assert the trigger.
1500 return InstrType::AssertTrigger();
1501}
1502
1503//----------------------------------------------------------------------//
1504// Measurements / Data //
1505//----------------------------------------------------------------------//
1506template <class InstrType, class RsrcMgrType>
1508 CFluxList & rBx, CFluxList & rBy, CFluxList & rBz,
1509 eUnits & rUnits, U16 & rTemp, CTimestampList & rTimestampList,
1510 sMeasurementConditions * pMeasurementConditions)
1511{
1512 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " nmeas=" << NoMeasurements << std::endl);
1513 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1514
1515 sArbitraryMeasurements l_ArbSelect;
1516 l_ArbSelect.Bx = true;
1517 l_ArbSelect.By = true;
1518 l_ArbSelect.Bz = true;
1519 l_ArbSelect.Temperature = true;
1520 l_ArbSelect.Timestamp = true;
1521 l_ArbSelect.NoMeasurements = NoMeasurements;
1522
1523 return MeasurementsGet(l_ArbSelect, rBx, rBy, rBz, rUnits, rTemp, rTimestampList, pMeasurementConditions);
1524}
1525
1526template <class InstrType, class RsrcMgrType>
1528 CFluxList & rBx, CFluxList & rBy, CFluxList & rBz,
1529 eUnits & rUnits, U16 & rTemp, CTimestampList & rTimestampList,
1530 sMeasurementConditions * pMeasurementConditions)
1531{
1532 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " " << ArbSelect << std::endl);
1533 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1534
1535 try
1536 {
1537 // Build command
1538 std::string l_Command = ":UNIT?;:SENS?";
1539 if (ArbSelect.Bx) l_Command += ";:FETC:ARR:X? " + std::to_string(ArbSelect.NoMeasurements);
1540 if (ArbSelect.By) l_Command += ";:FETC:ARR:Y? " + std::to_string(ArbSelect.NoMeasurements);
1541 if (ArbSelect.Bz) l_Command += ";:FETC:ARR:Z? " + std::to_string(ArbSelect.NoMeasurements);
1542 if (ArbSelect.Temperature) l_Command += ";:FETC:TEMP?";
1543 if (ArbSelect.Timestamp) l_Command += ";:FETC:TIM?";
1544 if (m_Sleep) l_Command += ";:SYST:SLEE";
1545
1546 // Execute it
1547 if (!WriteAndRead(l_Command, m_ReadBuffer))
1548 {
1549 // See whether we have a serious error that would prevent parsing the data.
1550 if(!m_ErrorList.empty())
1551 {
1552 auto l_pError = m_ErrorList.begin();
1553 for (; l_pError < m_ErrorList.end(); l_pError++)
1554 if (l_pError->Code <= THM1176_FATAL_ERROR_CODE_LIMIT) break;
1555 if (l_pError < m_ErrorList.end())
1556 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1557 }
1558 }
1559 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1560
1561 // Get Units
1562 std::vector<char>::const_iterator l_tokenbeg, l_tokenend;
1563 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ';'))
1564 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1565 if (!StringToUnits(std::string(l_tokenbeg, l_tokenend), rUnits))
1566 throw MTL::CException<CTHM1176Instrument>("Could not parse units", MTL__LOCATION__);
1567
1568 // Get the currently selected range.
1569 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ';'))
1570 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1571 std::string l_RangeString(l_tokenbeg, l_tokenend);
1572 tFlux l_Range = stof(l_RangeString);
1573
1574 // Get Bx
1575 rBx.clear();
1576 if (ArbSelect.Bx)
1577 {
1578 if (!ParseMeasurements(l_BP, rUnits, rBx, ArbSelect.NoMeasurements))
1579 throw MTL::CException<CTHM1176Instrument>("Error parsing Bx", MTL__LOCATION__);
1580 }
1581
1582 // Get By
1583 rBy.clear();
1584 if (ArbSelect.By)
1585 {
1586 if (!ParseMeasurements(l_BP, rUnits, rBy, ArbSelect.NoMeasurements))
1587 throw MTL::CException<CTHM1176Instrument>("Error parsing By", MTL__LOCATION__);
1588 }
1589
1590 // Get Bz
1591 rBz.clear();
1592 if (ArbSelect.Bz)
1593 {
1594 if (!ParseMeasurements(l_BP, rUnits, rBz, ArbSelect.NoMeasurements))
1595 throw MTL::CException<CTHM1176Instrument>("Error parsing Bz", MTL__LOCATION__);
1596 }
1597
1598 // Apply the angle correction – or not.
1599 if (!ApplyRotationMatrix(rBx, rBy, rBz))
1600 {
1601 sError l_Error = sError(THM1176_NO_ANGLE_CORRECTION_CODE, "No angle correction", __func__);
1602 m_ErrorList.push_back(l_Error);
1603 }
1604
1605 // Get Temperature
1606 rTemp = 0;
1607 if (ArbSelect.Temperature)
1608 {
1609 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ';'))
1610 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1611 std::string l_TemperatureString(l_tokenbeg, l_tokenend);
1612 rTemp = static_cast<U16>(stoul(l_TemperatureString));
1613 }
1614
1615 // Get Timestamp
1616 rTimestampList.clear();
1617 if (ArbSelect.Timestamp)
1618 {
1619 // Fetch the timestamp of the last acquisition.
1620 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ';'))
1621 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1622 std::string l_TimestampString(l_tokenbeg, l_tokenend);
1623 U64 l_LastTimestamp = std::stoull(l_TimestampString, nullptr, 0);
1624
1625 // Reconstruct a list of timestamps for each acquisition.
1626 // How we do this depends on the trigger mode.
1627 switch (m_TriggerParmsAtInit.Source)
1628 {
1629 // For Immediate trigger, use the empirically determined time per acquisition
1630 // and time per measurement [ns].
1632 for (int i = ArbSelect.NoMeasurements - 1; i >= 0; i--)
1633 {
1634 F64 l_Period;
1635 GetImmediateMeasurementPeriod (m_AveragingParmsAtInit, m_IdentificationStruct.ModelRevision, l_Period);
1636 U64 l_TimeOffset = static_cast<U64> (i * l_Period * 1E9 + 0.5);
1637 rTimestampList.push_back(l_LastTimestamp - l_TimeOffset);
1638 }
1639 break;
1640
1641 // For Timed trigger, use the programmed period [s, with conversion to ns].
1642 case kInputTrigSrcTimer:
1643 for (int i = ArbSelect.NoMeasurements - 1; i >= 0; i--)
1644 {
1645 U64 l_TimeOffset = static_cast<U64> (i * m_TriggerParmsAtInit.Period_s * 1E9 + 0.5);
1646 rTimestampList.push_back(l_LastTimestamp - l_TimeOffset);
1647 }
1648 break;
1649
1650 // For Bus trigger, use the time points saved by SendBusTrigger(), [durations converted to ns].
1651 case kInputTrigSrcBus:
1652 {
1653 auto l_LastTriggerTime = m_TriggerTimes.back();
1654 for (auto l_pTime = m_TriggerTimes.begin(); l_pTime < m_TriggerTimes.begin() + ArbSelect.NoMeasurements; l_pTime++)
1655 {
1656 std::chrono::nanoseconds l_TimeOffset = l_LastTriggerTime - *l_pTime;
1657 rTimestampList.push_back(l_LastTimestamp - l_TimeOffset.count());
1658 }
1659 break;
1660 }
1661 }
1662 } // if (ArbSelect.Timestamp)
1663
1664 // Get measurement conditions.
1665 if (pMeasurementConditions)
1666 {
1667 // Fill in the actual trigger period.
1668 switch (m_TriggerParmsAtInit.Source)
1669 {
1671 GetImmediateMeasurementPeriod(m_AveragingParmsAtInit, m_IdentificationStruct.ModelRevision, m_TriggerParmsAtInit.Period_s);
1672 break;
1673 case kInputTrigSrcTimer:
1674 break;
1675 case kInputTrigSrcBus:
1676 rTimestampList.GetEstimatedPeriod(m_TriggerParmsAtInit.Period_s);
1677 m_TriggerParmsAtInit.Period_s *= 1E-9;
1678 break;
1679 }
1680
1681 // Copy the parameters relevant to the measurement.
1682 pMeasurementConditions->TriggerParms = m_TriggerParmsAtInit;
1683 pMeasurementConditions->AveragingParms = m_AveragingParmsAtInit;
1684 pMeasurementConditions->UseCalibration = m_UseCalibration;
1685 pMeasurementConditions->Range = l_Range;
1686 }
1687
1688 } // try
1690 {
1692 MTL_Unused(rE);
1693 return false;
1694 }
1695 return true;
1696}
1697
1698template <class InstrType, class RsrcMgrType>
1700{
1701 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " " << Format << std::endl);
1702 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1703
1704 std::string l_Command;
1705
1706 switch (Format)
1707 {
1708 case kComFormatAscii:
1709 l_Command += ":FORM ASC";
1710 break;
1711 case kComFormatInteger:
1712 l_Command += ":FORM INT";
1713 break;
1715 l_Command += ":FORM PACK,2";
1716 break;
1718 l_Command += ":FORM PACK,1";
1719 break;
1720 }
1721 if (WriteAndRead(l_Command, m_ReadBuffer))
1722 {
1723 m_Format = Format;
1724 return true;
1725 }
1726 else
1727 {
1728 CErrorList l_ErrorList = m_ErrorList;
1729 GetFormat(m_Format);
1730 m_ErrorList = l_ErrorList;
1731 return false;
1732 }
1733}
1734
1735template <class InstrType, class RsrcMgrType>
1737{
1738 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1739 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1740
1741 try
1742 {
1743 // Fetch the format.
1744 std::string l_Command = ":FORM?";
1745 if (!WriteAndRead(l_Command, m_ReadBuffer))
1746 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1747
1748 // Parse the format.
1749 std::string l_Format = std::string(m_ReadBuffer.begin(), m_ReadBuffer.end());
1750 if (std::string("ASCII") == l_Format)
1751 Format = kComFormatAscii;
1752 else if (std::string("INTEGER") == l_Format)
1753 Format = kComFormatInteger;
1754 else if (std::string("PACKED,2") == l_Format)
1755 Format = kComFormatPacked2Byte;
1756 else if (std::string("PACKED,1") == l_Format)
1757 Format = kComFormatPacked1Byte;
1758 else
1759 throw MTL::CException<CTHM1176Instrument>("Received unknown format", MTL__LOCATION__);
1760 }
1762 {
1764 MTL_Unused(rE);
1765 return false;
1766 }
1767 return true;
1768}
1769
1770//----------------------------------------------------------------------//
1771// High Level Measurement
1772template <class InstrType, class RsrcMgrType>
1774 bool DefaultParms, eUnits Units, tFlux ExpectedField, unsigned int NoDigits)
1775{
1776 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " default=" << DefaultParms << " units=" << Units << " expect=" << ExpectedField << " ndigits=" << NoDigits << std::endl);
1777 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1778
1779 try
1780 {
1781 // Build command
1782 std::string l_Command = ":UNIT " + UnitsToString(Units);
1783 l_Command += DefaultParms ? ";:MEAS:X?" : ";:READ:X?";
1784 if (ExpectedField > 0.f || NoDigits != 0) l_Command += " ";
1785 if (ExpectedField > 0.f) l_Command += l_ToString(ExpectedField);
1786 if (NoDigits != 0) l_Command += "," + std::to_string(NoDigits);
1787 l_Command += ";:FETC:Y?";
1788 if (NoDigits != 0) l_Command += " " + std::to_string(NoDigits);
1789 l_Command += ";Z?";
1790 if (NoDigits != 0) l_Command += " " + std::to_string(NoDigits);
1791 if (m_Sleep) l_Command += ";:SYST:SLEE";
1792
1793 // Execute it
1794 if (!WriteAndRead(l_Command, m_ReadBuffer))
1795 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1796 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1797
1798 // Get Bx
1799 CFluxList l_Meas;
1800 if (!ParseMeasurements(l_BP, Units, l_Meas, 1))
1801 throw MTL::CException<CTHM1176Instrument>("Error parsing Bx", MTL__LOCATION__);
1802 rBx = l_Meas[0];
1803
1804 // Get By
1805 if (!ParseMeasurements(l_BP, Units, l_Meas, 1))
1806 throw MTL::CException<CTHM1176Instrument>("Error parsing By", MTL__LOCATION__);
1807 rBy = l_Meas[0];
1808
1809 // Get Bz
1810 if (!ParseMeasurements(l_BP, Units, l_Meas, 1))
1811 throw MTL::CException<CTHM1176Instrument>("Error parsing Bz", MTL__LOCATION__);
1812 rBz = l_Meas[0];
1813
1814 // Apply the angle correction – or not.
1815 if (!ApplyRotationMatrix(rBx, rBy, rBz))
1816 {
1817 sError l_Error = { THM1176_NO_ANGLE_CORRECTION_CODE, "No angle correction", __func__ };
1818 m_ErrorList.push_back(l_Error);
1819 }
1820 }
1822 {
1824 MTL_Unused(rE);
1825 return false;
1826 }
1827 return true;
1828}
1829
1830template <class InstrType, class RsrcMgrType>
1831bool CTHM1176Instrument<InstrType, RsrcMgrType>::Measure(CFluxList & rBx, CFluxList & rBy, CFluxList & rBz, unsigned int NoMeasurements,
1832 bool DefaultParms, eUnits Units, tFlux ExpectedField, unsigned int NoDigits)
1833{
1834 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " nmeas=" << NoMeasurements << " default=" << DefaultParms << " units=" << Units << " expect=" << ExpectedField << " ndigits=" << NoDigits << std::endl);
1835 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1836
1837 try
1838 {
1839 // Build command
1840 std::string l_Command = ":UNIT " + UnitsToString(Units);
1841 l_Command += (DefaultParms ? ";:MEAS:ARR:X? " : ";:READ:ARR:X? ") +
1842 std::to_string(NoMeasurements);
1843 if (ExpectedField > 0.f || NoDigits != 0) l_Command += ",";
1844 if (ExpectedField > 0.f) l_Command += l_ToString(ExpectedField);
1845 if (NoDigits != 0) l_Command += "," + std::to_string(NoDigits);
1846 l_Command += ";:FETC:ARR:Y? " + std::to_string(NoMeasurements);
1847 if (NoDigits != 0) l_Command += "," + std::to_string(NoDigits);
1848 l_Command += ";Z? " + std::to_string(NoMeasurements);
1849 if (NoDigits != 0) l_Command += "," + std::to_string(NoDigits);
1850 if (m_Sleep) l_Command += ";:SYST:SLEE";
1851
1852 // Execute it
1853 if (!WriteAndRead(l_Command, m_ReadBuffer))
1854 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1855 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1856
1857 // Get Bx
1858 CFluxList l_Meas;
1859 if (!ParseMeasurements(l_BP, Units, rBx, NoMeasurements))
1860 throw MTL::CException<CTHM1176Instrument>("Error parsing Bx", MTL__LOCATION__);
1861
1862 // Get By
1863 if (!ParseMeasurements(l_BP, Units, rBy, NoMeasurements))
1864 throw MTL::CException<CTHM1176Instrument>("Error parsing By", MTL__LOCATION__);
1865
1866 // Get Bz
1867 if (!ParseMeasurements(l_BP, Units, rBz, NoMeasurements))
1868 throw MTL::CException<CTHM1176Instrument>("Error parsing Bz", MTL__LOCATION__);
1869
1870 // Apply the angle correction – or not.
1871 if (!ApplyRotationMatrix(rBx, rBy, rBz))
1872 {
1873 sError l_Error = { THM1176_NO_ANGLE_CORRECTION_CODE, "No angle correction", __func__ };
1874 m_ErrorList.push_back(l_Error);
1875 }
1876 }
1878 {
1880 MTL_Unused(rE);
1881 return false;
1882 }
1883 return true;
1884}
1885
1886//----------------------------------------------------------------------//
1887// Status Handling //
1888//----------------------------------------------------------------------//
1889template <class InstrType, class RsrcMgrType>
1891{
1892 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1893 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1894
1895 return WriteAndRead(":STAT:PRES", m_ReadBuffer);
1896}
1897
1898template <class InstrType, class RsrcMgrType>
1900{
1901 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " " << Reg << std::endl);
1902 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1903
1904 m_ErrorList.clear();
1905 try
1906 {
1907 std::string l_Command(STATUS_GET_CMDS[Reg.Set][Reg.Type]);
1908 if (0 == l_Command.size())
1909 throw MTL::CException<CTHM1176Instrument>("Invalid register", MTL__LOCATION__);
1910
1911 if (!InstrType::Write(l_Command) ||
1912 !InstrType::Read(m_ReadBuffer) ||
1913 !GetErrorList(m_ErrorList, l_Command))
1914 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1915
1916 rStatus = static_cast<U16>(std::stoi(std::string(m_ReadBuffer.begin(), m_ReadBuffer.end())));
1917 } // try
1919 {
1921 MTL_Unused(rE);
1922 rStatus = 0;
1923 return false;
1924 }
1925 return true;
1926}
1927
1928template <class InstrType, class RsrcMgrType>
1930{
1931 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
1932 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1933
1934 m_ErrorList.clear();
1935 try
1936 {
1937 std::string l_CompleteCommand;
1938 for (auto l_Reg : Regs)
1939 {
1940 std::string l_Command(STATUS_GET_CMDS[l_Reg.Set][l_Reg.Type]);
1941 if (0 == l_Command.size())
1942 throw MTL::CException<CTHM1176Instrument>("Invalid register", MTL__LOCATION__);
1943 if (l_CompleteCommand.size() > 0) l_CompleteCommand += ";";
1944 l_CompleteCommand += l_Command;
1945 }
1946
1947 if (!InstrType::Write(l_CompleteCommand) ||
1948 !InstrType::Read(m_ReadBuffer) ||
1949 !GetErrorList(m_ErrorList, l_CompleteCommand))
1950 throw MTL::CException<CTHM1176Instrument>(std::string("Failed to read status register in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1951
1952 // Parse results
1953 rStatus.clear();
1954 CSCPIBufferParser l_BP(m_ReadBuffer.begin(), m_ReadBuffer.end());
1955 std::vector<char>::const_iterator l_tokenbeg, l_tokenend;
1956
1957 for (size_t i = Regs.size(); i > 0; i--)
1958 {
1959 if (!l_BP.GetNext(l_tokenbeg, l_tokenend, ';'))
1960 throw MTL::CException<CTHM1176Instrument>("Could not find next token", MTL__LOCATION__);
1961 rStatus.push_back(static_cast<U16>(std::stoi(std::string(l_tokenbeg, l_tokenend))));
1962 }
1963 }
1965 {
1967 MTL_Unused(rE);
1968 return false;
1969 }
1970 return true;
1971}
1972
1973template <class InstrType, class RsrcMgrType>
1975{
1976 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << Set << " disablemask=" << DisableMask << " enablemask=" << EnableMask << std::endl);
1977 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
1978
1979 try
1980 {
1981 // Read current enable register
1982 std::string l_Command(STATUS_GET_CMDS[Set][kStatusEnable]);
1983 if (!WriteAndRead(l_Command, m_ReadBuffer))
1984 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1985
1986 U16 l_StatusEnab = static_cast<U16>(std::stoi(std::string(m_ReadBuffer.begin(), m_ReadBuffer.end())));
1987
1988 // Compute new enable register
1989 l_StatusEnab &= ~DisableMask;
1990 l_StatusEnab |= EnableMask;
1991
1992 // Set new enable register
1993 l_Command = std::string(STATUS_SET_CMDS[Set][kStatusEnable]) + " " + std::to_string(l_StatusEnab);
1994 if (!WriteAndRead(l_Command, m_ReadBuffer))
1995 throw MTL::CException<CTHM1176Instrument>(std::string("Failed W&R in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
1996 }
1998 {
2000 MTL_Unused(rE);
2001 return false;
2002 }
2003 return true;
2004}
2005
2006//----------------------------------------------------------------------//
2007// Utilities //
2008//----------------------------------------------------------------------//
2009template <class InstrType, class RsrcMgrType>
2011{
2012 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2013 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2014
2015 rIdentification = m_Identification;
2016 return true;
2017}
2018
2019template <class InstrType, class RsrcMgrType>
2021{
2022 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2023 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2024
2025 rIdentification = m_IdentificationStruct;
2026 return true;
2027}
2028
2029template <class InstrType, class RsrcMgrType>
2031{
2032 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2033 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2034
2035 rRanges.clear();
2036 for (auto l_Range : m_Ranges)
2037 rRanges.push_back(l_Range);
2038 return true;
2039}
2040
2041template <class InstrType, class RsrcMgrType>
2043{
2044 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2045 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2046
2047 rUnits.clear();
2048 for (auto l_Unit : m_Units)
2049 rUnits.push_back(l_Unit.first);
2050 return true;
2051}
2052
2053template <class InstrType, class RsrcMgrType>
2055{
2056 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " units=" << Units << std::endl);
2057 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2058
2059 try
2060 {
2061 rDivisor = m_Units.at(Units);
2062 }
2063 catch (const std::out_of_range& oor)
2064 {
2065 MTL_INSTRUMENT_THM1176_DEBUG_CERR(std::string("Units not found: ") + oor.what() + ", " + MTL__FUNCTION_NAME__);
2066 MTL_Unused(oor);
2067 rDivisor = 0;
2068 return false;
2069 }
2070 return true;
2071}
2072
2073template <class InstrType, class RsrcMgrType>
2075{
2076 Matrix = m_RotationMatrix;
2077 return true;
2078}
2079
2080template <class InstrType, class RsrcMgrType>
2082{
2083 if (rAvg.NoPoints <= 0)
2084 {
2085 rPeriod = 0;
2086 return false;
2087 }
2088 else
2089 {
2090 switch (modelRev) {
2091 case kA:
2093 break;
2094 case kB:
2096 break;
2097 default:
2098 // Assume type A for the moment
2100 };
2101
2102 return true;
2103 }
2104}
2105
2106template <class InstrType, class RsrcMgrType>
2108{
2109 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << " force=" << ForceCalibration << std::endl);
2110 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2111
2112 // Unless warnings are being overridden, ensure that this model is OK to calibrate.
2113 if (!ForceCalibration &&
2114 MODELS_NOT_TO_CALIBRATE.find(m_IdentificationStruct.Model) != MODELS_NOT_TO_CALIBRATE.end())
2115 return false;
2116
2117 m_ErrorList.clear();
2118 try
2119 {
2120 // Extend timeout.
2121 if (!InstrType::SetTimeout(THM1176_CALIBRATION_TIMEOUT))
2122 throw MTL::CException<CTHM1176Instrument>(std::string("Could not set timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2123
2124 // Send the command.
2125 // Note: Reset first to work around firmware bug that can mess up calibratio with non-standard measurement paramters.
2126 if (!WriteAndRead("*RST;:CAL:INIT", m_ReadBuffer))
2127 throw MTL::CException<CTHM1176Instrument>(std::string("W&R failed in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2128
2129 // Restore the original parameters.
2130 CErrorList l_ErrorList = m_ErrorList;
2131 if (!ParmUnitsSet(m_UnitsParms) ||
2132 !ParmAveragingSet(m_AveragingParms) ||
2133 !ParmTriggerInputSet(m_TriggerParms) ||
2134 !ParmRangeSet(m_RangeParms) ||
2135 !ParmSleepSet(m_Sleep) ||
2136 !ParmUseCalibrationSet(m_UseCalibration) ||
2137 !SetFormat(m_Format))
2138 throw MTL::CException<CTHM1176Instrument>(std::string("Could not restore parameters in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2139
2140 // Reset timeout to default.
2141 if (!InstrType::SetTimeout(m_Timeout))
2142 throw MTL::CException<CTHM1176Instrument>(std::string("Could not reset timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2143 }
2145 {
2147 MTL_Unused(rE);
2148
2149 // Restore the parameters as best as we can.
2150 CErrorList l_ErrorList = m_ErrorList;
2151 ParmUnitsSet(m_UnitsParms);
2152 ParmAveragingSet(m_AveragingParms);
2153 ParmTriggerInputSet(m_TriggerParms);
2154 ParmRangeSet(m_RangeParms);
2155 ParmSleepSet(m_Sleep);
2156 ParmUseCalibrationSet(m_UseCalibration);
2157 SetFormat(m_Format);
2158 m_ErrorList = l_ErrorList;
2159
2160 InstrType::SetTimeout(m_Timeout);
2161 return false;
2162 }
2163 return true;
2164}
2165
2166template <class InstrType, class RsrcMgrType>
2168{
2169 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2170 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2171
2172 m_ErrorList.clear();
2173 try
2174 {
2175 // Extend timeout.
2176 if (!InstrType::SetTimeout(THM1176_CALIBRATION_TIMEOUT))
2177 throw MTL::CException<CTHM1176Instrument>(std::string("Could not set timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2178
2179 // Send the command.
2180 if (!WriteAndRead(":CAL:ZERO", m_ReadBuffer))
2181 throw MTL::CException<CTHM1176Instrument>(std::string("W&R failed in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2182
2183 // Reset timeout to default.
2184 if (!InstrType::SetTimeout(m_Timeout))
2185 throw MTL::CException<CTHM1176Instrument>(std::string("Could not reset timeout in ") + MTL__FUNCTION_NAME__, MTL__LOCATION__);
2186 }
2188 {
2190 MTL_Unused(rE);
2191 InstrType::SetTimeout(m_Timeout);
2192 return false;
2193 }
2194 return true;
2195}
2196
2197template <class InstrType, class RsrcMgrType>
2199{
2200 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2201 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2202
2203 // Clear class variables.
2204 m_TriggerTimes.clear();
2205 m_ErrorList.clear();
2206 m_Sleep = false;
2207 m_UseCalibration = true;
2208 m_AbortRead = false;
2209
2210 try
2211 {
2212 // Reset the instrument.
2213 if (!InstrType::Write("*RST;*CLS"))
2214 throw MTL::CException<CTHM1176Instrument>("Failed to reset instrument", MTL__LOCATION__);
2215
2216 // Update the cached parameters.
2217 if (!ReadUseCalibration(m_UseCalibration))
2218 throw MTL::CException<CTHM1176Instrument>("Could not get Use Calibration parameter", MTL__LOCATION__);
2219 if (!ParmUnitsGet(m_UnitsParms))
2220 throw MTL::CException<CTHM1176Instrument>("Could not get units", MTL__LOCATION__);
2221 if (!ParmAveragingGet(m_AveragingParms))
2222 throw MTL::CException<CTHM1176Instrument>("Could not get averaging parameters", MTL__LOCATION__);
2223 if (!ParmTriggerInputGet(m_TriggerParms))
2224 throw MTL::CException<CTHM1176Instrument>("Could not get trigger parameters", MTL__LOCATION__);
2225 if (!ParmRangeGet(m_RangeParms))
2226 throw MTL::CException<CTHM1176Instrument>("Could not get range parameters", MTL__LOCATION__);
2227 if (!GetFormat(m_Format))
2228 throw MTL::CException<CTHM1176Instrument>("Could not get communication format", MTL__LOCATION__);
2229 }
2231 {
2233 MTL_Unused(rE);
2234
2235 // Retrieve the cached parameters as best as we can.
2236 CErrorList l_ErrorList = m_ErrorList;
2237 ReadUseCalibration(m_UseCalibration);
2238 ParmUnitsGet(m_UnitsParms);
2239 ParmAveragingGet(m_AveragingParms);
2240 ParmTriggerInputGet(m_TriggerParms);
2241 ParmRangeGet(m_RangeParms);
2242 GetFormat(m_Format);
2243 m_ErrorList = l_ErrorList;
2244
2245 return false;
2246 }
2247 return true;
2248}
2249
2250template <class InstrType, class RsrcMgrType>
2252{
2253 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2254 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2255
2256 m_ErrorList.clear();
2257 return InstrType::Write(":DIAG:UPGR");
2258}
2259
2260template <class InstrType, class RsrcMgrType>
2261bool CTHM1176Instrument<InstrType, RsrcMgrType>::ReadInformationDates(std::string & rSManufacturingDate, std::time_t & rManufacturingDate, std::string & rSCalibrationDate, std::time_t & rCalibrationDate)
2262{
2263 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2264 CLockGuard<CRecursiveMutex> l_LockGuard(m_Lock);
2265
2266 try
2267 {
2268 // Copy the string version of the Manufacturing date.
2269 rSManufacturingDate = m_ManufacturingDate;
2270
2271 // Parse Manufacturing date, YYYY-MM-DD.
2272 std::match_results<std::string::const_iterator> l_Mm;
2273 if (!std::regex_match(rSManufacturingDate, l_Mm, std::regex("^([0-9]{4})-([0-9]{2})-([0-9]{2})")))
2274 throw MTL::CException<CTHM1176Instrument>("Invalid answer format", MTL__LOCATION__);
2275 struct std::tm l_ManDateStruct = { };
2276 l_ManDateStruct.tm_year = std::stoi(l_Mm[1]) - 1900; // years since 1900
2277 l_ManDateStruct.tm_mon = std::stoi(l_Mm[2]) - 1; // months since January (0-11)
2278 l_ManDateStruct.tm_mday = std::stoi(l_Mm[3]); // day of the month (1-31)
2279 if ((rManufacturingDate = mktime(&l_ManDateStruct)) < 0)
2280 throw MTL::CException<CTHM1176Instrument>("Invalid answer format", MTL__LOCATION__);
2281
2282 // Copy the string version of the Calibration date.
2283 rSCalibrationDate = m_CalibrationDate;
2284
2285 // Parse Calibrationdate, YYYY-MM-DD.
2286 std::match_results<std::string::const_iterator> l_Cm;
2287 if (!std::regex_match(rSCalibrationDate, l_Cm, std::regex("^([0-9]{4})-([0-9]{2})-([0-9]{2})")))
2288 throw MTL::CException<CTHM1176Instrument>("Invalid answer format", MTL__LOCATION__);
2289 struct tm l_CalDateStruct = { };
2290 l_CalDateStruct.tm_year = std::stoi(l_Cm[1]) - 1900; // years since 1900
2291 l_CalDateStruct.tm_mon = std::stoi(l_Cm[2]) - 1; // months since January (0-11)
2292 l_CalDateStruct.tm_mday = std::stoi(l_Cm[3]); // day of the month (1-31)
2293 if ((rCalibrationDate = mktime(&l_CalDateStruct)) < 0)
2294 throw MTL::CException<CTHM1176Instrument>("Invalid answer format", MTL__LOCATION__);
2295 } // try
2297 {
2299 MTL_Unused(rE);
2300 rSManufacturingDate.clear();
2301 rManufacturingDate = -1;
2302 rSCalibrationDate.clear();
2303 rCalibrationDate = -1;
2304 return false;
2305 }
2306 catch (std::regex_error& rE)
2307 {
2308 MTL_INSTRUMENT_THM1176_DEBUG_CERR(std::string(rE.what()) + " (" + l_ParseRegexError(rE) + ") at " + MTL__LOCATION__ + '\n');
2309 MTL_Unused(rE);
2310 return false;
2311 }
2312 return true;
2313}
2314
2315template <class InstrType, class RsrcMgrType>
2317{
2318 MTL_INSTRUMENT_THM1176_DEBUG_COUT(MTL__FUNCTION_NAME__ << std::endl);
2319
2320 // Note: this method does not obtain an exclusive lock on the interface,
2321 // because it is intended to run from a second thread, to abort a WriteAndRead in progress.
2322 // Its only job is to set m_AbortRead, which is atomic.
2323 try
2324 {
2325 // Set the abort flag.
2326 m_AbortRead = true;
2327
2328 // Wait for twice the current timeout value.
2329 U32 l_CurrentTimeout = InstrType::GetTimeout();
2330 std::this_thread::sleep_for(std::chrono::milliseconds(2 * l_CurrentTimeout));
2331
2332 // The Read in WriteAndRead should have quit by now, resetting the abort flag.
2333 if (m_AbortRead)
2334 throw MTL::CException<CTHM1176Instrument>("Abort flag not reset in time", MTL__LOCATION__);
2335 } // try
2337 {
2339 MTL_Unused(rE);
2340 m_AbortRead = false;
2341 return false;
2342 }
2343
2344 return true;
2345}
Exception handling utilities.
Collection of utility macros for error messages.
#define CERR(__X__)
Definition Helpers.h:28
#define MTL_Unused(x)
Definition Helpers.h:47
#define MTL__LOCATION__
Definition Helpers.h:22
Platform Dependent Definitions.
char I8
Signed byte.
Definition OSDefines.h:25
unsigned long long U64
64-bit unsigned integer.
Definition OSDefines.h:32
unsigned char U8
Unsigned byte.
Definition OSDefines.h:29
float F32
32-bit floating-point number.
Definition OSDefines.h:33
int I32
32-bit signed integer.
Definition OSDefines.h:27
unsigned int U32
32-bit unsigned integer.
Definition OSDefines.h:31
double F64
64-bit floating-point number.
Definition OSDefines.h:34
unsigned short U16
16-bit unsigned integer.
Definition OSDefines.h:30
Utilities to aid in sending SCPI commands and parsing of SCPI reponses.
static const F64 THM1176_IMMEDIATE_TIME_PER_ACQ_A(1.0281823091218700E-04)
static const U32 THM1176_CALIBRATION_TIMEOUT(30000)
static const std::string THM1176_INFO_FILE_NAME("info.dat")
static std::string l_ParseRegexError(std::regex_error &rE)
Definition THM1176.cpp:126
static const F64 THM1176_IMMEDIATE_TIME_PER_MEAS_B(0.0000285930870825438)
#define MTL_INSTRUMENT_THM1176_DEBUG_COUT(__X__)
Definition THM1176.cpp:36
static std::string l_ToString(F32 number, int precision=7, const char *locale="C")
Definition THM1176.cpp:87
#define MTL_INSTRUMENT_THM1176_DEBUG_CERR(__X__)
Definition THM1176.cpp:37
static const U32 THM1176_FILE_ACCESS_TIMEOUT(20000)
static const std::set< std::string > MODELS_NOT_TO_CALIBRATE
Definition THM1176.cpp:69
static const F64 THM1176_IMMEDIATE_TIME_PER_ACQ_B(0.0000116103073008506)
static const char * STATUS_SET_CMDS[4][3]
Definition THM1176.cpp:53
static const I32 THM1176_ERROR_CODE_MEAS_OVERRANGE(205)
static const U32 THM1176_CAL_FILE_OFFSET_VERSION(32)
static const std::string THM1176_CALIBRATION_FILE_NAME("cal.dat")
static const U32 THM1176_CAL_FILE_OFFSET_MATRIX_V2(116)
static const I32 THM1176_FATAL_ERROR_CODE_LIMIT(200)
static void l_ParseErrorString(std::string &rErrStr, const std::string &rContext, sError &rError)
Definition THM1176.cpp:114
static const F64 THM1176_IMMEDIATE_TIME_PER_MEAS_A(4.4532792007542600E-05)
static const char * STATUS_GET_CMDS[4][3]
Definition THM1176.cpp:61
Interface definition for C++ API for Metrolab THM1176/TFM1186.
#define THM1176_SUPPORTED_VERSION_MIN_MAJOR
Definition THM1176.h:67
#define THM1176_SUPPORTED_VERSION_MIN_MINOR
Definition THM1176.h:68
Type conversion routines for C++ API for Metrolab THM1176/TFM1186.
Exception to be thrown.
Definition Exception.h:17
virtual const char * what() const noexcept
Return string describing what happened.
Definition Exception.h:34
void resize(size_t size)
Resize the buffer.
std::vector< MTL_INSTRUMENT_BUFFER_TYPE >::iterator end()
Return an iterator to the end of the buffer.
void clear()
Clear by setting the buffer size to zero.
std::vector< MTL_INSTRUMENT_BUFFER_TYPE >::iterator begin()
Return an iterator to the beginning of the buffer.
size_t size() const
Return the buffer size.
bool GetNext(std::vector< char >::const_iterator &rNextBegin, std::vector< char >::const_iterator &rNextEnd, const char Separator=';')
Find the next token.
const tTokens Tokenize(const char Separator=';', size_t Offset=0)
Split the buffer into tokens.
std::vector< sToken > tTokens
List of tokens.
std::vector< char >::const_iterator end()
Return the end of the data to be parsed.
void SetNextOffset(std::vector< char >::const_iterator Offset)
Manually set the offset to the next token.
bool StatusPreset()
Reset OPERation and QUEStionable enable registers.
Definition THM1176.cpp:1890
bool ParmAveragingGet(sAveraging< uParm > &rAvg)
Fetch the currently selected averaging parameter.
Definition THM1176.cpp:958
bool GetIdentification(std::string &rIdentification)
Fetch the intrument's identification string.
Definition THM1176.cpp:2010
bool ReadFileDirectory(U32 &rUsedBytes, U32 &rAvailableBytes, tFileList &rFileList)
Read the instrument's file directory information.
Definition THM1176.cpp:1354
bool StatusGet(sStatusRegister Reg, U16 &rStatus)
Fetch current value of a single status register.
Definition THM1176.cpp:1899
virtual ~CTHM1176Instrument()
Destructor.
Definition THM1176.cpp:835
bool ParmUseCalibrationGet(bool &rUseCal)
Fetch parameter whether to return calibrated results.
Definition THM1176.cpp:1227
bool Initiate(bool Continuous=false)
Initiate measurements.
Definition THM1176.cpp:1463
bool GetAllRanges(CFluxList &rRanges)
Fetch all the intrument's ranges.
Definition THM1176.cpp:2030
bool ParmUnitsGet(eUnits &rUnits)
Fetch currently selected measurement units.
Definition THM1176.cpp:1186
bool SetFormat(eCommunicationFormat Format)
Select whether data is returned as text or binary.
Definition THM1176.cpp:1699
bool ParmSleepGet(bool &rSleep)
Fetch parameter whether to sleep after each acquisition.
Definition THM1176.cpp:1020
bool ParmRangeSet(const sRange< uParm > &rRange)
Set measurement range.
Definition THM1176.cpp:1237
bool Abort(void)
Abort a measurement in progress.
Definition THM1176.cpp:1479
bool ReadInformationDates(std::string &rSManufacturingDate, std::time_t &rManufacturingDate, std::string &rSCalibrationDate, std::time_t &rCalibrationDate)
Fetch the intrument's date information.
Definition THM1176.cpp:2261
bool Connect(U32 InitialTimeout, bool Exclusive=true, std::string *pErrMsg=nullptr)
Open the connection to the instrument.
Definition THM1176.cpp:866
bool Measure(tFlux &rBx, tFlux &rBy, tFlux &rBz, bool DefaultParms=true, eUnits Units=kT, tFlux ExpectedField=0., unsigned int NoDigits=0)
High-level measurement: single measurement.
Definition THM1176.cpp:1773
void Disconnect()
Close the connection to the instrument.
Definition THM1176.cpp:933
CTHM1176Instrument(RsrcMgrType &rResourceManager, tResourceName ResourceName)
Constructor.
Definition THM1176.cpp:828
bool ParmTriggerInputSet(const sInputTrigger< uParm > &rInputTrig)
Set trigger input parameters.
Definition THM1176.cpp:1030
bool GetDivisor(eUnits Units, U32 &rDivisor)
Fetch divisor to convert instrument's base units to given units.
Definition THM1176.cpp:2054
bool ParmRangeGet(sRange< uParm > &rRange)
Fetch currently selected measurement range.
Definition THM1176.cpp:1263
bool AbortRead()
Abort a read operation.
Definition THM1176.cpp:2316
bool StatusSetEnableRegister(eStatusRegisterSet Set, U16 DisableMask, U16 EnableMask)
Disable and enable bits in the given enable register.
Definition THM1176.cpp:1974
bool ParmUnitsSet(eUnits Units)
Set measurement units.
Definition THM1176.cpp:1164
bool GetImmediateMeasurementPeriod(const sAveraging< uParm > &rAvg, eModelRevision modelRev, F64 &rPeriod)
Compute measurement interval for Immediate Trigger, for a given averaging parameter.
Definition THM1176.cpp:2081
bool ReadFile(std::string Path, std::string &rContent)
Read a file from the instrument's file system.
Definition THM1176.cpp:1419
bool ParmAveragingSet(const sAveraging< uParm > &rAvg)
Set the averaging parameter.
Definition THM1176.cpp:946
bool Reset()
Reset the instrument to power-on configuration.
Definition THM1176.cpp:2198
bool SendBusTrigger()
Send a USB bus trigger.
Definition THM1176.cpp:1488
bool CalibrateZeroOffset(bool ForceCalibration=false)
Perform the Zero Offset calibration procedure.
Definition THM1176.cpp:2107
bool ParmSleepSet(bool Sleep)
Set parameter whether to sleep after each acquisition.
Definition THM1176.cpp:1010
bool ParmTriggerInputGet(sInputTrigger< uParm > &rInputTrig)
Fetch current trigger input parameters.
Definition THM1176.cpp:1068
bool SwitchToDFUMode()
Enter the Device Firmware Upgrade mode.
Definition THM1176.cpp:2251
bool GetAllUnits(CUnitsList &rUnits)
Fetch all units supported by instrument.
Definition THM1176.cpp:2042
bool ParmUseCalibrationSet(bool UseCal)
Set whether to return calibrated results.
Definition THM1176.cpp:1210
bool GetRotationMatrix(Matrix3f &Matrix)
Fetch the intrument's rotation matrix, to correct angular error.
Definition THM1176.cpp:2074
bool RestoreZeroOffset()
Restore the factory values for the Zero Offset.
Definition THM1176.cpp:2167
bool GetFormat(eCommunicationFormat &Format)
Retrieve whether data is returned as text or binary.
Definition THM1176.cpp:1736
const CErrorList & CurrentErrorList()
Fetch current error list.
Definition THM1176.cpp:848
bool MeasurementsGet(U32 NoMeasurements, CFluxList &rBx, CFluxList &rBy, CFluxList &rBz, eUnits &rUnits, U16 &rTemp, CTimestampList &rTimestampList, sMeasurementConditions *pMeasurementConditions=NULL)
Retrieve measurements: short form.
Definition THM1176.cpp:1507
void ClearErrorList()
Clear the error list.
Definition THM1176.cpp:854
void clear(void)
Clear to default values (zeroes).
List of errors returned by the instrument.
List of flux density values.
bool GetEstimatedPeriod(F64 &Period)
Estimate the measurement period from this timestamp list, by means of a least-squares fit.
List of SCPI status registers.
List of values returned for several SCPI status registers.
I32 BinaryToI32(const char pBinary[4])
Convert binary to I32, taking into account endedness.
@ kStatusEnable
Enable register.
std::string UnitsToString(eUnits Units)
Convert measurement units from enumeration to string.
F32 tFlux
Flux density value, as 32-bit floating-point number.
I16 BinaryToI16(const char pBinary[2])
Convert binary to I16, taking into account endedness.
eCommunicationFormat
Enumeration of possible formats for returned data.
@ kComFormatInteger
Binary (32-bit integers).
@ kComFormatPacked2Byte
Binary packed: first field value as I32, remainder deltas as I16.
@ kComFormatPacked1Byte
Binary packed: first field value as I32, remainder deltas as I8.
@ kComFormatAscii
Human-legible text.
std::vector< sFile > tFileList
List of directory entries.
eModelRevision
THM1176 type A or B.
@ kInputTrigSrcTimer
Timed trigger: start measurement at regular intervals.
@ kInputTrigSrcImmediate
Immediate trigger: start measurement immediately after previous one completes.
@ kInputTrigSrcBus
Bus trigger: start measurement upon USB trigger message.
bool StringToUnits(std::string SUnits, eUnits &rUnits)
Convert measurement units from string to enumeration.
eUnits
Enumeration of possible measurement units.
eStatusRegisterSet
Enumeration of SCPI status register sets.
std::string tResourceName
IEEE488 resource name.
static const I32 THM1176_NO_ANGLE_CORRECTION_CODE
Warning that angle correction was not applied.
Definition THM1176.h:82
bool IsArbitraryBlock(const iterator_type first, const iterator_type last, size_t &rStartOffset, size_t &rLength)
Find arbitrary-block data within a buffer.
Definition SCPIParsing.h:87
Specify the measurement data to be returned.
bool Bx
Return the flux density X-component.
bool Temperature
Return the sensor temperature.
bool By
Return the flux density Y-component.
bool Bz
Return the flux density Z-component.
U32 NoMeasurements
Return this number of measurements.
ParmType< U16 > NoPoints
Number of points in block average.
Error returned by the instrument.
std::string Context
SCPI commands being executed at time of error.
std::string Description
Error description.
Directory entry in the instrument's file system.
std::string Type
File type ("ASCII" or "BINARY").
size_t Size
File size, in bytes.
Instrument's identification string - parsed version.
struct sVersion FirmwareVersion
Version numbers of firmware.
std::string Manufacturer
Manufacturer name ("Metrolab Technology SA").
struct sVersion ElectronicsVersion
Version numbers of electronics.
struct sVersion ProbeVersion
Version numbers of probe.
std::string Model
Model name (e.g. "THM1176-MF").
enum eInstrModel InstrModel
Enumerator of instrument model.
enum eModelRevision ModelRevision
Revision of Model.
ParmType< U16 > Count
Trigger count: take this many measurements before sending results.
eInputTriggerSource Source
Trigger source.
ParmType< F64 > Period_s
Trigger period, for timed trigger.
Summary of the parameters used to make a measurement.
sAveraging< uParm > AveragingParms
Averaging parameters.
sInputTrigger< uParm > TriggerParms
Trigger parameters.
Measurement range parameter.
bool Auto
Auto-ranging enabled.
ParmType< tFlux > Range
Measurement range, if auto-ranging is not enabled.
Complete identification of a SCPI status register.
eStatusRegisterSet Set
SCPI register set.
eStatusRegisterType Type
SCPI register type.
U8 EAV
Error Available in Error / Event Queue.
Union to access the Status Byte as integer or bit fields.
struct MTL::Instrument::THM1176Types::uStatusByte::sStatusByte StatusByte
Access the Status Byte as bit fields.
U16 RawSTB
Access the Status Byte as unsigned integer.