Do tone.threshold(level) and tone.available() work?

Status
Not open for further replies.

insane

Active member
I'm trying detect and then differentiate the time between the arrivals of two 30ms pings (500Hz apart) using the audio adapter and a single mic. The tone.threshold(level) and tone.available() functions don't seem to limit what (or when) gets detected. I need to know when the end of each sonic ping occurs. Can you look at my code, please?

Code:
/* file: Ping_Listener.ino */

#include "AudioSetup.h"
#include "GlobalVars.h"

void loop()
{
  if ( sampleReady )
  {
    noInterrupts();
    digitalWriteFast(  LED_PIN, HIGH  );
    Serial.printf( "Ping available: h1:%4.4f, h2:%4.4f; Heard: %d\n",
                    h1, h2, elapsedTimerH );
    h1  = 0.0f;
    h2  = 0.0f;
    digitalWriteFast(  LED_PIN, LOW  );
  }
  sampleReady = false;
  elapsedTime = 0;
  interrupts();
}


/* file: MainSetup.ino */

void setup()
{
  Serial.begin( 115200 );                     // start serial monitor support
  
  pinMode( LED_PIN, OUTPUT );
  digitalWriteFast(  LED_PIN, LOW  );
  
  AudioMemory( 64 );
  sgtl5000_1.enable();
  sgtl5000_1.volume( 0.8 );                   // 0.8 is the max undistorted headphone level
  sgtl5000_1.inputSelect( AUDIO_INPUT_MIC );
  sgtl5000_1.micGain( microphoneGain );
  delay( 100 );
  
  toneHeard1.threshold(  levelThreshold );    // set up tone detectors to hear either ping
  toneHeard2.threshold(  levelThreshold );

  toneHeard1.frequency(  sine1Frequency, minSineCount1 );
  toneHeard2.frequency(  sine2Frequency, minSineCount2 );

  Serial.println( "listening...." );
  delay( 50 );

  sampleTimer.priority( 144 );                // start interval timer to listen for periodic pings
  sampleTimer.begin( pingListen, 21 );        // 21 ~= 48KHz
}


/* file: CallbackFunctions.ino */

void pingListen()
{
//  noInterrupts();
//  h1  = toneHeard1.read();
//  h2  = toneHeard2.read();
//  interrupts();
  if ( toneHeard1.available() || toneHeard2.available() )
  {
    noInterrupts();
    h1  = toneHeard1.read();
    h2  = toneHeard2.read();
    elapsedTimerH = elapsedTime;
    sampleReady = true;
    interrupts();
  }
}


/* file: AudioSetup.h */

#include <Audio.h>

AudioControlSGTL5000     sgtl5000_1;          //xy=66.1999969482422,  101.99998474121094
AudioAnalyzeToneDetect   toneHeard1;          //xy=436.1999969482422, 160.1999969482422
AudioAnalyzeToneDetect   toneHeard2;          //xy=436.1999969482422, 205.1999969482422
AudioInputI2S            i2s_mic;             //xy=66.1999969482422,  160.1999969482422
AudioConnection          patchCord1( i2s_mic, 0,  toneHeard1, 0 );
AudioConnection          patchCord2( i2s_mic, 0,  toneHeard2, 0 );


/* file GlobalVars.h */

#include <TeensyDelay.h>

#define LOW_FREQ_LIMIT   5000
#define HIGH_FREQ_LIMIT 20000
#define LED_PIN            20

IntervalTimer sampleTimer;                    // timer by which to index ping intervals

elapsedMicros elapsedTime;                    // timer to evaluate the period following ping-to-detected

volatile unsigned int elapsedTimerH;
volatile unsigned int pingTimer;

volatile float h1, h2;
volatile bool  sampleReady    =   false;

const float    gasSoS         =   0.343683f;  // speed of sound through air in mm/μsec
const float    h2oSoS         =   1.484f;     // speed of sound through water in mm/μsec
const float    levelThreshold =   0.20f;      // low-threshold level that will set frequency detect = true (0.0 - 1.0)

