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