Posts with «leonardo.» label

Sound Localization.

Well, it’s elementary simple in theory, how to do sound localization based on phase difference of signals, that received by two spatially distant microphones. The devil, as always, in details. I’ve not seen any such project created for arduino, and get curious if it’s possible at all. Long story short, here I’d like to present my project, which answer this question  - YES!

Moreover, quantity  of electronics components not much differs from what I’ve used in my previous blog.  Compare two drawings, you will notice only 4 resistors and 4 electret microphones were added! All circuitry is just a few capacitors, 9 resistors, one IC and mics.  Frankly speaking, writing a remix of oscilloscope, I was testing  an arduino analog inputs, keeping in mind to use it in junction with electret microphones in other projects, like sound pressure measurements (dBA),  voice recognition or something funny in “color music” series. As they call it – “a pilot” project?.  There are some issue (simplest ever) oscilloscope has when doing fast rate sampling on 4 channels (settings 7, 8 and 9 Time/Div ) I already described, so I slightly reduce sampling down to 40 kHz here.



Note: *Hardware would be different for arduino boards based on different chips, and must include pre-amplifiers with AtMega328 uCPU. 

One more important things to mention in this short introductory, as I used FFT algorithm for phase calculation ( I like FFT very much, you probably, already notice it ),

Arduino is capable not only track a MOSQUITO flying in your room, it could tell if it’s MALE of FEMALE !!!!!

                  SOFTWARE.

  As I say above, I choose 40 kHz for sampling rate, which is a good compromise between accuracy of the readings  and maximum audio frequency, that Localizator could hear. Getting signals from two mic’s simultaneously, upper limits for audio data is 10 kHz. No real-time, “conveyor belt” include 4 major separate stages:

  • sampling X dimension;
  • FFT
  • phase calculation
  • delay time extracting
  • sampling Y dimension;
  • FFT
  • phase calculation
  • delay time extracting

4 mic’s split in 2 groups for X and Y coordinate consequently. Picking up 4 mic’s simultaneously is possible, but would reduce audio range down to 5 kHz, so I decided to process two dimension (horizontal and vertical planes)  separately, in series. Removing vertical tracking from the code, if it’s not necessary, would increase speed and accuracy in leftover plane, of course. I’d refer you for description of the first and second stages to other blogs, FFT was brought w/o any modification at all. Essential and most important part of this project, stages 3 and 4.

Phase Calculation (3).

 Mathematical tutorial on a topic, I’m not any good as a teacher, so you better read somewhere else, to brush up a basic concept. Core of the process is arctangent function. This link says a number of cycles. In two words – too slow.  LUT ( Look Up Tables ) is the best solution for no-float uCPU to do complex math extremely fast, and reasonably (?) precise. Drawback of LUT is limited size, so it could be saved in FLASH memory, which in next tern  is also limited. This is what I did on “resource management” side: 1 kWords ( 16-bit integers, 2 kBytes) , 32 x 32 ( 5 x 5 bites) LUT, scaled up to 512 to get better “integer” resolution. There are a few values in top-right corner, that melted together as their differences are less than “1″ (not shown on the picture on right side). The “worst” resolution is in top-left corner, where “granularity” is reaching 256, or unacceptable 50% of the dynamic range. To stay as far away from this corner, I put a “Rainbow Noise Canceler” – single line with ” IF ” statement, which “disqualifies” any BIN with magnitude, calculated at the FFT stage, lower than 256.

