00001 /*!@file Devices/RadioDecoder.C Decode radio pulse-width-modulated signals */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the 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 iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Devices/RadioDecoder.C $ 00035 // $Id: RadioDecoder.C 14697 2011-04-08 21:34:48Z farhan $ 00036 // 00037 00038 #include "Devices/RadioDecoder.H" 00039 00040 #include "Component/ParamMap.H" 00041 #include "Devices/AudioGrabber.H" 00042 #include "Devices/AudioMixer.H" 00043 #include "Devices/DeviceOpts.H" 00044 #include "Util/Assert.H" 00045 #include "Util/Types.H" 00046 #include "Util/log.H" 00047 00048 #include <cstdio> 00049 #include <cmath> 00050 00051 // half window size, in samples: 00052 #define RD_WIN 2 00053 00054 // values higher than this are considered one: 00055 #define RD_THRESH (255 - 20) 00056 00057 // min and max radio pulse width, in samples: 00058 #define MINRDPULSE 30 00059 #define MAXRDPULSE 150 00060 00061 void* RadioDecoder_run(void *r0); // will live in a separate thread 00062 00063 // ###################################################################### 00064 void* RadioDecoder_run(void *r0) 00065 { 00066 RadioDecoder *r = (RadioDecoder *)r0; 00067 r->run(); return NULL; 00068 } 00069 00070 // ###################################################################### 00071 RadioDecoder::RadioDecoder(OptionManager& mgr, 00072 const std::string& descrName, 00073 const std::string& tagName) : 00074 ModelComponent(mgr, descrName, tagName) 00075 { 00076 #ifndef HAVE_SYS_SOUNDCARD_H 00077 CLFATAL("Oops! I can't run without <sys/soundcard.h>"); 00078 #else 00079 running = false; 00080 00081 agb = nub::soft_ref<AudioGrabber> 00082 (new AudioGrabber(mgr, "RadioDecoder Grabber", "RadioDecoderGrabber")); 00083 00084 // Feed our own default values to the grabber, to replace its defaults 00085 agb->setModelParamVal("AudioGrabberFreq", uint(48000)); 00086 agb->setModelParamVal("AudioGrabberChans", 1); 00087 agb->setModelParamVal("AudioGrabberBufSamples", uint(1000)); 00088 00089 // Create and attach our mixer and grabber as subcomponents: 00090 nub::soft_ref<AudioMixer> 00091 mix(new AudioMixer(mgr, "RadioDecoder Mixer", "RadioDecoderMixer")); 00092 addSubComponent(mix); 00093 addSubComponent(agb); 00094 00095 // By default, all of the command-line options of our newly-minted 00096 // AudioGrabber and RadioDecoder will be exported. However, in this 00097 // case we don't want that to happen, as we need the grabber and 00098 // mixer to be in a specific state for us to work, and we don't want 00099 // people messing with them; so, we explicit forget those guys as 00100 // command-line options, and then we will a hand-pick a few options 00101 // (typically, device names and recording inputs, but not recording 00102 // frequency and others) to export that can be safely twiddled by 00103 // the user. 00104 agb->forgetExports(); 00105 mix->forgetExports(); 00106 00107 agb->doRequestOption(&OPT_AudioGrabberDevice); 00108 agb->doRequestOption(&OPT_AudioGrabberChans, true); 00109 mix->doRequestOption(&OPT_AudioMixerDevice); 00110 mix->doRequestOption(&OPT_AudioMixerLineIn); 00111 mix->doRequestOption(&OPT_AudioMixerCdIn); 00112 mix->doRequestOption(&OPT_AudioMixerMicIn); 00113 #endif // HAVE_SYS_SOUNDCARD_H 00114 } 00115 00116 // ###################################################################### 00117 void RadioDecoder::start1() 00118 { 00119 #ifndef HAVE_SYS_SOUNDCARD_H 00120 CLFATAL("Oops! I can't run without <sys/soundcard.h>"); 00121 #else 00122 const bool stereo = agb->getModelParamVal<uint>("AudioGrabberChans"); 00123 nch = stereo ? 2 : 1; 00124 00125 // setup our internals: 00126 zero = new rutz::shared_ptr<NModelParam<float> >[nch]; 00127 posmult = new rutz::shared_ptr<NModelParam<float> >[nch]; 00128 negmult = new rutz::shared_ptr<NModelParam<float> >[nch]; 00129 servoval = new float[nch]; 00130 for (int i = 0; i < nch; i ++) 00131 { 00132 servoval[i] = 0.0F; char buf[20]; 00133 00134 sprintf(buf, "Zero%d", i); 00135 zero[i] = NModelParam<float>::make(buf, this, 0.0F); 00136 00137 sprintf(buf, "PosMult%d", i); 00138 posmult[i] = NModelParam<float>::make(buf, this, 1.0F); 00139 00140 sprintf(buf, "NegMult%d", i); 00141 negmult[i] = NModelParam<float>::make(buf, this, 1.0F); 00142 } 00143 #endif // HAVE_SYS_SOUNDCARD_H 00144 } 00145 00146 // ###################################################################### 00147 void RadioDecoder::start2() 00148 { 00149 // start thread for run(): 00150 pthread_create(&runner, NULL, &RadioDecoder_run, (void *)this); 00151 } 00152 00153 // ###################################################################### 00154 void RadioDecoder::stop1() 00155 { 00156 // stop our thread: 00157 running = false; while(running == false) usleep(5); 00158 usleep(50); running = false; 00159 00160 delete [] servoval; servoval = 0; 00161 delete [] zero; zero = 0; 00162 delete [] posmult; posmult = 0; 00163 delete [] negmult; negmult = 0; 00164 } 00165 00166 // ###################################################################### 00167 RadioDecoder::~RadioDecoder() 00168 { } 00169 00170 // ###################################################################### 00171 float RadioDecoder::getVal(const int channel) const 00172 { 00173 ASSERT(channel >= 0 && channel < nch); 00174 float val; 00175 if (servoval[channel] >= zero[channel]->getVal()) 00176 { 00177 val = (servoval[channel] - zero[channel]->getVal()) * 00178 posmult[channel]->getVal(); 00179 if (val >= 1.0F) val = 0.999999F; 00180 } 00181 else 00182 { 00183 val = (servoval[channel] - zero[channel]->getVal()) * 00184 negmult[channel]->getVal(); 00185 if (val <= -1.0F) val = -0.999999F; 00186 } 00187 return val; 00188 } 00189 00190 // ###################################################################### 00191 void RadioDecoder::zeroCalibrate(const int nbiter) 00192 { 00193 LINFO("Starting zero calibration. Put controls to rest."); 00194 float avg[nch]; for (int i = 0; i < nch; i ++) avg[i] = 0.0F; 00195 00196 // somehow it takes some time for the signal to be good; maybe some 00197 // automatic gain control in the sound card? 00198 usleep(2000000); 00199 00200 for (int i = 0; i < nbiter; i ++) 00201 { 00202 for (int j = 0; j < nch; j ++) avg[j] += servoval[j]; 00203 usleep(30000); 00204 } 00205 for (int i = 0; i < nch; i ++) zero[i]->setVal(avg[i] / ((float)nbiter)); 00206 LINFO("Zero calibration done."); 00207 } 00208 00209 // ###################################################################### 00210 void RadioDecoder::rangeCalibrate(const int nbiter) 00211 { 00212 LINFO("Range calibration: Full-swing all controls now!"); 00213 float pmax[nch], nmax[nch]; 00214 for (int i = 0; i < nch; i ++) { pmax[i] = -10000.0; nmax[i] = 10000.0; } 00215 00216 for (int i = 0; i < nbiter; i ++) 00217 { 00218 for (int j = 0; j < nch; j ++) 00219 { 00220 float x = servoval[j]; // avoid value change while we work on it 00221 if (x > pmax[j]) pmax[j] = x; 00222 if (x < nmax[j]) nmax[j] = x; 00223 } 00224 usleep(20000); 00225 } 00226 for (int i = 0; i < nch; i ++) 00227 { 00228 if (fabs(pmax[i] - zero[i]->getVal()) < 0.001F) 00229 { 00230 LERROR("ZERO positive range? Setting multiplier to 1"); 00231 posmult[i]->setVal(1.0F); 00232 } 00233 else 00234 posmult[i]->setVal(0.99999F / (pmax[i] - zero[i]->getVal())); 00235 00236 if (fabs(zero[i]->getVal() - nmax[i]) < 0.001F) 00237 { 00238 LERROR("ZERO negative range? Setting multiplier to 1"); 00239 negmult[i]->setVal(1.0F); 00240 } 00241 else 00242 negmult[i]->setVal(-0.99999F / (nmax[i] - zero[i]->getVal())); 00243 } 00244 LINFO("Range calibration done."); 00245 } 00246 00247 // ###################################################################### 00248 void RadioDecoder::run() 00249 { 00250 #ifndef HAVE_SYS_SOUNDCARD_H 00251 CLFATAL("Oops! I can't run without <sys/soundcard.h>"); 00252 #else 00253 running = true; 00254 int scoreStart[nch], idxUp[nch], scoreEnd[nch], idxDown[nch], 00255 first[nch], middle[nch], last[nch], bestUp[nch], bestDown[nch]; 00256 00257 while(running) 00258 { 00259 // grab some data (blocking): 00260 AudioBuffer<byte> buffer; 00261 agb->grab(buffer); 00262 int nsamples = int(buffer.nsamples()); 00263 00264 // slide a window and count how many positive and negative samples 00265 // are found around its center: 00266 for (int i = 0; i < nch; i ++) { 00267 scoreStart[i] = 0; scoreEnd[i] = 0; idxUp[i] = -1; idxDown[i] = -1; 00268 bestUp[i] = 0; bestDown[i] = 0; 00269 } 00270 const byte *bptr = buffer.getDataPtr(); 00271 00272 // initialize scores for initial window: 00273 for (int i = 0; i < RD_WIN; i ++) 00274 for (int j = 0; j < nch; j ++) 00275 { 00276 if (*bptr > RD_THRESH) scoreStart[j] ++; 00277 if (bptr[RD_WIN] > RD_THRESH) scoreEnd[j] ++; 00278 bptr ++; 00279 } 00280 00281 int mid = RD_WIN * nch, end = (2 * RD_WIN - 1) * nch; 00282 bptr = buffer.getDataPtr(); 00283 for (int i = 0; i < nch; i ++) 00284 { 00285 if (*bptr > RD_THRESH) first[i] = 1; else first[i] = 0; 00286 if (bptr[mid] > RD_THRESH) middle[i] = 1; else middle[i] = 0; 00287 if (bptr[end] > RD_THRESH) last[i] = 1; else last[i] = 0; 00288 bptr ++; 00289 } 00290 00291 // now slide the window and update the scores, subtracting the first 00292 // value, sliding the middle value, and adding the last value: 00293 bptr = buffer.getDataPtr() + nch; 00294 for (int i = RD_WIN + 1; i < nsamples - RD_WIN; i ++) 00295 for (int j = 0; j < nch; j ++) 00296 { 00297 if (idxUp[j] != -1 && scoreStart[j] - scoreEnd[j] > bestDown[j]) { 00298 bestDown[j] = scoreStart[j] - scoreEnd[j]; 00299 idxDown[j] = i; 00300 //LDEBUG("down(%d): %d %d %d %d",j,scoreStart[j],scoreEnd[j], 00301 // bestDown[j], idxDown[j]); 00302 } 00303 if (idxDown[j] == -1 && scoreEnd[j] - scoreStart[j] > bestUp[j]) { 00304 bestUp[j] = scoreEnd[j] - scoreStart[j]; 00305 idxUp[j] = i; 00306 //LDEBUG("up(%d): %d %d %d %d",j,scoreStart[j],scoreEnd[j], 00307 // bestUp[j], idxUp[j]); 00308 } 00309 00310 scoreStart[j] += middle[j] - first[j]; 00311 scoreEnd[j] += last[j] - middle[j]; 00312 00313 if (*bptr > RD_THRESH) first[j] = 1; else first[j] = 0; 00314 if (bptr[mid] > RD_THRESH) middle[j] = 1; else middle[j] = 0; 00315 if (bptr[end] > RD_THRESH) last[j] = 1; else last[j] = 0; 00316 bptr ++; 00317 } 00318 00319 // compute pulse width, in samples: 00320 for (int i = 0; i < nch; i ++) 00321 { 00322 //LDEBUG("pulse(%d): %d--%d",idxUp[i],idxDown[i]); 00323 if (idxDown[i] != -1 && idxUp[i] != -1 && idxUp[i] < idxDown[i] && 00324 idxDown[i] - idxUp[i] > MINRDPULSE && 00325 idxDown[i] - idxUp[i] < MAXRDPULSE) 00326 servoval[i] = (float)(idxDown[i] - idxUp[i] + 1); 00327 } 00328 // sleep a little: 00329 usleep(5000); 00330 } 00331 00332 // we got an order to stop: 00333 running = true; // FIXME: bogus! other thread may unlock too soon 00334 pthread_exit(0); 00335 #endif // HAVE_SYS_SOUNDCARD_H 00336 } 00337 00338 // ###################################################################### 00339 /* So things look consistent in everyone's emacs... */ 00340 /* Local Variables: */ 00341 /* indent-tabs-mode: nil */ 00342 /* End: */