00001
00005 #include "radiometry.hpp"
00006
00007 int main(int argc, char **argv) {
00008
00009 ros::init(argc, argv, "radiometry");
00010
00011 ros::NodeHandle private_node_handle("~");
00012
00013 radiometryData startupData;
00014
00015 startupData.read_addr = argv[0];
00016 startupData.read_addr = startupData.read_addr.substr(0, startupData.read_addr.size()-12);
00017 bool inputIsValid = startupData.obtainStartingData(private_node_handle);
00018
00019 if (!inputIsValid) {
00020 ROS_ERROR("Configuration invalid!\n");
00021 return -1;
00022 }
00023
00024 ros::NodeHandle nh;
00025
00026 ROS_INFO("Establishing radiometry node...");
00027 boost::shared_ptr < radiometryNode > radiometry_node (new radiometryNode (nh, startupData));
00028
00029 globalNodePtr = &radiometry_node;
00030
00031 if (!(*globalNodePtr)->isNodeValid()) {
00032 ros::shutdown();
00033 return 0;
00034 }
00035
00036 signal(SIGINT, mySigintHandler);
00037
00038 if (wantsToShutdown) {
00039 ros::shutdown();
00040 return 0;
00041 }
00042
00043
00044
00045 while ((*globalNodePtr)->dataStillStreaming()) {
00046
00047 ros::spinOnce();
00048
00049 }
00050
00051 ros::shutdown();
00052
00053 return 0;
00054
00055 }
00056
00057 bool radiometryNode::dataStillStreaming() {
00058
00059
00060
00061
00062 return true;
00063 }
00064
00065 void radiometryNode::prepareForTermination() {
00066
00067 if (configData.communicateWithBlackbody) {
00068 closeBlackbodyComms();
00069 }
00070
00071 if (ofs_thermistor_log.is_open()) ofs_thermistor_log.close();
00072 if (ofs_image_log.is_open()) ofs_image_log.close();
00073
00074 }
00075
00076 bool radiometryNode::isNodeValid() {
00077 return nodeStillValid;
00078 }
00079
00080 void mySigintHandler(int sig)
00081 {
00082
00083 ROS_WARN("Requested shutdown... terminating feeds...");
00084 wantsToShutdown = true;
00085
00086 (*globalNodePtr)->prepareForTermination();
00087
00088 }
00089
00090
00091
00092
00093 radiometryNode::radiometryNode(ros::NodeHandle& nh, radiometryData startupData) {
00094
00095 nodeStillValid = true;
00096
00097 temperatureIsStable = false;
00098 stabilityOnceOffFlag = false;
00099
00100 blackbodyIsSetup = false;
00101 imageIndex = 0;
00102
00103 stabilityTimer = 0.0;
00104 elapsedTime = -1.0;
00105
00106 sprintf(nodeName, "%s", ros::this_node::getName().substr(1).c_str());
00107
00108 ROS_INFO("Initializing node (%s)", nodeName);
00109
00110
00111
00112 configData = startupData;
00113
00114
00115
00116 ROS_INFO("Establishing server callback...");
00117 f = boost::bind (&radiometryNode::serverCallback, this, _1, _2);
00118 server.setCallback (f);
00119
00120 if (configData.communicateWithBlackbody) {
00121 if (configData.serialPollingRate > 0.1) {
00122 ROS_INFO("Initializing serial timer at (%f)", 1.0 / ((double) configData.serialPollingRate));
00123 serial_timer = nh.createTimer(ros::Duration(1.0 / ((double) configData.serialPollingRate)), &radiometryNode::serialCallback, this);
00124 } else {
00125 ROS_INFO("Initializing serial timer at (%f)", DEFAULT_SERIAL_POLLING_RATE);
00126 serial_timer = nh.createTimer(ros::Duration(DEFAULT_SERIAL_POLLING_RATE), &radiometryNode::serialCallback, this);
00127 }
00128
00129 nodeStillValid = setupBlackbodyComms();
00130
00131 if (nodeStillValid) {
00132 nodeStillValid = setupBlackbody();
00133
00134 blackbodyIsSetup = true;
00135
00136
00137
00138 }
00139
00140
00141 }
00142
00143 if (!setupFilesAndDirectories()) {
00144 wantsToShutdown = true;
00145 (*globalNodePtr)->prepareForTermination();
00146 }
00147
00148 thermistorLogFile = configData.outputFolder + "/" + string(subdirectoryName) + "-thermistor-log.txt";
00149 ofs_thermistor_log.open(thermistorLogFile.c_str());
00150
00151 imageLogFile = configData.outputFolder + "/" + string(subdirectoryName) + "-image-log.txt";
00152 ofs_image_log.open(imageLogFile.c_str());
00153
00154 serialCommsFile = configData.outputFolder + "/" + string(subdirectoryName) + "-serial-log.txt";
00155
00156
00157
00158
00159 if (nodeStillValid) {
00160 it = new image_transport::ImageTransport(nh);
00161
00162 if (configData.verboseMode) { ROS_INFO("Subscribing to topic: (%s)", configData.thermal_topic.c_str()); }
00163 camera_sub = it->subscribeCamera(configData.thermal_topic, 1, &radiometryNode::handle_camera, this);
00164 }
00165
00166 }
00167
00168 bool radiometryNode::setupFilesAndDirectories() {
00169 DIR * dirp;
00170 struct dirent * entry;
00171
00172 dirp = opendir(configData.outputFolder.c_str());
00173
00174
00175
00176 if (dirp != NULL) {
00177 ROS_WARN("Directory (%s) already exists!", configData.outputFolder.c_str());
00178
00179 } else {
00180
00181 struct stat st;
00182 if(stat(configData.outputFolder.c_str(),&st) != 0) {
00183 char folderCommand[256];
00184 sprintf(folderCommand, "mkdir -p %s", configData.outputFolder.c_str());
00185 int res = system(folderCommand);
00186
00187 if (res == 0) {
00188
00189 }
00190 }
00191 ROS_WARN("Directory (%s) created.", configData.outputFolder.c_str());
00192 }
00193
00194
00195
00196
00197 char coeff;
00198
00199 if (configData.targetTemperature < 0) {
00200 coeff = 'n';
00201 } else {
00202 coeff = 'p';
00203 }
00204
00205 sprintf(subdirectoryName, "deg-%c%05.02f", coeff, abs(configData.targetTemperature));
00206
00207
00208
00209
00210
00211 dirp = opendir(configData.outputFolder.c_str());
00212
00213 while ((entry = readdir(dirp)) != NULL) {
00214 if (entry->d_type == DT_DIR) {
00215 if (strcmp(entry->d_name, subdirectoryName) == 0) {
00216 ROS_ERROR("Subdirectory (%s) already exists! (This temperature has already been tested.)", subdirectoryName);
00217 closedir(dirp);
00218 return false;
00219 }
00220 }
00221 }
00222
00223 closedir(dirp);
00224
00225
00226 string fullFolder = configData.outputFolder + "/" + subdirectoryName;
00227 struct stat st;
00228 if(stat(fullFolder.c_str(),&st) != 0) {
00229 char folderCommand[256];
00230 sprintf(folderCommand, "mkdir -p %s", fullFolder.c_str());
00231
00232 ROS_WARN("Creating subdirectory (%s)..", folderCommand);
00233 int res = system(folderCommand);
00234
00235 if (res == 0) {
00236
00237 }
00238 }
00239
00240 return true;
00241
00242 }
00243
00244 void radiometryNode::serialCallback(const ros::TimerEvent&) {
00245
00246
00247
00248 if (!temperatureIsStable) {
00249
00250 if (elapsedTime < 0.0) {
00251 elapsedTime = 0.0;
00252 } else {
00253 elapsedTime = (ros::Time::now().toNSec() - previousTime.toNSec()) / 1000000000.0;
00254 }
00255
00256 previousTime = ros::Time::now();
00257
00258 readTemperature(currentTemp);
00259
00260 double tempDiff = abs(currentTemp - configData.targetTemperature);
00261
00262 if (tempDiff > (configData.maxStableVariation + TEMP_ERROR_HACK)) {
00263 stabilityTimer = 0.0;
00264 ROS_WARN("Temp diff still out of range (%1.2f > %1.2f)", tempDiff, configData.maxStableVariation);
00265 } else {
00266
00267 stabilityTimer += elapsedTime;
00268
00269
00270 if (stabilityTimer > configData.minStableTime) {
00271 temperatureIsStable = true;
00272 stabilityOnceOffFlag = true;
00273 ROS_INFO("=====================================================");
00274 ROS_INFO("Blackbody source sufficiently stable for calibration!");
00275 ROS_INFO("=====================================================");
00276
00277
00278 elapsedTime = -1.0;
00279
00280 } else {
00281 ROS_WARN("Elapsed time at stable temperature not yet sufficient (%f < %f)", stabilityTimer, configData.minStableTime);
00282 }
00283
00284
00285
00286 }
00287
00288 } else {
00289
00290
00291
00292 readTemperature(currentTemp);
00293
00294 double tempDiff = abs(currentTemp - configData.targetTemperature);
00295
00296 if (tempDiff > (configData.maxStableVariation + TEMP_ERROR_HACK)) {
00297 ROS_WARN("Temp diff currently out of range (%1.2f > %1.2f)", tempDiff, configData.maxStableVariation);
00298 }
00299
00300 }
00301
00302
00303
00304
00305
00306
00307 }
00308
00309 bool radiometryNode::closeBlackbodyComms() {
00310
00311
00312 free(tab_rp_bits);
00313 free(tab_rp_registers);
00314
00315
00316 modbus_close(ctx);
00317 modbus_free(ctx);
00318
00319 return true;
00320 }
00321
00322 bool radiometryNode::changeSetPoint(double newVal) {
00323
00324
00325
00326 int rc;
00327
00328 int integerVal = int(abs(newVal)*100);
00329
00330 if (newVal < 0) {
00331 integerVal = 65535 - integerVal + 1;
00332 }
00333
00334 uint16_t numberOfWords = 0x1;
00335 uint16_t sendValue[1];
00336
00337 sendValue[0] = integerVal;
00338
00339
00340
00341
00342 rc = modbus_write_registers(ctx, ISOTECH_SETPOINT_ADDR, numberOfWords, sendValue);
00343
00344
00345 if (rc != numberOfWords) {
00346 ROS_ERROR("changeSetPoint() Failed.");
00347 return false;
00348 }
00349
00350
00351 return true;
00352
00353 }
00354
00355 bool radiometryNode::readSetpoint(double& setPoint) {
00356
00357
00358
00359 int rc;
00360
00361 uint16_t numberOfWords = 0x1;
00362
00363 uint16_t *dest;
00364 dest = new uint16_t[256];
00365
00366
00367 rc = modbus_read_registers(ctx, ISOTECH_SP_ADDR, numberOfWords, dest);
00368
00369
00370 uint16_t x;
00371 std::stringstream ss;
00372
00373 ss << std::hex << dest[0];
00374 ss >> x;
00375
00376 setPoint = float(x) / 100.0;
00377
00378 if (setPoint > MAXIMUM_BLACKBODY_TEMPERATURE) {
00379 setPoint = -float(65536 - x) / 100.0;
00380 }
00381
00382 if (rc != numberOfWords) {
00383 ROS_ERROR("readSetpoint() Failed.");
00384 return false;
00385 }
00386
00387 return true;
00388
00389 }
00390
00391 bool radiometryNode::readTemperature(double& actualTemp) {
00392
00393 ROS_INFO("Attempting to read current params...");
00394
00395 int rc;
00396
00397 uint16_t numberOfWords = 0x1;
00398
00399 uint16_t *dest;
00400 dest = new uint16_t[256];
00401
00402
00403 rc = modbus_read_registers(ctx, ISOTECH_TEMP_ADDR, numberOfWords, dest);
00404
00405
00406 uint16_t x;
00407 std::stringstream ss;
00408
00409 ss << std::hex << dest[0];
00410 ss >> x;
00411
00412 actualTemp = float(x) / 100.0;
00413
00414 if (actualTemp > MAXIMUM_BLACKBODY_TEMPERATURE) {
00415 actualTemp = -float(65536 - x) / 100.0;
00416 }
00417
00418 if (rc != numberOfWords) {
00419 ROS_ERROR("readTemperature() Failed.");
00420 return false;
00421 }
00422
00423 return true;
00424
00425 }
00426
00427 bool radiometryNode::setupBlackbodyComms() {
00428 ROS_INFO("Establishing serial connection with blackbody...");
00429
00430 ctx = modbus_new_rtu(configData.portAddress.c_str(), configData.baudRate, 'N', 8, 1);
00431
00432 if (ctx == NULL) {
00433 ROS_ERROR("Unable to allocate libmodbus context (%s)", configData.portAddress.c_str());
00434 return false;
00435 }
00436
00437 modbus_set_debug(ctx, TRUE);
00438
00439 modbus_error_recovery_mode erm = (modbus_error_recovery_mode) (MODBUS_ERROR_RECOVERY_LINK | MODBUS_ERROR_RECOVERY_PROTOCOL);
00440
00441 modbus_set_error_recovery(ctx, erm);
00442
00443 modbus_set_slave(ctx, 1);
00444
00445 if (modbus_connect(ctx) == -1) {
00446 ROS_ERROR("Connection failed: %s", modbus_strerror(errno));
00447 modbus_free(ctx);
00448 return false;
00449 }
00450
00451
00452 nb_points = (UT_BITS_NB > UT_INPUT_BITS_NB) ? UT_BITS_NB : UT_INPUT_BITS_NB;
00453
00454 tab_rp_bits = (uint8_t *) malloc(nb_points * sizeof(uint8_t));
00455 memset(tab_rp_bits, 0, nb_points * sizeof(uint8_t));
00456
00457
00458
00459 nb_points = (UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB) ? UT_REGISTERS_NB : UT_INPUT_REGISTERS_NB;
00460 tab_rp_registers = (uint16_t *) malloc(nb_points * sizeof(uint16_t));
00461 memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t));
00462
00463 return true;
00464
00465 }
00466
00467 bool radiometryNode::setupBlackbody() {
00468
00469 if (configData.verboseMode) { ROS_INFO("Setting up blackbody..."); }
00470
00471
00472
00473
00474
00475 changeSetPoint(configData.targetTemperature);
00476
00477
00478
00479 ros::Duration(0.5).sleep();
00480
00481
00482 double actualTemp, setPoint;
00483 readTemperature(actualTemp);
00484 readSetpoint(setPoint);
00485
00486 ROS_INFO("Current Actual Temp = (%f)", actualTemp);
00487 ROS_INFO("Current Set Point = (%f)", setPoint);
00488
00489 if (setPoint != configData.targetTemperature) {
00490 ROS_ERROR("Set Point (%6.2f) is not equal to specified target temperature (%6.2f)! (consider restarting)", setPoint, configData.targetTemperature);
00491 return false;
00492 }
00493
00494 if (configData.verboseMode) { ROS_INFO("Blackbody setup."); }
00495
00496 blackbodyIsSetup = true;
00497
00498 return true;
00499
00500 }
00501
00502 void radiometryNode::handle_camera(const sensor_msgs::ImageConstPtr& msg_ptr, const sensor_msgs::CameraInfoConstPtr& info_msg) {
00503
00504
00505
00506
00507
00508 cv_ptr = cv_bridge::toCvCopy(msg_ptr, "16UC1");
00509
00510 frame = cv::Mat(cv_ptr->image);
00511
00512 float thermistorVal;
00513
00514 memcpy(&thermistorVal, &info_msg->binning_x, 4);
00515
00516 char timeString[256];
00517
00518 sprintf(timeString,"%010d.%06d ",info_msg->header.stamp.sec, info_msg->header.stamp.nsec/1000);
00519
00520 ofs_thermistor_log << timeString << thermistorVal << endl;
00521
00522 if (matEquality) {
00523
00524 matEquality = matricesAreEqual(frame, lastFrame);
00525
00526 if (!matEquality) {
00527
00528 if (temperatureIsStable) {
00529
00530
00531 previousTime = ros::Time::now();
00532
00533 if (elapsedTime < 0.0) {
00534 elapsedTime = 0.0;
00535 } else {
00536 elapsedTime = (ros::Time::now().toNSec() - previousTime.toNSec()) / 1000000000.0;
00537 }
00538
00539 ROS_WARN("Fresh frame! [%d, %f, %f] (%fs elapsed since previous)", imageIndex, currentTemp, thermistorVal, elapsedTime);
00540
00541
00542
00543 ofs_image_log << timeString << " " << imageIndex << " " << thermistorVal << " " << currentTemp << endl;
00544
00545 char outputFilename[256];
00546 sprintf(outputFilename, "%s/%s/frame%06d.%s", configData.outputFolder.c_str(), subdirectoryName, imageIndex, "png");
00547
00548 cv::imwrite(outputFilename,cv_ptr->image);
00549
00550 imageIndex++;
00551
00552
00553 }
00554
00555 }
00556 } else {
00557 matEquality = matricesAreEqual(frame, lastFrame);
00558 }
00559
00560
00561
00562 frame.copyTo(lastFrame);
00563
00564 if (elapsedTime > configData.completionTimeout) {
00565 ROS_ERROR("Time elapsed since last fresh frame has exceeded specified limit.");
00566 nodeStillValid = false;
00567 }
00568
00569 }
00570
00571
00572 radiometryData::radiometryData() {
00573
00574 verboseMode = false;
00575 communicateWithBlackbody = true;
00576
00577
00578 }
00579
00580 bool radiometryData::obtainStartingData(ros::NodeHandle& nh) {
00581
00582 nh.param<bool>("communicateWithBlackbody", communicateWithBlackbody, true);
00583
00584
00585
00586 nh.param<string>("thermal_topic", thermal_topic, "/thermalvis/streamer/image_raw");
00587
00588 nh.param<string>("outputFolder", outputFolder, "outputFolder");
00589
00590 nh.param<string>("portAddress", portAddress, DEFAULT_PORT_ADDRESS);
00591 nh.param<int>("baudRate", baudRate, DEFAULT_BAUD_RATE);
00592
00593 if (outputFolder == "outputFolder") {
00594 ROS_ERROR("No output folder specified!");
00595 return false;
00596 }
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606 return true;
00607 }
00608
00609 void radiometryNode::serverCallback(thermalvis::radiometryConfig &config, uint32_t level) {
00610
00611 if (configData.verboseMode){ ROS_INFO("Processing reconfigure request..."); }
00612
00613 configData.verboseMode = config.verboseMode;
00614
00615 if (blackbodyIsSetup) {
00616 if (configData.targetTemperature != config.targetTemperature) {
00617 ROS_WARN("Cannot change target temperature now - directories have already been setup. Consider restarting.");
00618 }
00619 } else {
00620 configData.targetTemperature = config.targetTemperature;
00621 }
00622
00623
00624 ROS_INFO("targetTemperature = (%f)", configData.targetTemperature);
00625
00626 if (configData.serialPollingRate != config.serialPollingRate) {
00627 configData.serialPollingRate = config.serialPollingRate;
00628 ROS_INFO("Updating period with rate (%f)", configData.serialPollingRate);
00629
00630 if (configData.serialPollingRate > 0.1) {
00631 serial_timer.setPeriod(ros::Duration(1.0 / configData.serialPollingRate));
00632 } else {
00633 serial_timer.setPeriod(ros::Duration(1.0 / 1.0));
00634 }
00635 }
00636
00637 configData.maxStableVariation = config.maxStableVariation;
00638 configData.completionTimeout = config.completionTimeout;
00639 configData.minStableTime = config.minStableTime;
00640
00641 if (configData.verboseMode) { ROS_INFO("Reconfigure request complete..."); }
00642 }
00643