IF(((sina * sina) + (cosina * cosina)) < 256) phase = -1;

 I called it “Rainbow” because of it’s shape, “red line” is an arc, going from 16 on top line to 16 on left side. Also, “Gain Reset” – 6 bit ( depends on the FFT size, has to be 6 bits for 128) reduced to 5 bits, in order to get better sensitivity. This two parameters / settings, 5-bit and 3.5 bit magnitude limit, create a “threshold” for weak spectral peaks. Basically, depends on application, both values can be adjusted in  different proportions.

 There are two category of tracking technics, with mic’s installed on moving platform, and stationary mic’s. First one is a little bit easier  to understand and build, requires Relative direction to sound source. This what I’ve done. Stationary mic’s approach, when motors are moving laser pointer (or filming camera) alone, would require Absolute direction to sound source, and must include stage #5 – angle calculation via known delay time. Math is pretty simple, acrsine function, and at this point only one calculation per several frames would be necessary, so floating point math wouldn’t be an issue at all. No LUT, scaling, rounding/truncation. Elementary school geometry knowledge – thats all you need.

Delay Time Extraction (4).

 Subtraction phase value of one “qualified” mic’s data pull from another, produce phase difference. To turn phase difference in delay time, division by BIN number is performed. Lets call this operation “Denominator” process.  The denomination is necessary, because all data after this step going to be combine and process together, doesn’t matter of wave length, which is different for every bin. Frequency and wavelength related to each other via simple formula:  Wavelength = Velocity / Frequency, where velocity is a speed of sound wave in the air ( 340 m/sec at room temperature). As distance between two mic’s is a constant,  sound with different wavelength ( frequency ) produce different phase offset, and denomination make them proportional. (WikiPedia, I’m sure, would explain this much better, mind you, I’m a Magician, not mathematician).

First picter on right side shows  ”Nuisance 3: Incorrect arctan” correction. You will find two lines with “IF” statements in the code relaited to stage #3.

Second one,  gives you idea why other correction at stage #4 is necessary As you can see, subtraction one arctan from another generates a rectangular “pulse” ( Diff. n. corr., violet line) whenever one function changes sign but other (delayed version) not yet. Light blue line (DIFF(B)) doesn’t have such abnormality. Math is simple, just two lines with “IF’s” in the same manner, only “double size” constants this time. 2048 on my scale corresponds to 2 x PI, 1024 – PI, and 512 – PI / 2.

Arduino has only 1 ADC, so there is always constant delay time equals to one sampling period ( T = 1/40 kHz = 25 usec), which also should be subtracted ( or added, depends how you associate input 1 and 2 – left / right side mic.)

Filtering.

 To fight reverberation and noise, I choose a Low Pass Filter, which I’d call here as a “Rolling Filter”. My research with regular LPF, shows that this class of filters is completely NOT appropriate for such type of data, due their high susceptibility to “spikes”, or sudden jump in magnitude level. For example, when system getting steady reading from 2-3 test frequencies with low values, let say -10, simple averaging ( should be -10 ) results will be corrupted with one accidental spike (magnitude +2000) during next 60 – 100 consecutive frames !!!  The Median Filter doing well eliminating sudden spikes, the same time is very hungry to CPU cycles, as it’s using “sort” algorithm each time new sample was arrived to the data pull. Having 64 frequencies, and setting filter kernel to 5 – 8 samples, arduino would be buried doing sorting at almost 40 ksps.  Even processing each frequencies data not individually,  and sorting only one 64 elements array still very time consuming job.  After thinking a while, I came up to conclusion, that “Rolling Filter” has almost the same efficiency as Median, but instead of “sorting” requires only 1 additive operation! On long run, the output value will “roll” and “stick” to the middle of the pull. ( Try to model it in LibreOffice. )  Adjusting “step” of the “Rolling Filter”, you can easy manipulate responsiveness,  which is almost impossible with Median Filters. (Things TO DO: Adaptive Filtering, real time adjustment depends on input data “quality”).

 To be continue…. Video will follows !