int microphoneGain  =  63;                    // microphone input gain
int pingTimeMs      =  30;                    // 30.0 ms recommended

int sine1Frequency  =  LOW_FREQ_LIMIT;        // an arbitrary (ping) audible frequency -- the default start frequency
int sine2Frequency  =  LOW_FREQ_LIMIT + 500;  // a second, similar frequency, differentiable from the first

int minSineCount1   = (sine1Frequency / 1000.0f) * pingTimeMs;  // allow 30msec to detect sine1 (240 cycles)
int minSineCount2   = (sine2Frequency / 1000.0f) * pingTimeMs;  // allow 30msec to detect sine2 (225 cycles)
 
That is a good question. I have been fiddling around with both functions and they do not seem to work for me either. Could it be a bug in the library?
 
What is "two 30ms pings (500Hz apart)"?

Any chance for a WAV file recording of this input?

I could have been clearer, sorry. So the first ping generated from "speaker 1" consists of 5kHz sine wave (generated from an independent Teensy/Audio-board/Sine-function) lasting 30ms (150 cycles) every (for now) second. The other ping source, "speaker 2", is generated similarly, but at 5.5kHz for 165 cycles. They are synchronized. Here's the code that generates the ping--I tried to take out anything irrelevant, like running switches, display, etc., but may have missed something. This works fine, also I can attach a WAV of its output, but it will take me a little while to set it up for a recording.

Code:
#define LOW_FREQ_LIMIT   5000

IntervalTimer pingStartTimer;                 // timer by which to index ping intervals

IntervalTimer sampleTimer;

elapsedMicros elapsedTime;                    // timer to evaluate the period following ping-to-detected

volatile unsigned int elapsedTimerP;
volatile unsigned int elapsedTimerH;
volatile unsigned int pingTimer;

int microphoneGain  =      63;                // microphone input gain
int micAmplifier    =       2;                // additional microphone gain

int levelThreshold  =     0.5;                // low-threshold level that will set frequency detect = true (0.0 - 1.0)

int sine1Frequency  = LOW_FREQ_LIMIT;         // an arbitrary (ping) audible frequency -- the default start frequency
int sine2Frequency  = LOW_FREQ_LIMIT + 500;   // a second, similar frequency, differentiable from the first

unsigned int pingTimeMs    = 30;  // 30.0;
int minSineCount1   = (sine1Frequency / 1000.0f) * pingTimeMs;  // 5K * 2ms = 10cycles  // allow 30msec to detect sine1 (150)
int minSineCount2   = (sine2Frequency / 1000.0f) * pingTimeMs;  // 5.5K * 2ms = 11 cycles //allow 30msec to detect sine2 (165)
int minSineCount    = ( minSineCount1 > minSineCount2 ) ? minSineCount1 : minSineCount2;

int pingInterval    =  1000000;   // 1 sec             // start ping (usec) every 100ms (100,000usec)
int pingDurration   =  (int)(  ((1000000.0f / (float)sine1Frequency)) * (float)minSineCount  ); // + 6000;  //(in usec)



void startPing()                  /* Timer callback function to begin generating two arbitrary sine */
{                                 /* waves simultaneously; each pinging on a seperate audio channel */
  noInterrupts();
  sampleTimer.end();
  interrupts();
  AudioNoInterrupts();
  sine1.amplitude( 1.0 );                     // begin sine wave 1  (Left)
  sine2.amplitude( 1.0 );                     // begin sine wave 2  (Right)
  AudioInterrupts();                          // ...using audio-interrupt control to synchronize

  if ( !continuous )
  {
    noInterrupts();                           // set timer to end ping signal
    elapsedTime = 0;                          // only needed to check ping interval in stopPing()
    TeensyDelay::trigger( pingDurration );
    interrupts();
  }
}


