7 #ifndef tcsInterface_hpp
8 #define tcsInterface_hpp
12 #include "../../libMagAOX/libMagAOX.hpp"
13 #include "../../magaox_git_version.h"
16 #include "../../libMagAOX/app/dev/telemeter.hpp"
211 const std::string &statreq
221 const std::string & xmsstr
431 config.add(
"labMode",
"",
"labMode", argType::Required,
"",
"labMode",
false,
"bool",
"Flag to enable lab mode. Default is true.");
433 config.add(
"pyrNudger.C_00",
"",
"pyrNudger.C_00", argType::Required,
"pyrNudger",
"C_00",
false,
"float",
"Pyramid to AEG control matrix [0,0] of a 2x2 matrix");
434 config.add(
"pyrNudger.C_01",
"",
"pyrNudger.C_01", argType::Required,
"pyrNudger",
"C_01",
false,
"float",
"Pyramid to AEG control matrix [0,1] of a 2x2 matrix ");
435 config.add(
"pyrNudger.C_10",
"",
"pyrNudger.C_10", argType::Required,
"pyrNudger",
"C_10",
false,
"float",
"Pyramid to AEG control matrix [1,0] of a 2x2 matrix ");
436 config.add(
"pyrNudger.C_11",
"",
"pyrNudger.C_11", argType::Required,
"pyrNudger",
"C_11",
false,
"float",
"Pyramid to AEG control matrix [1,1] of a 2x2 matrix ");
438 config.add(
"pyrNudger.ang",
"",
"pyrNudger.ang", argType::Required,
"pyrNudger",
"ang",
false,
"float",
"");
439 config.add(
"pyrNudger.ang0",
"",
"pyrNudger.ang0", argType::Required,
"pyrNudger0",
"ang0",
false,
"float",
"");
440 config.add(
"pyrNudger.parity",
"",
"pyrNudger.parity", argType::Required,
"pyrNudger",
"parity",
false,
"float",
"");
442 config.add(
"pyrNudger.F_sign",
"",
"pyrNudger.F_sign", argType::Required,
"pyrNudger",
"F_sign",
false,
"int",
"Pyramid to AEG control matrix [1,1] of a 2x2 matrix ");
444 config.add(
"acqFromGuider.zdSign",
"",
"acqFromGuider.zdSign", argType::Required,
"acqFromGuider",
"zdSign",
false,
"int",
"Sign of the Zd to rotation angle, +1 or -1, -1 default");
445 config.add(
"acqFromGuider.az0",
"",
"acqFromGuider.az0", argType::Required,
"acqFromGuider",
"az0",
false,
"float",
"az component of acquisition vector a 0 zd.");
446 config.add(
"acqFromGuider.azoff",
"",
"acqFromGuider.azoff", argType::Required,
"acqFromGuider",
"azoff",
false,
"float",
"static offset to az component of acquisition vector");
447 config.add(
"acqFromGuider.el0",
"",
"acqFromGuider.el0", argType::Required,
"acqFromGuider",
"el0",
false,
"float",
"el component of acquisition vector a 0 zd.");
448 config.add(
"acqFromGuider.eloff",
"",
"acqFromGuider.eloff", argType::Required,
"acqFromGuider",
"eloff",
false,
"float",
"static offset to el component of acquisition vector");
449 config.add(
"acqFromGuider.focus",
"",
"acqFromGuider.focus", argType::Required,
"acqFromGuider",
"focus",
false,
"float",
"static offset for focus acquisition");
451 config.add(
"offload.TT_avgInt",
"",
"offload.TT_avgInt", argType::Required,
"offload",
"TT_avgInt",
false,
"float",
"Woofer to Telescope T/T offload averaging interval [sec] ");
452 config.add(
"offload.TT_gain",
"",
"offload.TT_gain", argType::Required,
"offload",
"TT_gain",
false,
"float",
"Woofer to Telescope T/T offload gain");
453 config.add(
"offload.TT_thresh",
"",
"offload.TT_thresh", argType::Required,
"offload",
"TT_thresh",
false,
"float",
"Woofer to Telescope T/T offload threshold");
455 config.add(
"offload.lab_TT_C_00",
"",
"offload.lab_TT_C_00", argType::Required,
"offload",
"lab_TT_C_00",
false,
"float",
"Woofer to TTM T/T offload control matrix [0,0] of a 2x2 matrix");
456 config.add(
"offload.lab_TT_C_01",
"",
"offload.lab_TT_C_01", argType::Required,
"offload",
"lab_TT_C_01",
false,
"float",
"Woofer to TTM T/T offload control matrix [0,1] of a 2x2 matrix ");
457 config.add(
"offload.lab_TT_C_10",
"",
"offload.lab_TT_C_10", argType::Required,
"offload",
"lab_TT_C_10",
false,
"float",
"Woofer to TTM T/T offload control matrix [1,0] of a 2x2 matrix ");
458 config.add(
"offload.lab_TT_C_11",
"",
"offload.lab_TT_C_11", argType::Required,
"offload",
"lab_TT_C_11",
false,
"float",
"Woofer to TTM T/T offload control matrix [1,1] of a 2x2 matrix ");
460 config.add(
"offload.TT_C_00",
"",
"offload.TT_C_00", argType::Required,
"offload",
"TT_C_00",
false,
"float",
"Woofer to Telescope T/T offload control matrix [0,0] of a 2x2 matrix");
461 config.add(
"offload.TT_C_01",
"",
"offload.TT_C_01", argType::Required,
"offload",
"TT_C_01",
false,
"float",
"Woofer to Telescope T/T offload control matrix [0,1] of a 2x2 matrix ");
462 config.add(
"offload.TT_C_10",
"",
"offload.TT_C_10", argType::Required,
"offload",
"TT_C_10",
false,
"float",
"Woofer to Telescope T/T offload control matrix [1,0] of a 2x2 matrix ");
463 config.add(
"offload.TT_C_11",
"",
"offload.TT_C_11", argType::Required,
"offload",
"TT_C_11",
false,
"float",
"Woofer to Telescope T/T offload control matrix [1,1] of a 2x2 matrix ");
466 config.add(
"offload.F_avgInt",
"",
"offload.F_avgInt", argType::Required,
"offload",
"F_avgInt",
false,
"float",
"Woofer to Telescope Focus offload averaging interval [sec] ");
467 config.add(
"offload.F_gain",
"",
"offload.F_gain", argType::Required,
"offload",
"F_gain",
false,
"float",
"Woofer to Telescope Focus offload gain");
468 config.add(
"offload.F_thresh",
"",
"offload.F_thresh", argType::Required,
"offload",
"F_thresh",
false,
"float",
"Woofer to Telescope Focus offload threshold");
470 config.add(
"offload.CFocus00",
"",
"offload.CFocus00", argType::Required,
"offload",
"CFocus00",
false,
"float",
"Woofer to Telescope Focus offload control scale factor.");
472 config.add(
"offload.CComa00",
"",
"offload.CComa00", argType::Required,
"offload",
"CComa00",
false,
"float",
"Woofer to Telescope Coma offload control matrix [0,0] of a 2x2 matrix");
473 config.add(
"offload.CComa01",
"",
"offload.CComa01", argType::Required,
"offload",
"CComa01",
false,
"float",
"Woofer to Telescope Coma offload control matrix [0,1] of a 2x2 matrix ");
474 config.add(
"offload.CComa10",
"",
"offload.CComa10", argType::Required,
"offload",
"CComa10",
false,
"float",
"Woofer to Telescope Coma offload control matrix [1,0] of a 2x2 matrix ");
475 config.add(
"offload.CComa11",
"",
"offload.CComa11", argType::Required,
"offload",
"CComa11",
false,
"float",
"Woofer to Telescope Coma offload control matrix [1,1] of a 2x2 matrix ");
477 config.add(
"device.address",
"",
"device.address", argType::Required,
"device",
"address",
false,
"string",
"The IP address or resolvable name of the TCS.");
478 config.add(
"device.port",
"",
"device.port", argType::Required,
"device",
"port",
false,
"int",
"The IP port for TCS communications. Should be the command port. Default is 5811.");
500 _config(
m_acqAz0,
"acqFromGuider.az0");
502 _config(
m_acqEl0,
"acqFromGuider.el0");
567 indi::addNumberElement<double>(
m_indiP_teltime,
"sidereal_time", 0, std::numeric_limits<double>::max(), 0,
"%0.6f");
572 indi::addNumberElement<double>(
m_indiP_telpos,
"epoch", 0, std::numeric_limits<double>::max(), 0,
"%0.6f");
574 indi::addNumberElement<double>(
m_indiP_telpos,
"ra", 0, 360, 0,
"%0.6f");
576 indi::addNumberElement<double>(
m_indiP_telpos,
"dec", -90, 90, 0,
"%0.6f");
578 indi::addNumberElement<double>(
m_indiP_telpos,
"el", 0, 90, 0,
"%0.6f");
580 indi::addNumberElement<double>(
m_indiP_telpos,
"ha", -180, 160, 0,
"%0.6f");
582 indi::addNumberElement<double>(
m_indiP_telpos,
"am", 0, 4, 0,
"%0.2f");
584 indi::addNumberElement<double>(
m_indiP_telpos,
"rotoff", 0, 360, 0,
"%0.6f");
593 indi::addNumberElement<int>(
m_indiP_teldata,
"tracking", 0, 1, 1,
"%d");
595 indi::addNumberElement<int>(
m_indiP_teldata,
"guiding", 0, 1, 1,
"%d");
597 indi::addNumberElement<int>(
m_indiP_teldata,
"slewing", 0, 1, 1,
"%d");
599 indi::addNumberElement<int>(
m_indiP_teldata,
"guider_moving", 0, 1, 1,
"%d");
601 indi::addNumberElement<double>(
m_indiP_teldata,
"az", 0, 360, 0,
"%0.6f");
603 indi::addNumberElement<double>(
m_indiP_teldata,
"zd", 0, 90, 0,
"%0.6f");
605 indi::addNumberElement<double>(
m_indiP_teldata,
"pa", 0, 360, 0,
"%0.6f");
607 indi::addNumberElement<double>(
m_indiP_teldata,
"dome_az", 0, 360, 0,
"%0.6f");
609 indi::addNumberElement<int>(
m_indiP_teldata,
"dome_stat", 0, 1, 1,
"%d");
622 indi::addNumberElement<double>(
m_indiP_catdata,
"ra", 0, 360, 0,
"%0.6f");
624 indi::addNumberElement<double>(
m_indiP_catdata,
"dec", -90, 90, 0,
"%0.6f");
626 indi::addNumberElement<double>(
m_indiP_catdata,
"epoch", 0, std::numeric_limits<double>::max(), 0,
"%0.6f");
628 indi::addNumberElement<double>(
m_indiP_catdata,
"rotoff", 0, 360, 0,
"%0.6f");
636 indi::addNumberElement<double>(
m_indiP_vaneend,
"secz", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
638 indi::addNumberElement<double>(
m_indiP_vaneend,
"encz", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
640 indi::addNumberElement<double>(
m_indiP_vaneend,
"secx", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
642 indi::addNumberElement<double>(
m_indiP_vaneend,
"encx", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
644 indi::addNumberElement<double>(
m_indiP_vaneend,
"secy", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
646 indi::addNumberElement<double>(
m_indiP_vaneend,
"ency", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
648 indi::addNumberElement<double>(
m_indiP_vaneend,
"sech", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
650 indi::addNumberElement<double>(
m_indiP_vaneend,
"ench", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
652 indi::addNumberElement<double>(
m_indiP_vaneend,
"secv", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
654 indi::addNumberElement<double>(
m_indiP_vaneend,
"encv", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.6f");
661 indi::addNumberElement<double>(
m_indiP_env,
"temp-out", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
663 indi::addNumberElement<double>(
m_indiP_env,
"pressure", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
665 indi::addNumberElement<double>(
m_indiP_env,
"humidity", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
667 indi::addNumberElement<double>(
m_indiP_env,
"wind", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
669 indi::addNumberElement<double>(
m_indiP_env,
"winddir", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
671 indi::addNumberElement<double>(
m_indiP_env,
"temp-truss", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
673 indi::addNumberElement<double>(
m_indiP_env,
"temp-cell", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
675 indi::addNumberElement<double>(
m_indiP_env,
"temp-seccell", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
677 indi::addNumberElement<double>(
m_indiP_env,
"temp-amb", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
679 indi::addNumberElement<double>(
m_indiP_env,
"dewpoint", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
685 indi::addNumberElement<unsigned>(
m_indiP_seeing,
"dimm_time", std::numeric_limits<unsigned>::lowest(), std::numeric_limits<unsigned>::max(), 0,
"%d");
687 indi::addNumberElement<double>(
m_indiP_seeing,
"dimm_el", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
689 indi::addNumberElement<double>(
m_indiP_seeing,
"dimm_fwhm", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
691 indi::addNumberElement<double>(
m_indiP_seeing,
"dimm_fwhm_corr", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
693 indi::addNumberElement<unsigned>(
m_indiP_seeing,
"mag1_time", std::numeric_limits<unsigned>::lowest(), std::numeric_limits<unsigned>::max(), 0,
"%d");
695 indi::addNumberElement<double>(
m_indiP_seeing,
"mag1_el", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
697 indi::addNumberElement<double>(
m_indiP_seeing,
"mag1_fwhm", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
699 indi::addNumberElement<double>(
m_indiP_seeing,
"mag1_fwhm_corr", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
701 indi::addNumberElement<unsigned>(
m_indiP_seeing,
"mag2_time", std::numeric_limits<unsigned>::lowest(), std::numeric_limits<unsigned>::max(), 0,
"%d");
703 indi::addNumberElement<double>(
m_indiP_seeing,
"mag2_el", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
705 indi::addNumberElement<double>(
m_indiP_seeing,
"mag2_fwhm", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
707 indi::addNumberElement<double>(
m_indiP_seeing,
"mag2_fwhm_corr", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0,
"%0.2f");
712 signal(SIGPIPE, SIG_IGN);
733 log<software_error>({__FILE__,__LINE__});
740 log<software_error>({__FILE__,__LINE__});
747 log<software_error>({__FILE__,__LINE__});
756 log<software_error>({__FILE__,__LINE__});
765 log<software_error>({__FILE__,__LINE__});
774 log<software_error>({__FILE__,__LINE__});
781 log<software_error>({__FILE__,__LINE__});
788 log<software_error>({__FILE__,__LINE__});
797 log<software_error>({__FILE__,__LINE__});
806 log<software_error>({__FILE__,__LINE__});
815 log<software_error>({__FILE__,__LINE__});
829 log<software_error>({__FILE__, __LINE__});
863 static int lastrv = 0;
864 static int lasterrno = 0;
875 std::stringstream logs;
877 log<text_log>(logs.str());
893 if( errno != lasterrno )
895 log<software_error>( {__FILE__,__LINE__, errno});
904 std::string response;
977 const std::string &statreq
983 std::string statreq_nl;
985 #ifdef LOG_TCS_STATUS
986 log<text_log>(
"Sending status request: " + statreq);
989 std::lock_guard<std::mutex> guard(
m_tcsMutex);
991 statreq_nl = statreq;
1006 log<text_log>(
"No response received to status request: " + statreq,
logPrio::LOG_ERROR);
1011 char * nl = strchr(answer,
'\n');
1012 if(nl) answer[nl-answer] =
'\0';
1014 #ifdef LOG_TCS_STATUS
1015 log<text_log>(std::string(
"Received response: ") + answer);
1030 std::string command_nl;
1032 #ifdef LOG_TCS_STATUS
1033 log<text_log>(
"Sending command: " + command);
1036 std::lock_guard<std::mutex> guard(
m_tcsMutex);
1038 command_nl = command;
1056 char * nl = strchr(answer,
'\n');
1057 if(nl) answer[nl-answer] =
'\0';
1059 #ifdef LOG_TCS_STATUS
1060 log<text_log>(std::string(
"Received response: ") + answer);
1063 return atoi(answer);
1070 std::vector<std::string> vres;
1078 pos1 = tdat.find_first_not_of(
" " , 0);
1080 if(pos1 == -1) pos1 = 0;
1082 pos2 = tdat.find_first_of(
" ", pos1);
1086 tok = tdat.substr(pos1, pos2-pos1);
1088 vres.push_back(tok);
1093 pos2 = tdat.find_first_not_of(
" ", pos1);
1098 pos2 = tdat.find_first_of(
" ", pos1);
1104 pos2 = tdat.length();
1106 tok = tdat.substr(pos1, pos2-pos1);
1108 pos2 = tok.find_first_of(
" \n\r", 0);
1110 if(pos2 >= 0) tok.erase(pos2, tok.length()-pos2);
1112 vres.push_back(tok);
1122 const std::string & xmsstr
1130 en = xmsstr.find(
':', st);
1133 if(en == std::string::npos)
1135 log<software_error>({__FILE__, __LINE__,
"error parsing x:m:s"});
1140 if(en - st < 2 || en < st)
1142 log<software_error>({__FILE__, __LINE__,
"error parsing x:m:s"});
1149 xstr = xmsstr.substr(st, en-st);
1151 catch(
const std::exception & e)
1153 log<software_error>({__FILE__, __LINE__, e.what()});
1159 x = std::stod(xstr);
1161 catch(
const std::exception & e)
1163 log<software_error>({__FILE__, __LINE__, e.what()});
1168 if(std::signbit(x)) sgn = -1;
1169 if(xmsstr[0] ==
'-') sgn = -1;
1173 en = xmsstr.find(
':', st);
1176 if(en == std::string::npos)
1178 log<software_error>({__FILE__, __LINE__,
"error parsing x:m:s"});
1183 if(en - st < 2 || en < st)
1185 log<software_error>({__FILE__, __LINE__,
"error parsing x:m:s"});
1192 mstr = xmsstr.substr(st, en-st);
1194 catch(
const std::exception & e)
1196 log<software_error>({__FILE__, __LINE__, e.what()});
1202 m = sgn*std::stod(mstr);
1204 catch(
const std::exception & e)
1206 log<software_error>({__FILE__, __LINE__, e.what()});
1212 if(st >= xmsstr.length() - 1)
1214 log<software_error>({__FILE__, __LINE__,
"error parsing x:m:s"});
1221 sstr = xmsstr.substr(st, xmsstr.length()-st);
1223 catch(
const std::exception & e)
1225 log<software_error>({__FILE__, __LINE__, e.what()});
1231 s = sgn*std::stod(sstr);
1233 catch(
const std::exception & e)
1235 log<software_error>({__FILE__, __LINE__, e.what()});
1247 std::vector<std::string> pdat;
1262 log<text_log>(
"Error getting telescope time (datetime): TCS returned -1",
logPrio::LOG_WARNING);
1266 if(pdat.size() != 3)
1269 log<text_log>(
"Error getting telescope position (datetime): TCS response wrong size, returned " + std::to_string(pdat.size()) +
" values",
logPrio::LOG_WARNING);
1279 m_telST = (h + m/60. + s/3600.);
1289 std::vector<std::string> pdat;
1304 log<text_log>(
"Error getting telescope position (telpos): TCS returned -1",
logPrio::LOG_WARNING);
1308 if(pdat.size() != 6)
1311 log<text_log>(
"Error getting telescope position (telpos): TCS response wrong size, returned " + std::to_string(pdat.size()) +
" values",
logPrio::LOG_WARNING);
1321 m_telRA = (h + m/60. + s/3600.)*15.;
1343 m_telHA = h + m/60. + s/3600.;
1345 m_telAM = strtod(pdat[4].c_str(),0);
1361 std::vector<std::string> tdat;
1375 log<text_log>(
"Error getting telescope data (teldata): TCS returned -1",
logPrio::LOG_WARNING);
1379 if(tdat.size() != 10)
1382 log<text_log>(
"[TCS] Error getting telescope data (teldata): TCS response wrong size, returned " + std::to_string(tdat.size()) +
" values",
logPrio::LOG_WARNING);
1390 char bit[2] = {0,0};
1392 bit[0] = tdat[1].c_str()[0];
1394 bit[0] = tdat[1].c_str()[1];
1398 bit[0] = tdat[2].c_str()[0];
1400 bit[0] = tdat[2].c_str()[1];
1405 m_telAz = strtod(tdat[4].c_str(),0);
1407 m_telEl = strtod(tdat[5].c_str(),0);
1409 m_telZd = strtod(tdat[6].c_str(),0);
1411 m_telPA = strtod(tdat[7].c_str(),0);
1430 std::vector<std::string> cdat;
1449 if(cdat.size() != 6)
1451 bool pointing =
false;
1453 if(cdat.size() == 7)
1455 if(cdat[6] ==
"Pointing")
1464 log<text_log>(
"Catalog data (catdata): TCS response wrong size, returned " + std::to_string(cdat.size()) +
" values",
logPrio::LOG_WARNING);
1466 for(
size_t n = 0; n < cdat.size(); ++n)
1468 std::cerr << n <<
" " << cdat[n] <<
"\n";
1492 m_catRA = (h + m/60. + s/3600.)*15.;
1502 m_catEp = strtod(cdat[2].c_str(),0);
1504 m_catRo = strtod(cdat[3].c_str(),0);
1517 std::vector<std::string> vedat;
1522 log<text_log>(
"Error getting telescope secondary positions (vedata)",
logPrio::LOG_ERROR);
1528 if(vedat[0] ==
"-1")
1531 log<text_log>(
"Error getting telescope secondary positions (vedata): TCS returned -1",
logPrio::LOG_WARNING);
1535 if(vedat.size() != 10)
1538 log<text_log>(
"Error getting telescope secondary positions (vedata): TCS response wrong size, returned " + std::to_string(vedat.size()) +
" values",
logPrio::LOG_WARNING);
1566 std::vector<std::string> edat;
1571 log<text_log>(
"Error getting telescope environment data (telenv)",
logPrio::LOG_ERROR);
1580 log<text_log>(
"Error getting telescope environment data (telenv): TCS returned -1",
logPrio::LOG_WARNING);
1584 if(edat.size() != 10)
1587 log<text_log>(
"Error getting telescope environment data (telenv): TCS response wrong size, returned " + std::to_string(edat.size()) +
"values",
logPrio::LOG_WARNING);
1591 m_wxtemp = strtod(edat[0].c_str(), 0);
1592 m_wxpres = strtod(edat[1].c_str(), 0);
1594 m_wxwind = strtod(edat[3].c_str(), 0);
1595 m_wxwdir = strtod(edat[4].c_str(), 0);
1596 m_ttruss = strtod(edat[5].c_str(), 0);
1597 m_tcell = strtod(edat[6].c_str(), 0);
1613 static int last_query = 0;
1619 time_t sec_midnight;
1622 int rv = system(
"query_seeing > /dev/null");
1625 log<software_error>({__FILE__, __LINE__,
"Error from seeing query"});
1630 last_query = time(0);
1631 sec_midnight = last_query % 86400;
1634 std::string label, datestr, timestr, fwhmstr;
1638 fin.open(
"/tmp/dimm.tsv");
1642 log<software_error>({__FILE__, __LINE__,
"Error reading dimm seeing"});
1661 if(dt < 0) dt = 86400-dt;
1674 fin.open(
"/tmp/mag1.tsv");
1678 log<software_error>({__FILE__, __LINE__,
"Error reading mag1 seeing"});
1697 if(dt < 0) dt = 86400-dt;
1710 fin.open(
"/tmp/mag2.tsv");
1714 log<software_error>({__FILE__, __LINE__,
"Error reading mag2 seeing"});
1733 if(dt < 0) dt = 86400-dt;
1765 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1803 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1842 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1852 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1862 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1874 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1910 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1950 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1960 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1970 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
1991 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2001 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2011 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2036 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2046 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2056 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2090 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2121 log<software_error>({__FILE__,__LINE__,
"INDI library exception"});
2179 static double lastEpoch = 0;
2180 static double lastRA = 0;
2181 static double lastDec = 0;
2182 static double lastEl = 0;
2183 static double lastHA = 0;
2184 static double lastAM = 0;
2185 static double lastRotOff = 0;
2212 static int lastROI = -999;
2213 static int lastTracking = -999;
2214 static int lastGuiding = -999;
2215 static int lastSlewing = -999;
2216 static int lastGuiderMoving = -999;
2217 static double lastAz = 0;
2218 static double lastZd = 0;
2219 static double lastPA = 0;
2220 static double lastDomeAz = 0;
2221 static int lastDomeStat = -999;
2223 if( force || lastROI !=
m_telROI ||
2254 static double lastSecZ = -999;
2255 static double lastEncZ = -999;
2256 static double lastSecX = -999;
2257 static double lastEncX = -999;
2258 static double lastSecY = -999;
2259 static double lastEncY = -999;
2260 static double lastSecH = -999;
2261 static double lastEncH = -999;
2262 static double lastSecV = -999;
2263 static double lastEncV = -999;
2296 static double lastWxtemp = -999;
2297 static double lastWxpres = -999;
2298 static double lastWxhumid = -999;
2299 static double lastWxwind = -999;
2300 static double lastWxwdir = -999;
2301 static double lastTtruss = -999;
2302 static double lastTcell = -999;
2303 static double lastTseccell = -999;
2304 static double lastTambient = -999;
2305 static double lastWxdewpoint = -999;
2308 if( force || lastWxtemp !=
m_wxtemp ||
2340 static std::string last_catObj;
2341 static std::string last_catRm;
2342 static double last_catRA = 0;
2343 static double last_catDec = 0;
2344 static double last_catEp = 0;
2345 static double last_catRo = 0;
2347 if(force ||
m_catObj != last_catObj ||
2370 static int last_dimm_time = 0;
2371 static double last_dimm_el = 0;
2372 static double last_dimm_fwhm = 0;
2375 static int last_mag1_time = 0;
2376 static double last_mag1_el = 0;
2377 static double last_mag1_fwhm = 0;
2378 static double last_mag1_fwhm_corr = 0;
2380 static int last_mag2_time = 0;
2381 static double last_mag2_el = 0;
2382 static double last_mag2_fwhm = 0;
2383 static double last_mag2_fwhm_corr = 0;
2399 telem<telem_telsee>({
m_dimm_time,
m_dimm_el,
m_dimm_fwhm, -1,
m_mag1_time,
m_mag1_el,
m_mag1_fwhm,
m_mag1_fwhm_corr,
m_mag2_time,
m_mag2_el,
m_mag2_fwhm,
m_mag2_fwhm_corr});
2425 if(x != 0 || y != 0)
2437 snprintf(ttstr,
sizeof(ttstr) ,
"aeg %f %f", dx, dy);
2443 log<software_error>({__FILE__,__LINE__, std::string(
"error sending command: ") + ttstr});
2456 log<software_error>({__FILE__,__LINE__, std::string(
"error sending command: ") + ttstr});
2474 snprintf(ttstr,
sizeof(ttstr) ,
"aeg %f %f", az, el);
2480 log<software_error>({__FILE__,__LINE__, std::string(
"error sending command: ") + ttstr});
2490 log<software_error>({__FILE__,__LINE__, std::string(
"error sending command: ") + ttstr});
2507 static int last_loopState = -1;
2517 int sincelast_TT = 0;
2520 int sincelast_F = 0;
2639 log<text_log>(
"[OFFL] TT dumping: " + std::to_string(tt_0) +
" " + std::to_string(tt_1));
2652 if(tt_0 ==0 && tt_1 == 0)
2658 log<text_log>(
"[OFFL] TT offloading: " + std::to_string(tt_0) +
" " + std::to_string(tt_1));
2678 pcf::IndiProperty ip(pcf::IndiProperty::Number);
2679 ip.setDevice(
"modwfs");
2680 ip.setName(
"offset12");
2681 ip.add(pcf::IndiElement(
"dC1"));
2682 ip.add(pcf::IndiElement(
"dC2"));
2696 snprintf(ttstr,
sizeof(ttstr) ,
"aeg %f %f", tt_0, tt_1);
2698 log<text_log>(std::string(
"[OFFL] sending: ") + ttstr);
2707 log<text_log>(
"[OFFL] Focus dumping: " + std::to_string(F_0));
2723 log<text_log>(
"[OFFL] Focus sending: " + std::to_string(F_0));
2729 log<text_log>(
"Focus offload above threshold but Focus offloading disabled",
logPrio::LOG_WARNING);
2741 snprintf(fstr,
sizeof(fstr),
"zimr %f", F_0);
2743 log<text_log>(std::string(
"[OFFL] sending: ") + fstr);
2752 if(!
ipRecv.find(
"toggle"))
2759 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
2768 if(m_labMode == labMode)
2773 m_labMode = labMode;
2799 x =
ipRecv[
"y"].get<
float>();
2804 y =
ipRecv[
"x"].get<
float>();
2809 z =
ipRecv[
"z"].get<
float>();
2812 return sendPyrNudge(x, y, z);
2819 if(!
ipRecv.find(
"request"))
2824 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2826 return acquireFromGuider();
2836 if(!
ipRecv.find(
"toggle"))
return 0;
2838 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
2854 if(m_loopState != 2)
return 0;
2856 size_t nextReq = m_lastRequest + 1;
2858 if(nextReq >= m_offloadRequests[0].size()) nextReq = 0;
2861 float tt0 =
ipRecv[
"00"].get<
float>();
2862 float tt1 =
ipRecv[
"01"].get<
float>();
2866 m_offloadRequests[0][nextReq] = m_lab_offlTT_C_00 * tt0 + m_lab_offlTT_C_01 * tt1;
2867 m_offloadRequests[1][nextReq] = m_lab_offlTT_C_10 * tt0 + m_lab_offlTT_C_11 * tt1;
2871 m_offloadRequests[0][nextReq] = m_offlTT_C_00 * tt0 + m_offlTT_C_01 * tt1;
2872 m_offloadRequests[1][nextReq] = m_offlTT_C_10 * tt0 + m_offlTT_C_11 * tt1;
2876 float f0 =
ipRecv[
"02"].get<
float>();
2878 m_offloadRequests[2][nextReq] = m_offlCFocus_00 * f0;
2882 float c0 =
ipRecv[
"03"].get<
float>();
2883 float c1 =
ipRecv[
"04"].get<
float>();
2885 m_offloadRequests[3][nextReq] = m_offlCComa_00 * c0 + m_offlCComa_01 * c1;
2886 m_offloadRequests[4][nextReq] = m_offlCComa_10 * c0 + m_offlCComa_11 * c1;
2889 m_lastRequest = nextReq;
2891 if(m_nRequests > m_offloadRequests[0].size()) ++m_firstRequest;
2892 if(m_firstRequest >= m_offloadRequests[0].size()) m_firstRequest = 0;
2900 if(
ipRecv.getName() != m_indiP_offlTTenable.getName())
2902 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
2906 if(!
ipRecv.find(
"toggle"))
return 0;
2908 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On && m_offlTT_enabled ==
false)
2912 m_offlTT_enabled =
true;
2914 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off && m_offlTT_enabled ==
true)
2918 m_offlTT_enabled =
false;
2929 if(!
ipRecv.find(
"request"))
return 0;
2931 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2935 m_offlTT_dump =
true;
2948 if( indiTargetUpdate( m_indiP_offlTTavgInt, target,
ipRecv,
true) < 0)
2950 log<software_error>({__FILE__,__LINE__});
2954 m_offlTT_avgInt = target;
2965 if( indiTargetUpdate( m_indiP_offlTTgain, target,
ipRecv,
true) < 0)
2967 log<software_error>({__FILE__,__LINE__});
2971 m_offlTT_gain = target;
2982 if( indiTargetUpdate( m_indiP_offlTTthresh, target,
ipRecv,
true) < 0)
2984 log<software_error>({__FILE__,__LINE__});
2988 m_offlTT_thresh = target;
2998 if(!
ipRecv.find(
"toggle"))
return 0;
3000 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On && m_offlF_enabled ==
false)
3004 m_offlF_enabled =
true;
3006 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off && m_offlF_enabled ==
true)
3010 m_offlF_enabled =
false;
3021 if(!
ipRecv.find(
"request"))
return 0;
3023 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
3027 m_offlF_dump =
true;
3040 if( indiTargetUpdate( m_indiP_offlFavgInt, target,
ipRecv,
true) < 0)
3042 log<software_error>({__FILE__,__LINE__});
3046 m_offlF_avgInt = target;
3057 if( indiTargetUpdate( m_indiP_offlFgain, target,
ipRecv,
true) < 0)
3059 log<software_error>({__FILE__,__LINE__});
3063 m_offlF_gain = target;
3076 if( indiTargetUpdate( m_indiP_offlFthresh, target,
ipRecv,
true) < 0)
3078 log<software_error>({__FILE__,__LINE__});
3082 m_offlF_thresh = target;
The base-class for MagAO-X applications.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
int createStandardIndiNumber(pcf::IndiProperty &prop, const std::string &name, const T &min, const T &max, const T &step, const std::string &format, const std::string &label="", const std::string &group="")
Create a standard R/W INDI Number property with target and current elements.
int shutdown()
Get the value of the shutdown flag.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
int createROIndiText(pcf::IndiProperty &prop, const std::string &propName, const std::string &elName, const std::string &propLabel="", const std::string &propGroup="", const std::string &elLabel="")
Create a standard ReadOnly INDI Text property, with at least one element.
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
The MagAO-X Clay Telescope TCS Interface.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFgain)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTgain)
double m_dimm_el
DIMM elevation at time of seeing measurement.
pcf::IndiProperty m_indiP_offlTTdump
double m_wxdewpoint
Dew point from weather station.
int recordTelem(const telem_telpos *)
virtual int appLogic()
Implementation of the FSM for tcsInterface.
pcf::IndiProperty m_indiP_offlFgain
pcf::IndiProperty m_indiP_acqFromGuider
Property used to request a pyramid nudge.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTthresh)
~tcsInterface() noexcept
D'tor, declared and defined for noexcept.
pcf::IndiProperty m_indiP_seeing
INDI Property for seeing.
double m_mag1_fwhm
MAG1 raw FWHM.
double m_telDomeAz
dome azimuth
int m_telGuiding
guider moving state
int sendMagTelCommand(const std::string &command, int timeout)
int recordTelCat(bool force=false)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFavgInt)
double m_catRA
Catalog right ascension [degrees].
pcf::IndiProperty m_indiP_teltime
double m_mag1_el
MAG1 elevation at time of seeing measurement.
double m_dimm_fwhm_corr
DIMM elevation corrected FWHM.
pcf::IndiProperty m_indiP_labMode
int m_telGuiderMoving
guider moving state
pcf::IndiProperty m_indiP_env
INDI Property for environment.
int sendTToffload(float TT_0, float TT_1)
pcf::IndiProperty m_indiP_pyrNudge
Property used to request a pyramid nudge.
int recordTelPos(bool force=false)
int recordTelEnv(bool force=false)
pcf::IndiProperty m_indiP_telpos
int recordTelData(bool force=false)
double m_telPA
parallactic angle
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTenable)
double m_wxpres
Outside pressue, millibars.
std::string m_catObj
Catalog object name.
int m_mag2_time
Seconds since midnight of MAG2 measurement.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFthresh)
pcf::IndiProperty m_indiP_offloadCoeffs
Property used to report the latest woofer modal coefficients for offloading.
pcf::IndiProperty m_offloadThreadProp
Offload thread INDI property.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_pyrNudge)
pcf::IndiProperty m_indiP_offlTTgain
pcf::IndiProperty m_indiP_catdata
INDI Property for the catalog data.
bool m_offloadThreadInit
Initialization flag for the offload thread.
int m_mag1_time
Seconds since midnight of MAG1 measurement.
double m_mag2_el
MAG2 elevation at time of seeing measurement.
std::thread m_offloadThread
The offloading thread.
double m_telZd
zenith distance
friend class tcsInterface_test
void offloadThreadExec()
Offload thread function.
int getMagTelStatus(std::string &response, const std::string &statreq)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFdump)
virtual void loadConfig()
double m_catDec
Catalog declination [degrees].
double m_wxwind
outside wind intensity, mph
pcf::IndiProperty m_indiP_teldata
int parse_xms(double &x, double &m, double &s, const std::string &xmsstr)
int m_devicePort
The IP port for TCS communications. Should be the command port. Default is 5811.
std::mutex m_tcsMutex
Mutex for locking TCS communications.
double m_mag2_fwhm_corr
MAG2 elevation corrected FWHM.
double m_tcell
Primary mirror cell temperature, Celsius.
double m_mag1_fwhm_corr
MAG1 elevation corrected FWHM.
virtual void setupConfig()
std::string m_deviceAddr
The IP address or resolvable name of the TCS.
pid_t m_offloadThreadID
Offload thread pid.
pcf::IndiProperty m_indiP_vaneend
INDI Property for the vane end positions.
std::string m_catRm
Catalog rotator mode.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_acqFromGuider)
pcf::IndiProperty m_indiP_offlFenable
INDI_SETCALLBACK_DECL(tcsInterface, m_indiP_offloadCoeffs)
int m_dimm_time
Seconds since midnight of DIMM measurement.
pcf::IndiProperty m_indiP_offlFdump
double m_catEp
Catalog epoch.
double m_tambient
Dome air temperature, Celsius.
tcsInterface()
Default c'tor.
int sendFoffload(float F_0)
pcf::IndiProperty m_indiP_offlTTenable
double m_ttruss
Telescope truss temperature, Celsius.
int doTToffload(float TT_0, float TT_1)
pcf::IndiProperty m_indiP_catalog
INDI Property for the catalog text information.
double m_wxhumid
Outside humidity, percent.
virtual int appStartup()
Startup function.
int recordTelVane(bool force=false)
virtual int appShutdown()
Shutdown the app.
int recordTelSee(bool force=false)
int doFoffload(float F_0)
int m_telDomeStat
dome status
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int m_telSlewing
slewing state
int m_telROI
The rotator of interest.
int m_telTracking
tracking state
double m_catRo
Catalog rotator offset.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFenable)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_labMode)
double m_wxtemp
Outside temperature, Celsius.
pcf::IndiProperty m_indiP_offlTTavgInt
double m_dimm_fwhm
DIMM raw FWHM.
static void offloadThreadStart(tcsInterface *t)
Offload thread starter function.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTdump)
pcf::IndiProperty m_indiP_loopState
Property used to report the loop state.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTavgInt)
double m_mag2_fwhm
MAG2 raw FWHM.
double m_wxwdir
outside wind direction, degrees
double m_tseccell
Secondary mirror cell temperature, Celsius.
pcf::IndiProperty m_indiP_offlFavgInt
std::vector< std::string > parse_teldata(std::string &tdat)
pcf::IndiProperty m_indiP_offlFthresh
pcf::IndiProperty m_indiP_offlTTthresh
std::vector< std::vector< float > > m_offloadRequests
int sendPyrNudge(float x, float y, float z)
INDI_SETCALLBACK_DECL(tcsInterface, m_indiP_loopState)
#define CREATE_REG_INDI_NEW_TOGGLESWITCH(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
const pcf::IndiProperty & ipRecv
INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
#define NETSERIAL_E_NOERROR
An input/output capable device.
int loadConfig(mx::app::appConfigurator &config)
Load the device section from an application configurator.
int appStartup()
Perform application startup steps specific to an ioDevice.
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the device section.
unsigned m_readTimeout
The read timeout [msec].
A device base class which saves telemetry.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int appLogic()
Perform telemeter application logic.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Manage a connectio to a serial device over a network.
int serialOut(const char *buf, int len)
int serialInString(char *buf, int len, int timeout, char terminator)
int serialInit(const char *address, int port)