RadioDecoder.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:40:37 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3