radiometry.cpp
Go to the documentation of this file.
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         // Then is this the main part?
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         // Should basically just contain a check to make sure there hasn't been too large a delay since the last frame...
00060         // (except for the very first)
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         // Some variables...
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                         // Then serial callback will just be used to verify that the blackbody temperature hasn't strayed too much
00137                         // Also, blackbody temp will actually be saved to file like the thermistor data
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         //ofs_serial_log.open(serialCommsFile.c_str());
00156         
00157         // Then subscribe to topics...
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         // First make parent directory if there's nothing there
00175         
00176         if (dirp != NULL) {
00177                 ROS_WARN("Directory (%s) already exists!", configData.outputFolder.c_str());
00178                 // return false;
00179         } else {
00180                 // Create folder if necessary
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                                 // ROS_WARN("system() call returned 0 when making main folder");
00189                         }
00190                 }
00191                 ROS_WARN("Directory (%s) created.", configData.outputFolder.c_str());
00192         }
00193         
00194         // Determine appropriate name for subdirectory
00195         // subdirectoryName
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         //dirp = opendir(outputFolder.c_str());
00208         
00209         // Now check to see if this specific temperature is there..
00210         
00211         dirp = opendir(configData.outputFolder.c_str());
00212         
00213         while ((entry = readdir(dirp)) != NULL) {
00214                 if (entry->d_type == DT_DIR) { // If the entry is a regular file
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         // Create folder if necessary
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                         // ROS_WARN("system() call returned 0...");
00237                 }
00238         }
00239         
00240         return true;
00241                 
00242 }
00243 
00244 void radiometryNode::serialCallback(const ros::TimerEvent&) {
00245         
00246         //ROS_INFO("In callback...");
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                         //ROS_INFO("Elapsed time: (%f)", elapsedTime);
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                                 // Deactivate elapsedTime until the video streaming is ready to utilize it..
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                 // If in here, should actually be performing BB calibration (well, once data is streaming...)
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         /* Free the memory */
00312     free(tab_rp_bits);
00313     free(tab_rp_registers);
00314 
00315     /* Close the connection */
00316     modbus_close(ctx);
00317     modbus_free(ctx);
00318         
00319         return true;
00320 }
00321 
00322 bool radiometryNode::changeSetPoint(double newVal) {
00323         
00324         // ROS_INFO("Changing set point to (%f)", newVal);
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     //sendValue[0] = 0x0960;    // This is hard-coded to perhaps 24 degrees? want it to depend on newVal...
00340         
00341         //freopen (serialCommsFile.c_str(), "w", stdout);
00342         rc = modbus_write_registers(ctx, ISOTECH_SETPOINT_ADDR, numberOfWords, sendValue);
00343         //fclose (stdout);
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         //ROS_INFO("Attempting to read current params...");
00358         
00359         int rc;
00360         
00361         uint16_t numberOfWords = 0x1;
00362         
00363         uint16_t *dest;
00364     dest = new uint16_t[256];
00365     
00366     //freopen (serialCommsFile.c_str(), "w", stdout);
00367     rc = modbus_read_registers(ctx, ISOTECH_SP_ADDR, numberOfWords, dest);
00368     //fclose (stdout);
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     //freopen (serialCommsFile.c_str(), "w", stdout);
00403     rc = modbus_read_registers(ctx, ISOTECH_TEMP_ADDR, numberOfWords, dest);
00404     //fclose (stdout);
00405     
00406     uint16_t x;   
00407         std::stringstream ss; // dest[3] is the 6th byte... so want 2 & 3...
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         /* Allocate and initialize the memory to store the bits */
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     /* Allocate and initialize the memory to store the registers */
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         // This function makes sure the blackbody converges on the correct temperature
00472         
00473         // Set temperature
00474         
00475         changeSetPoint(configData.targetTemperature);
00476         
00477         // Introduce a small pause to allow the change to take effect...
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         //ROS_INFO("Handling camera...");
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                                 // So now want to extract data...
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         //nh.param<double>("minStableTime", minStableTime, DEFAULT_MIN_STABLE_TIME);
00584         //nh.param<double>("maxStableVariation", maxStableVariation, DEFAULT_MAX_STABLE_VARIATION);
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         // nh.param<double>("targetTemperature", targetTemperature, 9e99);
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         


thermalvis
Author(s): Stephen Vidas
autogenerated on Sun Jan 5 2014 11:38:45