TOBI SignalServer
0.1
|
00001 /* 00002 This file is part of the TOBI SignalServer. 00003 00004 Commercial Usage 00005 Licensees holding valid Graz University of Technology Commercial 00006 licenses may use this file in accordance with the Graz University 00007 of Technology Commercial License Agreement provided with the 00008 Software or, alternatively, in accordance with the terms contained in 00009 a written agreement between you and Graz University of Technology. 00010 00011 -------------------------------------------------- 00012 00013 GNU General Public License Usage 00014 Alternatively, this file may be used under the terms of the GNU 00015 General Public License version 3.0 as published by the Free Software 00016 Foundation and appearing in the file gpl.txt included in the 00017 packaging of this file. Please review the following information to 00018 ensure the GNU General Public License version 3.0 requirements will be 00019 met: http://www.gnu.org/copyleft/gpl.html. 00020 00021 In case of GNU General Public License Usage ,the TOBI SignalServer 00022 is distributed in the hope that it will be useful, 00023 but WITHOUT ANY WARRANTY; without even the implied warranty of 00024 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00025 GNU General Public License for more details. 00026 00027 You should have received a copy of the GNU General Public License 00028 along with the TOBI SignalServer. If not, see <http://www.gnu.org/licenses/>. 00029 00030 Copyright 2010 Graz University of Technology 00031 Contact: SignalServer@tobi-project.org 00032 */ 00033 00038 #include "hardware/g_mobilab.h" 00039 #include "hardware/hw_thread_builder.h" 00040 00041 #include <vector> 00042 #include <boost/bind.hpp> 00043 #include <boost/lexical_cast.hpp> 00044 00045 namespace tobiss 00046 { 00047 00048 using std::vector; 00049 using std::string; 00050 using std::map; 00051 00052 using std::cout; 00053 using std::cerr; 00054 using std::endl; 00055 using std::flush; 00056 00057 using boost::uint16_t; 00058 using boost::uint32_t; 00059 00060 static const unsigned int MOBILAB_MAX_NR_OF_CHANNELS = 8; 00061 static const double MOBILAB_DAQ_RESOLUTION_BIT = 16; 00062 static const unsigned int MOBILAB_DAQ_RESOLUTION_BYTE = 2; 00063 static const unsigned int MOBILAB_SAMPLING_RATE = 256; // Hz 00064 00065 static const float MOBILAB_EEG_SENSITIVITY = 500; //micro volts 00066 static const float MOBILAB_EOG_SENSITIVITY = 2000; //micro volts 00067 static const float MOBILAB_EMG_SENSITIVITY = 5000; //micro volts 00068 static const float MOBILAB_ANALOG_SENSITIVITY = 250000; //micro volts 00069 00070 static const double MOBILAB_COMMON_LOWPASS = 100; // Hz 00071 static const double MOBILAB_EEG_HIGHPASS = 0.5; // Hz 00072 static const double MOBILAB_EOG_HIGHPASS = 0.01; // Hz 00073 static const double MOBILAB_EMG_HIGHPASS = 0.5; // Hz 00074 00075 static const unsigned int MOBILAB_BAUD_RATE = 57600; 00076 static const unsigned int MOBILAB_CHAR_SIZE = 8; 00077 static const std::string MOBILAB_FLOW_CONTROL = "none"; 00078 static const std::string MOBILAB_STOP_BITS = "1"; 00079 static const std::string MOBILAB_PARITY = "none"; 00080 00081 00082 const string GMobilab::hw_mobilab_serial_port_("serial_port"); 00083 const string GMobilab::hw_mobilab_type_("mobilab_type"); 00084 const string GMobilab::hw_mobilab_eeg_("eeg"); 00085 const string GMobilab::hw_mobilab_multi_("multi"); 00086 00087 //----------------------------------------------------------------------------- 00088 00089 #ifndef WIN32 00090 00091 #include <termios.h> 00092 00093 // very very bad hack ... taken from ttylog 00094 // don't know if something similar has to be done for windows 00095 // if this code is not executed, the mobilab does not respond 00096 // don't know why 00097 // only starting ttylog before the sigserver was enough, otherwise 00098 // the mobilab did not respond 00099 // maybe some pins have to be set manually 00100 void DO_BAD_SERIAL_PORT_HACK(std::string dev) 00101 { 00102 unsigned int count = 2; 00103 while(count) 00104 { 00105 FILE *logfile; 00106 int fd; 00107 fd_set rfds; 00108 struct termios oldtio, newtio; 00109 00110 logfile = fopen (dev.c_str(), "rb"); 00111 00112 fd = fileno (logfile); 00113 00114 tcgetattr (fd, &oldtio); /* save current serial port settings */ 00115 bzero (&newtio, sizeof (newtio)); /* clear struct for new port settings */ 00116 00117 newtio.c_cflag = B57600 | CRTSCTS | CS8 | CLOCAL | CREAD; 00118 newtio.c_iflag = IGNPAR | IGNCR; 00119 newtio.c_oflag = 0; 00120 newtio.c_lflag = ICANON; 00121 00122 tcflush (fd, TCIFLUSH); 00123 tcsetattr (fd, TCSANOW, &newtio); 00124 00125 /* Clear the device */ 00126 FD_ZERO (&rfds); 00127 FD_SET (fd, &rfds); 00128 00129 fclose (logfile); 00130 tcsetattr (fd, TCSANOW, &oldtio); 00131 00132 count--; 00133 usleep(20000); 00134 } 00135 } 00136 00137 #endif 00138 00139 const HWThreadBuilderTemplateRegistrator<GMobilab> GMobilab::factory_registrator_ ("mobilab", "mobilab+", "g.mobilab", "g.mobilab+"); 00140 00141 //----------------------------------------------------------------------------- 00142 GMobilab::GMobilab(boost::asio::io_service& io, ticpp::Iterator<ticpp::Element> hw) 00143 : SerialPortBase(io), HWThread(), mobilab_type_(MOBILAB_EEG), async_acqu_thread_(0) 00144 { 00145 setType("g.Mobilab"); 00146 setHardware(hw); 00147 00148 checkNrOfChannels(); 00149 setScalingValues(); 00150 00151 #ifndef WIN32 00152 DO_BAD_SERIAL_PORT_HACK(getSerialPortName()); 00153 #endif 00154 00155 open(); 00156 setBaudRate(MOBILAB_BAUD_RATE); 00157 setFlowControl(MOBILAB_FLOW_CONTROL); 00158 setParity(MOBILAB_PARITY); 00159 setStopBits(MOBILAB_STOP_BITS); 00160 setCharacterSize(MOBILAB_CHAR_SIZE); 00161 00162 raw_data_.resize(nr_ch_, 0); 00163 00164 samples_.reserve(4 * nr_ch_); 00165 samples_.resize(nr_ch_, 0); 00166 00167 if(blocks_ != 1) 00168 throw(std::invalid_argument("Blocksize > 1 not supported yet!") ); 00169 00170 std::vector<unsigned char> command; 00171 std::vector<unsigned char> reply(1,0); 00172 00173 unsigned char channel_code = getChannelCode(); 00174 00175 // command to set channels ... sniffed from serial communication 00176 command.push_back(0x63); 00177 command.push_back(channel_code); 00178 command.push_back(0x20); 00179 00180 sync_write(command); 00181 sync_read(reply); 00182 00183 // response from mobilab if channel setting was ok -- don't know other messages 00184 if(reply[0] != 0x63) 00185 throw(std::runtime_error("GMobilab::Constructor -- Wrong hardware response from mobilab!") ); 00186 00187 data_.init(blocks_ , nr_ch_ , channel_types_); 00188 00189 } 00190 00191 //----------------------------------------------------------------------------- 00192 00193 void GMobilab::run() 00194 { 00195 00196 // command to start data transmission 00197 std::vector<unsigned char> command(1, 0x61); 00198 sync_write(command); 00199 00200 if(mode_ == SLAVE) 00201 async_acqu_thread_ = new boost::thread( boost::bind(&GMobilab::acquireData, this) ); 00202 00203 running_ = true; 00204 } 00205 00206 //----------------------------------------------------------------------------- 00207 00208 GMobilab::~GMobilab() 00209 { 00210 async_acqu_thread_->join(); 00211 close(); 00212 if(async_acqu_thread_) 00213 delete async_acqu_thread_; 00214 } 00215 00216 //----------------------------------------------------------------------------- 00217 00218 void GMobilab::stop() 00219 { 00220 running_ = false; 00221 boost::unique_lock<boost::shared_mutex> lock(rw_); 00222 00223 // command to stop data transmission 00224 std::vector<unsigned char> command(1,0x62); 00225 sync_write(command); 00226 00227 } 00228 00229 //----------------------------------------------------------------------------- 00230 00231 SampleBlock<double> GMobilab::getSyncData() 00232 { 00233 if(running_) 00234 { 00235 boost::unique_lock<boost::shared_mutex> lock(rw_); 00236 00237 sync_read(raw_data_); 00238 00239 for(unsigned int n = 0; n < raw_data_.size(); n++) 00240 samples_[n] = raw_data_[n]* scaling_factors_[n]; 00241 00242 data_.setSamples(samples_); 00243 } 00244 return(data_); 00245 } 00246 00247 //----------------------------------------------------------------------------- 00248 00249 SampleBlock<double> GMobilab::getAsyncData() 00250 { 00251 if(running_) 00252 { 00253 boost::unique_lock<boost::shared_mutex> lock(rw_); 00254 00255 data_.setSamples(samples_); 00256 00257 } 00258 00259 return(data_); 00260 } 00261 00262 //----------------------------------------------------------------------------- 00263 00264 void GMobilab::setHardware(ticpp::Iterator<ticpp::Element>const &hw) 00265 { 00266 #ifdef DEBUG 00267 std::cout << "GMobilab: setHardware" << std::endl; 00268 #endif 00269 00270 checkMandatoryHardwareTags(hw); 00271 ticpp::Iterator<ticpp::Element> ds(hw->FirstChildElement(hw_devset_, true)); 00272 00273 setDeviceSettings(ds); 00274 00275 ticpp::Iterator<ticpp::Element> cs(hw->FirstChildElement(hw_chset_, false)); 00276 if (cs != cs.end()) 00277 { 00278 for(ticpp::Iterator<ticpp::Element> it(cs); ++it != it.end(); ) 00279 if(it->Value() == hw_chset_) 00280 { 00281 string ex_str(type_ + " -- "); 00282 ex_str += "Multiple channel_settings found!"; 00283 throw(std::invalid_argument(ex_str)); 00284 } 00285 setChannelSettings(cs); 00286 } 00287 } 00288 00289 //----------------------------------------------------------------------------- 00290 00291 void GMobilab::setDeviceSettings(ticpp::Iterator<ticpp::Element>const &father) 00292 { 00293 #ifdef DEBUG 00294 std::cout << "GMobilab: setDeviceSettings" << std::endl; 00295 #endif 00296 00297 fs_ = MOBILAB_SAMPLING_RATE; 00298 00299 ticpp::Iterator<ticpp::Element>elem = father->FirstChildElement(hw_mobilab_serial_port_,true); 00300 setPortName(elem->GetText(true)); 00301 00302 elem = father->FirstChildElement(hw_mobilab_type_,true); 00303 if(elem->GetText(true) == hw_mobilab_eeg_) 00304 mobilab_type_ = MOBILAB_EEG; 00305 else if(elem->GetText(true) == hw_mobilab_multi_) 00306 mobilab_type_ = MOBILAB_MULTI; 00307 else 00308 throw(std::invalid_argument( 00309 "GMobilab::setDeviceSettings() -- type not supported or wrong:") ); 00310 00311 elem = father->FirstChildElement(hw_channels_,false); 00312 if(elem != elem.end()) 00313 setDeviceChannels(elem); 00314 00315 elem = father->FirstChildElement(hw_blocksize_,false); 00316 if(elem != elem.end()) 00317 setBlocks(elem); 00318 } 00319 00320 //--------------------------------------------------------------------------------------- 00321 00322 void GMobilab::setChannelSettings(ticpp::Iterator<ticpp::Element>const &father) 00323 { 00324 #ifdef DEBUG 00325 std::cout << "GMobilab: setChannelSettings" << std::endl; 00326 #endif 00327 00328 ticpp::Iterator<ticpp::Element> elem(father->FirstChildElement(hw_chset_sel_,false)); 00329 if (elem != elem.end()) 00330 setChannelSelection(elem); 00331 } 00332 00333 //--------------------------------------------------------------------------------------- 00334 00335 void GMobilab::setScalingValues() 00336 { 00337 // use only 50% of the sensitivity to get a correct scaling 00338 00339 if(mobilab_type_ == MOBILAB_EEG) 00340 { 00341 std::map<boost::uint16_t, std::pair<std::string, boost::uint32_t> >::iterator it; 00342 for(it = channel_info_.begin(); it != channel_info_.end(); it++) 00343 { 00344 scaling_factors_.push_back( 00345 MOBILAB_EEG_SENSITIVITY/(2.0 * pow(2.0 ,MOBILAB_DAQ_RESOLUTION_BIT) ) ); 00346 } 00347 } 00348 if(mobilab_type_ == MOBILAB_MULTI) 00349 { 00350 double scaling = 0; 00351 double sensitivity = 0; 00352 boost::uint16_t channel = 0; 00353 00354 std::map<boost::uint16_t, std::pair<std::string, boost::uint32_t> >::iterator it; 00355 for(it = channel_info_.begin(); it != channel_info_.end(); it++) 00356 { 00357 channel = (*it).first; 00358 00359 std::cout << " GMobilab::setScalingValues() -- channel numbers: "; 00360 std::cout << channel << std::flush << std::endl; 00361 00362 if(channel == 1 || channel == 2) 00363 sensitivity = MOBILAB_EEG_SENSITIVITY; 00364 if(channel == 3 || channel == 4) 00365 sensitivity = MOBILAB_EOG_SENSITIVITY; 00366 if(channel == 5 || channel == 6) 00367 sensitivity = MOBILAB_EMG_SENSITIVITY; 00368 if(channel == 7 || channel == 8) 00369 sensitivity = MOBILAB_ANALOG_SENSITIVITY; 00370 00371 scaling = sensitivity/pow(2.0 ,MOBILAB_DAQ_RESOLUTION_BIT); 00372 scaling_factors_.push_back(scaling/2.0); 00373 00374 } 00375 } 00376 } 00377 00378 //--------------------------------------------------------------------------------------- 00379 00380 unsigned char GMobilab::getChannelCode() 00381 { 00382 // binary channel coding for the mobilab 00383 channel_coding_[1] = 0x80; 00384 channel_coding_[2] = 0x40; 00385 channel_coding_[3] = 0x20; 00386 channel_coding_[4] = 0x10; 00387 channel_coding_[5] = 0x08; 00388 channel_coding_[6] = 0x04; 00389 channel_coding_[7] = 0x02; 00390 channel_coding_[8] = 0x01; 00391 00392 unsigned char code = 0; 00393 std::map<boost::uint16_t, std::pair<std::string, boost::uint32_t> >::iterator it; 00394 for(it = channel_info_.begin(); it != channel_info_.end(); it++) 00395 code |= channel_coding_[ (*it).first ]; 00396 00397 return(code); 00398 } 00399 00400 //--------------------------------------------------------------------------------------- 00401 00402 void GMobilab::checkNrOfChannels() 00403 { 00404 #ifdef DEBUG 00405 cout << "USBamp: checkNrOfChannels" << endl; 00406 #endif 00407 00408 if(nr_ch_ > MOBILAB_MAX_NR_OF_CHANNELS ) 00409 throw(std::invalid_argument("Too many channels defined -- maximum nr of channels: "\ 00410 +boost::lexical_cast<string>(MOBILAB_MAX_NR_OF_CHANNELS)) ); 00411 00412 map<uint16_t, std::pair<string, uint32_t> >::iterator it(channel_info_.begin()); 00413 00414 for( ; it != channel_info_.end(); it++ ) 00415 if(it->first > MOBILAB_MAX_NR_OF_CHANNELS ) 00416 throw(std::invalid_argument("Channel number too high -- maximum nr of channels: "\ 00417 +boost::lexical_cast<string>(MOBILAB_MAX_NR_OF_CHANNELS)) ); 00418 } 00419 00420 //--------------------------------------------------------------------------------------- 00421 00422 void GMobilab::acquireData() 00423 { 00424 while(running_) 00425 { 00426 sync_read(raw_data_); 00427 00428 boost::unique_lock<boost::shared_mutex> lock(rw_); 00429 for(unsigned int n = 0; n < raw_data_.size(); n++) 00430 samples_[n] = raw_data_[n]* scaling_factors_[n]; 00431 lock.unlock(); 00432 } 00433 } 00434 00435 //--------------------------------------------------------------------------------------- 00436 00437 } // Namespace tobiss 00438