void stopPing()                   /* Timer callback function to terminate sine-wave functions; used  */
{                                 /* to implement ping durration; set at the end of startPing() call */
  AudioNoInterrupts();
  sine1.amplitude( 0.0 );                     // supress sine wave 1
  sine2.amplitude( 0.0 );                     // supress sine wave 2
  AudioInterrupts();                          // ...using audio-interrupt control to synchronize

  if ( !continuous )
  {
    noInterrupts();
    pingTimer   = elapsedTime;                // just to check the ping interval
    elapsedTime = 0;                          // zero the elapsed-time timer at the end of each ping
    interrupts();
  }
}


void setup()
{
	:
	:
  AudioMemory( 64 );
  sgtl5000_1.enable();
  sgtl5000_1.volume( 0.8 );                   // 0.8 is the max undistorted headphone level
  sgtl5000_1.inputSelect( AUDIO_INPUT_MIC );
  sgtl5000_1.micGain( microphoneGain );
  amp1.gain( micAmplifier );
  delay( 100 );
  
  toneHeard1.threshold(  levelThreshold );    // set up tone detectors to hear either ping
  toneHeard2.threshold(  levelThreshold );
  tonePlayed1.threshold(  levelThreshold );
  tonePlayed2.threshold(  levelThreshold );

  toneHeard1.frequency(  sine1Frequency, minSineCount1 );
  toneHeard2.frequency(  sine2Frequency, minSineCount2 );
  tonePlayed1.frequency( sine1Frequency, minSineCount1 );
  tonePlayed2.frequency( sine2Frequency, minSineCount2 );

  AudioNoInterrupts();                        // set up tone generators to define both pings (off)
  sine1.frequency( sine1Frequency );
  sine2.frequency( sine2Frequency );
  sine1.phase( 0 );
  sine2.phase( 0 );
  sine1.amplitude( 0.0 );
  sine2.amplitude( 0.0 );
  AudioInterrupts();                          // both sine waves at zero amplitude, but in phase
	:
	:
  pingStartTimer.priority( 144 );             // start interval timer to generate periodic pings
  pingStartTimer.begin( startPing, pingInterval );
  startPing();
  TeensyDelay::begin();                       // initialize end-of-ping timer
  TeensyDelay::addDelayChannel( stopPing );   // add delay channel & attach callback function
}


AudioControlSGTL5000     sgtl5000_1;          //xy=66.19999694824219,  101.99998474121094
AudioSynthWaveformSine   sine1;               //xy=66.19999694824219,  267.20001220703125
AudioSynthWaveformSine   sine2;               //xy=66.19999694824219,  332.20001220703125
AudioAnalyzeToneDetect   tonePlayed1;         //xy=436.20001220703125, 267.20001220703125
AudioAnalyzeToneDetect   tonePlayed2;         //xy=436.20001220703125, 332.20001220703125
AudioAnalyzeToneDetect   toneHeard1;          //xy=436.20001220703125, 160.1999969482422
AudioAnalyzeToneDetect   toneHeard2;          //xy=436.1999969482422,  205.1999969482422
AudioOutputI2S           i2s1_spkr;           //xy=266.20001220703125, 299.20001220703125
AudioInputI2S            i2s_mic;             //xy=66.19999694824219,  160.1999969482422
AudioAmplifier           amp1;                //xy=266.20001220703125, 160.1999969482422
AudioConnection          patchCord1( sine1,   0, i2s1_spkr,   0 );
AudioConnection          patchCord2( sine2,   0, i2s1_spkr,   1 );
AudioConnection          patchCord3( sine1,   tonePlayed1       );
AudioConnection          patchCord4( sine2,   tonePlayed2       );
AudioConnection          patchCord5( i2s_mic, 0,  toneHeard1, 0 );
AudioConnection          patchCord6( i2s_mic, 0,  toneHeard2, 0 );
 
FWIW, i have simple sketch using AudioAnalyzeToneDetect with submarine sonar test at
https://www.youtube.com/watch?v=lj6h9acBTbI

Code:
// sub sonar https://www.youtube.com/watch?v=lj6h9acBTbI
// if audioout USB set USB to Audio in IDE
#include <Audio.h>

