00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
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
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
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
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
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
00306 file_name.append(msg->header.frame_id);
00307
00308
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
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
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