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