#define MIC_GAIN  3  // multiplier for the specific mic

// GUItool: begin automatically generated code
AudioInputI2S            audioin;           //xy=145,151
AudioOutputI2S           audioout;           //xy=337,201
//AudioOutputUSB           audioout;
AudioAnalyzeToneDetect      tone1;      //xy=341,256
AudioConnection          patchCord1(audioin, 0, audioout, 0);
AudioConnection          patchCord2(audioin, 0, audioout, 1);
AudioConnection          patchCord3(audioin, 0, tone1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=413,385
// GUItool: end automatically generated code


void setup() {
  Serial.begin(9600);

  AudioMemory(12);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
  sgtl5000_1.micGain(46);
  tone1.frequency(830);
  //tone1.threshold(0.1);  // can test if (tone1)
  delay(1000);
  Serial.println("starting");
}

void loop() {
  if (tone1.available()) {
    float v = tone1.read();
    if (v > 0.1) Serial.printf("%d ms %f\n", millis(), v);
  }
}

if i use tone1.threshold(0.1) and if (tone1) ..., i get a lot of same values from read()

i used FFT to identify the frequency bin. 500 ms ping event every 5 seconds ish.
fftsonar.png
 
Last edited:
The ping.wav file contains a sequence of nine synchronized 5kHz and 5.5kHz ping-pairs, one second apart. The ping.png (unfortunate use of file extension) file is an image of the FFT.

Looking at the FFT again, I'm not sure why there is such an amplitude difference between the two frequencies--probably my speaker/mic setup.

ping.jpg
 

Attachments

  • ping.zip
    476.4 KB · Views: 68
FWIW, i have simple sketch using AudioAnalyzeToneDetect with submarine sonar test at
https://www.youtube.com/watch?v=lj6h9acBTbI

...

if i use tone1.threshold(0.1) and if (tone1) ..., i get a lot of same values from read()

i used FFT to identify the frequency bin. 500 ms ping event every 5 seconds ish.

You didn't happen to try out the tone.available() function, did you? That's what's critical for me to determine timing (from the end of the pulse, given the count of sine-wave cycles at the minimum tone.threshold() level).
 
You didn't happen to try out the tone.available() function, did you? That's what's critical for me to determine timing (from the end of the pulse, given the count of sine-wave cycles at the minimum tone.threshold() level).

Yes, the sample sketch is using tone1.available(). i found that more informative than using threshold() and if (tone1) ... and what read() returned.

I didn't ever figure out why increasing cycles in tone1.frequency(860,cycles) stopped sketch from detecting > 0.1 value events ...
 
Ah, yes... The difference, though--and this is where I'm having difficulty--is that I am using the tone.frequency() with the two-parameter overload, specifying the number of cycles detected before returning a "true" response. It actually *may* be working correctly for you. As I understand it--and I might be wrong--is that the specified number of cycles must be detected (at the specified level?) continuously before returning a 'true' response.
 
Last edited:
If I use tone1.frequency(830,10) (the default) and tone1.available() and print readings > 0.1, then from my youtube audio, i get a clump of values about every 11 ms (10/830 is 12 ms) for about 500 ms. If I use (830,5), i get values >0.1 every 6 ms for about 500 ms. If I use (830,16), i don't get output as frequently and small clumps every 19 ms. If I use (830,20) ... never prints values > 0.1. if i drop the if-test to > 0.01, then i see the ping clumps at 24 cycles. I don't pretend to understand the semantics of cycles in tone.frequency()
 
Last edited:
I don't remember where, but I read that 30ms would give a good read--assuming good levels. I'm hopeful that I can precisely determine the end of the ping signal so that I can mark that time. Using two different frequencies from two speakers at two distances, I should be able to measure the difference in ping arrivals between the pair.
 
I ran the code from msg #5 while playing the WAV file from msg #6.

I did need to increase the mic gain (my speakers weren't quite loud enough), and I set the tone detection frequency to 5000 Hz.

 
Status
Not open for further replies.
Back
Top