( Predicting your question, how “good” is localization?  Its about same, as Laser TRF (tracking range finder) has, look at other blogs for now to get impression.  In other words: ASTONISHINGLY GOOD,   a few (1 – 5) angular degree in closed environment.

Link to Arduino (Leonardo) sketch:  Localizator-beta-9.


Quasi real-time oscilloscope: REMIX

 Updated on 21 Sept. 2012, version 4.

 Updated on 15 Oct. 2012, version 5.

Recently I was reviewing one of my oldest project, and decided to “refresh” previous design by taking full advantage of the new arduino Leonardo board.  Based on AtMega32U4, which include PGA (programmable gain amplifier), oscilloscope’s  analog front end doesn’t require external OPA this time, end could be build in 1-2 hours on prototype board, using 5 resistors, 5 capacitors and one IC. Short specification:

  • Four channels.
  • Switchable gain settings 1x, 10x, 40x, 200x.

Hardware.

 As you can see on drawings above, inputs are AC coupled with caps, and biased with 1.25V generated by LM317. Connector for other two inputs not installed yet.

Software.

Project keeps almost same  structure of commands (CLI – command line interface) as its ancestor, with only two new for channel and gain selection. Read comments, it explains how to use them. One more things, I removed “r” – re-print option from the list of available commands.

Have fun!.

Link to arduino Leonardo sketch:  Oscilloscope_Leonardo.

********************************************* Version 4*********************************************

 Well, even posted above sketch has low complexity, and its good for beginning, nevertheless it’s quite limited as measuring device. The most important feature for oscilloscope, except V/div,  is T/div, or timing, that has to be as much precise as possible.   This is why “standard” timing options based on TIMER1 were add to next version of software. There are 9 time settings Time/div (10 samples):

50ms, 20ms, 10ms, 5ms, 2ms, 1ms, 500us, 200us, 100usec

corresponding to

200Hz, 500Hz, 1kHz, 2kHz, 5kHz, 10kHz, 20kHz, 50kHz, 100kHz

sampling rate. You can choose any of of this in the same manner, entering a digit 1-9 and letter d (display). Zero would skip capture, and just do whatever next letter request. Basically, 0 should be used to print channels data from memory. Combination: 9d0i – capture at 100 kHz rate, print chart and info-table,
4c2g7d – select 4-th channel, set gain to 10, select 20 kHz sampling rate and display.

I also add multichannel sampling capability. Commands 2m, 3m or 4m would configure oscilloscope for 2, 3 or 4 channels simultaneously capturing input waveform.   As arduino has only 1 ADC, switching in multichannel mode would reduce sampling rate proportionally to number of channels, and this changes would be reflected in right top corner of the display. What more, arduino would automatically change vertical resolution per channel, to fit all 2 – 4 charts on one screen!

Known caveats:

  • Sampling rate 9, or 100 usec per division (10 usec per sample) could not be selected in multichannel mode, should be in use for single channel only (1m, 1c – 2c – 3c – 4c).
  • There is a “shift” in channel number, signal  presented at input 1 would show up on screen 2, and so on. This happens on time settings 7 (occasionally), 8 and 9 in 4x multichannel mode due delay in MUX registers switching. Shouldn’t be an issue for 2x channel mode, or when you have an “overview” of the signals shape in single channel mode (1m) before switching to 4x, so you would know what to expect at each input port.

Link to arduino Leonardo sketch:  Oscilloscope_LeonardoV4

********************************************* Version 5*********************************************

 New updated version. I was thinking how to improve simplest ever oscilloscope, and have made some structural changes in the code, mostly related to sampling in multichannel mode.

First of all, instead of “delay” 2 microseconds, that was used to give a multiplexer (and PGA amplifier) time to “settle” on new channel, I decided not to waste a time (that may be priceless in real-time application), rather start new conversion, than track samples based on the “history” of MUX settings, and store new sample in corresponding two dimensional array box. Now MUX and PGA would have more time,  and consequently I could reduce ADC pre-selector’s clock to get better readings. It solved all the problems with wrong association port number and picture on the screen.

Secondly,  as you, probably, already notice I’ve been working on another project recently, where phase is a PRIME factor of the whole idea of the design. Phase noise is a jitter, and it degrades  spatial resolution and the sensitivity of the sound localization.  Jitter always would be presented in the incoming signal – sampled waveform due “not synchronous” way of sampling, as “start new conversion” events were generated in “manual” mode. As microprocessor spend different amount of time to get inside of the ISR (interrupt subroutine) depends on where it was interrupted, time frame of the events, basically, was not defined. In order to get rid off the phase noise, I changed ADC settings to be triggered via TIMER 1. There is a code:

ADCSRA = ((1<< ADEN)| // 1 = ADC Enable
(0<< ADSC)| // 1 = ADC Start Conversion
(1 <<ADATE) | / / 1 = ADC Auto Trigger Enable
*****
ADCSRB = ((1<<ADHSM)| // High Speed mode select
(0<< MUX5)| // 0 (10100) ADC1<->ADC4 Gain = 1x.
(0<<ADTS3)|
(1 <<ADTS2) | / / Sets Auto Trigger source Timer/Counter1 Compare Match B
(0 <<ADTS1) |
(1 <<ADTS0) );

For some unknown for me reason, Atmel designed TIMER 1 channel B to be an auto trigger source of the ADC, the same time to run TIMER 1 itself in CTC mode, channel A must be set. I simply “bind” two channels A and B in “parallel”, so both of them rise interrupt flag at the same moment, only A re-starts a TIMER 1, and B generates “start new conversion” event and calling ISR for “maintenance” – take a new sample waiting in the line and switch a MUX to another channel of the oscilloscope.

uint8_t take_it( int fast )
{
   ADCSRA &= 0xF8;
   if ( multChan -1 )
   {
    switch( fast ) { 
       case 7: // 20 kHz / 
         ADCSRA |= 0×03; 
         break; 
       case 8: // 50 kHz / 
         ADCSRA |= 0×02; 
         break; 
       case 9: // 100 kHz / 
         Serial.print(F(“\n\t *** NOT SUPPORTED ***”));
         return 0;
        default:
        ADCSRA |= 0×04;
       }
     }
    else
    { 
     switch( fast ) { 
        case 6: // 10 kHz / 
          ADCSRA |= 0×06; 
          break; 
        case 7: // 20 kHz / 
          ADCSRA |= 0×05; 
          break; 
        case 8: // 50 kHz / 
          ADCSRA |= 0×04; 
          break; 
        case 9: // 100 kHz / 
          ADCSRA |= 0×03; 
          break; 
        default:
          ADCSRA |= 0×07;
        }
      }
OCR1A   = smplTime[fast -1];
OCR1B   = smplTime[fast -1];
TCNT1   = 0;
TIFR1    |= (1<<OCF1B); 
TIMSK1 |= (1<<OCIE1B);
   flagSamp = 0; 
   while ( !flagSamp );
   for ( uint8_t indx, y = 0; y < multChan; y++){
      if ( multChan -1) indx = y;
      else indx = chanNumb;
   for ( int i = 0; i < INBUF; i++){
      if ( x[indx][i] & 0×0200) x[indx][i] += 0xFE00; // Convert to negative 16-bit word (2′s comp)
      else x[indx][i] += 0×200;
      }
     }
  return 1;
}
ISR(TIMER1_COMPB_vect)
{
   static uint8_t n_sampl = 0;
   static uint8_t history = 0;
   x[history][n_sampl] = ADC;
    history = chanNumb; 
   if ( multChan -1 )
   { 
    chanNumb++;
    if ( chanNumb >= multChan )
      {
       chanNumb = 0;
       n_sampl++;
      }   
     ADMUX &= 0xFC;
     ADMUX |= chanNumb; 
    }
   else
   {
     n_sampl++;
    }
   if ( n_sampl >= INBUF )
    {
      flagSamp = 1;
      n_sampl = 0;
TIMSK1 &= ~(1<<OCIE1B);
   }
}

Oscilloscope_Leonardo_V5.

The only issue that not solved yet, is a sampling in multichannel mode in “9″ T/div. Probably, Atmel just was not design to do such things…