nose_calibration.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2010, ISR University of Coimbra.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of the ISR University of Coimbra nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Gonçalo Cabrita and Pedro Sousa on 07/03/2011
00036 *********************************************************************/
00037 
00038 #include <stdlib.h>
00039 #include <stdio.h>
00040 #include <cstdlib>
00041 #include <vector>
00042 #include <string>
00043 #include <list>
00044 #include <iostream>
00045 #include <sys/ioctl.h> 
00046 #include <fcntl.h> 
00047 #include <linux/kd.h>
00048 #include <sys/types.h>
00049 #include <unistd.h>
00050 
00051 #include <boost/thread/mutex.hpp>
00052 #include <boost/thread/thread.hpp>
00053 
00054 #include "ros/ros.h"
00055 #include "lse_sensor_msgs/Nostril.h"
00056 #include "cereal_port/CerealPort.h"
00057 #include "std_msgs/UInt16.h"
00058 
00059 class NoseCalibration
00060 {
00061         public:
00062         NoseCalibration();
00063         ~NoseCalibration();
00064         
00065         void userInterface();
00066         void mainLoop();
00067         
00068         private:
00069         
00070         // Serial Port
00071         cereal::CerealPort serial_port;
00072         
00073         ros::NodeHandle n;
00074         ros::NodeHandle pn;
00075         
00076         ros::Subscriber sub;
00077         
00078         struct Nose
00079         {
00080                 lse_sensor_msgs::Nostril nostril;
00081                 FILE * csv_file;
00082                 std::list<int> readings;
00083                 ros::Publisher pub;
00084         };
00085         std::vector<Nose> noses;
00086         
00087         double temperature;
00088         double humidity;
00089         
00090         double chemical_volume;
00091         
00092         bool fill_buffer;
00093         
00094         // Parameters
00095         std::string port;
00096         int baudrate;
00097         
00098         double volume;
00099         double density;
00100         double molar_volume;
00101         double molar_mass;
00102         
00103         int num_noses;
00104         int buffer_size;
00105         
00106         double fan_on_time;
00107         double fan_off_time;
00108         
00109         // Functions
00110         void noseCallback(const lse_sensor_msgs::Nostril::ConstPtr& msg);
00111         void arduinoCallback(std::string * data);
00112         
00113         void fanOn();
00114         void fanOff();
00115         void beep();
00116         void closeFiles();
00117 };
00118 
00119 NoseCalibration::NoseCalibration() : n(), pn("~"), serial_port()
00120 {
00121         pn.param<std::string>("port", port, "/dev/ttyUSB0");
00122         pn.param("baudrate", baudrate, 9600);
00123         
00124         pn.param("box_volume", volume, 1.0);
00125         pn.param("gas_density", density, 1.0);
00126         pn.param("gas_molar_volume", molar_volume, 1.0);
00127         pn.param("gas_molar_mass", molar_mass, 1.0);
00128         
00129         pn.param("number_of_noses", num_noses, 1);
00130         pn.param("number_of_samples", buffer_size, 10);
00131         
00132         pn.param("fan_on_time", fan_on_time, 10.0);
00133         pn.param("fan_off_time", fan_off_time, 3.0);
00134         
00135         // Serial port
00136         try{ serial_port.open((char*)port.c_str(), baudrate); }
00137         catch(cereal::Exception& e)
00138         {
00139                 ROS_FATAL("eNose Calibration -- Failed to open serial port!");
00140                 ROS_BREAK();
00141         }
00142         ROS_INFO("eNose Calibration -- Successfully connected to the arduino!");
00143         
00144         if( !serial_port.startReadBetweenStream(boost::bind(&NoseCalibration::arduinoCallback, this, _1), '@', 'e') )
00145         {
00146                 ROS_FATAL("eNose Calibration -- Failed to start streaming data!");
00147                 ROS_BREAK();
00148         }
00149         ROS_INFO("eNose Calibration -- Starting to stream data...");
00150         
00151         fill_buffer = false;
00152         sub = n.subscribe("nose", 100, &NoseCalibration::noseCallback, this);
00153         
00154         chemical_volume = 0.0;
00155         
00156         // Wait for all noses...
00157         ROS_INFO("eNose Calibration -- Waiting for all noses to come online...");
00158         ros::Rate r(1.0);
00159         while(n.ok() && noses.size()<num_noses)
00160         {
00161                 ros::spinOnce();
00162                 r.sleep();
00163         }
00164         
00165         ROS_INFO("eNose Calibration -- Got all noses!");
00166 }
00167 
00168 NoseCalibration::~NoseCalibration()
00169 {
00170         serial_port.close();
00171 }
00172 
00173 void NoseCalibration::userInterface()
00174 { 
00175         ROS_INFO("eNose Calibration - %s - User Interface", __FUNCTION__);
00176 
00177         ros::Rate r(1.0);
00178         char input[8];
00179                 
00180         while(n.ok())
00181         {
00182                 if(!fill_buffer)
00183                 {
00184                         printf("[QUERY] How much chemical did you add? ");
00185                         scanf("%s", input);
00186                         getchar();
00187                 
00188                         if(input[0]=='q' || input[0]=='Q')
00189                         {
00190                                 closeFiles();
00191                                 ROS_INFO("eNose Calibration - %s - Press CTRL+C to exit.", __FUNCTION__);
00192                                 return;
00193                         }
00194                 
00195                         chemical_volume += (double)atoi(input);
00196                 
00197                         fanOn();
00198                 
00199                         if(input[0]=='a' || input[0]=='A')
00200                         {
00201                                 ros::Duration(fan_on_time).sleep();
00202                         }
00203                         else
00204                         {
00205                                 printf("[QUERY] Press ENTER to stop the fan... ");
00206                                 fflush(stdout);
00207                                 getchar();
00208                         }
00209                 
00210                         fanOff();
00211                         ros::Duration(fan_off_time).sleep();
00212                         beep();
00213                 
00214                         fill_buffer = true;
00215                 }
00216                 r.sleep();
00217         }
00218 }
00219 
00220 void NoseCalibration::mainLoop()
00221 {
00222         ROS_INFO("eNose Calibration - %s - Main Loop", __FUNCTION__);
00223 
00224         ros::Rate r(1.0);
00225         std::vector<Nose>::iterator n_it;
00226                 
00227         while(n.ok())
00228         {
00229                 if(fill_buffer)
00230                 {
00231                         bool found_uncomplete_buffer = false;
00232                         for(n_it=noses.begin() ; n_it!=noses.end() ; n_it++)
00233                         {
00234                                 if(n_it->readings.size() < buffer_size)
00235                                 {
00236                                         found_uncomplete_buffer = true;
00237                                         break;
00238                                 }
00239                         }
00240                 
00241                         if(!found_uncomplete_buffer)
00242                         {
00243                                 fill_buffer = false;
00244                                 double ppm;
00245                         
00246                                 for(n_it=noses.begin() ; n_it!=noses.end() ; n_it++)
00247                                 {
00248                                         n_it->readings.sort();
00249                                         while(n_it->readings.size() > buffer_size/2) n_it->readings.pop_front();
00250                         
00251                                         ppm = (molar_volume/molar_mass)*(density*chemical_volume*1000/volume);
00252                                                                 
00253                                         fprintf(n_it->csv_file, "%.6lf,%d,%.2lf,%.2lf\n", ppm, n_it->readings.front(), temperature, humidity);
00254                                 
00255                                         n_it->readings.clear();
00256                                 }
00257                                 
00258                                 ROS_INFO("eNose Calibration - %s - Wrote to file. Now at %duL %.3lfppm", __FUNCTION__, (int)chemical_volume, ppm);
00259                         }
00260                 }
00261                 
00262                 for(n_it=noses.begin() ; n_it!=noses.end() ; n_it++)
00263                 {
00264                         std_msgs::UInt16 view_nose_msg;
00265                         view_nose_msg.data = n_it->nostril.raw_data;
00266                         n_it->pub.publish(view_nose_msg);
00267                 }
00268                 
00269                 ros::spinOnce();
00270                 r.sleep();
00271         }
00272 }
00273 
00274 void NoseCalibration::noseCallback(const lse_sensor_msgs::Nostril::ConstPtr& msg)
00275 {
00276         bool nostril_found = false;
00277         std::vector<Nose>::iterator n_it;
00278         for(n_it=noses.begin() ; n_it!=noses.end() ; n_it++)
00279         {
00280                 if(n_it->nostril.header.frame_id.compare(msg->header.frame_id)==0 && n_it->nostril.sensor_model.compare(msg->sensor_model)==0)
00281                 {
00282                         n_it->nostril.header.stamp = msg->header.stamp;
00283                         n_it->nostril.raw_data = msg->raw_data;
00284                         
00285                         if(fill_buffer)
00286                         {
00287                                 n_it->readings.push_front((int)msg->raw_data);
00288                                 if(n_it->readings.size() > buffer_size) n_it->readings.pop_back();
00289                         }
00290                         
00291                         nostril_found = true;
00292                         break;
00293                 }
00294         }
00295         
00296         if(!nostril_found)
00297         {
00298                 ROS_INFO("eNose Calibration - %s - Found a new nostril %s %s", __FUNCTION__, msg->header.frame_id.c_str(), msg->sensor_model.c_str());
00299         
00300                 Nose new_nose;
00301         
00302                 std::string file_name;
00303                 
00304                 file_name.append(msg->sensor_model);
00305                 //file_name.append("_");
00306                 file_name.append(msg->header.frame_id);
00307                 
00308                 // Lets take all '/' and ' ' out!
00309                 for(int i=0 ; i<file_name.size() ; i++)
00310                 {
00311                         if(file_name[i] == '/' || file_name[i] == ' ') file_name[i] = '_';
00312                 }
00313                 
00314                 ROS_INFO("eNose Calibration - %s - Publishing raw data on topic /%s...", __FUNCTION__, file_name.c_str());
00315                 
00316                 new_nose.pub = n.advertise<std_msgs::UInt16>(file_name, 10);
00317                 
00318                 ROS_INFO("eNose Calibration - %s - Opening file %s...", __FUNCTION__, file_name.c_str());
00319                 
00320                 file_name.append(".csv");
00321                 new_nose.csv_file = fopen(file_name.c_str(), "w");
00322                 new_nose.nostril = *msg;
00323                 
00324                 noses.push_back(new_nose);
00325         }
00326 }
00327 
00328 void NoseCalibration::arduinoCallback(std::string * data)
00329 {
00330         sscanf(data->c_str(), "@,%lf,%lf,e", &temperature, &humidity);
00331         
00332         //ROS_INFO("eNose Calibration - %s - %s T:%lf H:%lf", __FUNCTION__, data->c_str(), temperature, humidity);
00333 }
00334 
00335 void NoseCalibration::fanOn()
00336 {
00337         ROS_INFO("eNose Calibration - %s - Turning the fan on...", __FUNCTION__);
00338         serial_port.write("@1");
00339 }
00340 
00341 void NoseCalibration::fanOff()
00342 {
00343         serial_port.write("@0");
00344         ROS_INFO("eNose Calibration - %s - Turned the fan off.", __FUNCTION__);
00345 }
00346 
00347 void NoseCalibration::beep()
00348 {
00349         serial_port.write("@b");
00350         printf("%c", 7);
00351 }
00352 
00353 void NoseCalibration::closeFiles()
00354 {
00355         if(noses.size()>0)
00356         {
00357                 ROS_INFO("eNose Calibration - %s - Closing files...", __FUNCTION__);
00358         
00359                 std::vector<Nose>::iterator n;
00360                 for(n=noses.begin() ; n!=noses.end() ; n++)
00361                 {
00362                         fclose(n->csv_file);
00363                 }
00364         }
00365 }
00366 
00367 // Main
00368 int main(int argc, char** argv)
00369 {
00370         ros::init(argc, argv, "nose_calibration");
00371         
00372         ROS_INFO("eNose Calibration Node for ROS v0.1");
00373         
00374         NoseCalibration nc;
00375         
00376         boost::thread t(boost::bind(&NoseCalibration::userInterface, &nc));
00377         
00378         nc.mainLoop();
00379         
00380         t.join();
00381         
00382         ROS_INFO("eNose Calibration -- Goodbye!");
00383         return 0;
00384 }
00385 
00386 // EOF


ardusim
Author(s): Gonçalo Cabrita and Pedro Sousa
autogenerated on Mon Jan 6 2014 11:26:58