Sound Location Part 2 with the Qwiic Sound Trigger and the u-blox ZED-F9x

Pages
Contributors: PaulZC
Favorited Favorite 2

Introduction

In the previous tutorial, we showed you how to calculate the location of a sound using the new SparkX Qwiic Sound Trigger and the u-blox ZED-F9P GNSS receiver.

SparkFun GPS-RTK-SMA Breakout - ZED-F9P (Qwiic)

SparkFun GPS-RTK-SMA Breakout - ZED-F9P (Qwiic)

GPS-16481
$274.95
10

Qwiic Sound Trigger

SPX-17979
Retired

The Qwiic Sound Trigger can be used on its own, or as part of your Qwiic system. It is based on the VM1010 from Vesper Technologies and the TI PCA9536 GPIO expander. The VM1010 is a clever little device which can be placed into a very low power "Wake On Sound" mode. When it detects a sound, it wakes up and pulls its TRIG (DOUT) pin high. The VM1010 can then be placed into "Normal" mode by pulling the MODE pin low; it then functions as a regular microphone. The analog microphone signal is available on the AUDIO (VOUT) pin. All of this happens really quickly, within 50 microseconds (much faster than a capacitive MEMS microphone), so you don't miss the start of the audio signal. This makes it ideal for use as a sound trigger!

The u-blox ZED-F9P GNSS receiver is an old friend. It is a top-of-the-line module for high accuracy GNSS location solutions including RTK that is capable of 10mm, three-dimensional accuracy. With this board, you will be able to know where your (or any object's) X, Y, and Z location is within roughly the width of your fingernail! Something that doesn’t get discussed as much as it should is that the ZED-F9P can also capture the timing of a signal on its INT pin with nanosecond resolution! It does that through a UBX message called TIM_TM2.

In this tutorial, we take this to the next dimension. Quite literally! Last time, we used two sound trigger systems to calculate where a sound came from along the line from one trigger to the other. That’s a one-dimensional (1D) system. If we add a third sound trigger, we can triangulate the location of a sound in two dimensions (2D) allowing us to plot the position in X and Y or East and North!

Let's talk first about triangulation….

Sound Source Triangulation

Last time, we learned that we can calculate the location of a sound by measuring the difference in the sound’s time-of-arrival at two sound triggers. In our 1D example, we:

  • Converted the time difference into distance (by multiplying by the speed of sound)
  • Subtracted that distance from the distance between our two sound triggers
  • And then divided by 2 to calculate the distance from the closest trigger (the one that detects the sound first)

In 2D, there is a little more math involved. Let’s call our three sound trigger systems A, B and C. A is our reference or origin. If we position trigger B due East from A, we can call the line that joins A to B our X axis. Remember when you used to draw graphs in math class? You had the origin in the bottom left corner of your graph paper and you drew the X axis horizontally out to the right. We are doing the same thing here. Trigger A is our origin at X = 0 and Y = 0. We write that as (0,0). If Trigger B is 8 metres (8m) away from A, then its location is X = 8 and Y = 0. We write that as (8,0).

Pictured are the coordinates of two points A and B and the line formed between them

So far, so good. Now, where should we position trigger C? In reality, it doesn’t really matter. We could position C so that ABC forms a perfect equilateral triangle (a triangle where all three sides are the same length). That would give us the best coverage of the area. But the coordinates of C would be (4,6.93). Not pleasant.

Pictured is an equilateral triangle formed from points A, B and C

To keep the math simpler for this example, let’s position C 6m due North from A. The coordinates of C are X = 0 and Y = 6. We write that as (0,6).

Pictured is a right triangle formed from points A, B and C.

We know the distance from A to B is 8m, and that the distance from A to C is 6m. But what about the distance from B to C? We need to know that too. If we get out a tape measure and measure it, we’ll find it is exactly 10m. If you remember Pythagoras’ Theorem from your math class, the square on the hypotenuse is equal to the sum of the squares on the other two sides, we can calculate the distance from B to C by:

  • Squaring (multiplying by itself) the distance from A to B: 8 x 8 = 64
  • Squaring (multiplying by itself) the distance from A to C: 6 x 6 = 36
  • Summing (adding) them: 64 + 36 = 100
  • Calculating the square root: √100 = 10

Pictured are squares formed on the three sides of the triangle

Again, to keep the math easier, let’s pretend that the speed of sound is 1 metre per second (1m/s), not 343.42m/s.

Now, let’s suppose our sound trigger system is running and it detects a sound:

  • The time recorded by trigger A is 10:00:03.605551
  • The time recorded by trigger B is 10:00:05.385165
  • The time recorded by trigger C is 10:00:05.000000

Let’s calculate the differences in those times:

  • A records the sound first, so we will use that as our reference
  • B records the sound 10:00:05.385165 - 10:00:03.605551 = 1.779614 seconds later
  • C records the sound 10:00:05.000000 - 10:00:03.605551 = 1.394449 seconds later

With the speed of sound being 1m/s, we now know that the sound travelled an extra 1.779614m when travelling to B compared to A. And we know that the sound travelled an extra 1.394449m when travelling to C compared to A.

What we do not know is how far the sound travelled to get to A. That’s the first thing we need to calculate.

Let’s call the location of the sound: S. Let’s call the coordinates of S: ( x , y ). Let’s call the distance from S to A: D.

If we sketch that - not to scale - it looks like this:

Pictured is right triangle A B C with point S located within the triangle

We know that:

  • The distance from S to A is D metres
  • The distance from S to B is D + 1.779614 metres
  • The distance from S to C is D + 1.394449 metres

In order to find the location of S, we need to use triangles. That’s why this technique is called “triangulation”. In trigonometry and geometry, triangulation is the process of determining the location of a point by forming triangles to it from known points.

If we divide our diagram up into more triangles:

Pictured is the triangle formed from points A B and S

We can use Pythagoras’ Theorem to calculate the distances we need:

  • D2 = x2 + y2
  • (D + 1.779614)2 = (8 - x)2 + y2
  • (D + 1.394449)2 = x2 + (6 - y)2

We can write that as:

  • D2 = x2 + y2
  • y2 = (D + 1.779614)2 - (8 - x)2
  • x2 = (D + 1.394449)2 - (6 - y)2

We need to solve for D. To begin, let’s substitute the value for y2 from the second equation into the first equation:

  • D2 = x2 + (D + 1.779614)2 - (8 - x)2

Multiplying out the brackets, that becomes:

  • D2 = x2 + D2 + 1.779614.D + 1.779614.D + 1.7796142 - ( 82 - 8x - 8x + x2 )

Simplifying, it becomes:

  • D2 = x2 + D2 + 3.559228.D + 3.167026 - (64 - 16.x + x2)

If we remove the brackets:

  • D2 = x2 + D2 + 3.559228.D + 3.167026 - 64 + 16.x - x2

The D2 cancel out and so do the x2 leaving:

  • 0 = 3.559228.D + 3.167026 - 64 + 16.x

One final rearrange leaves:

  • 16.x = -3.559228.D + 60.832974

If we divide through by 16 we are left with:

  • x = -0.222452.D + 3.802061

Now let’s go back to our three triangles:

  • D2 = x2 + y2
  • y2 = (D + 1.779614)2 - (8 - x)2
  • x2 = (D + 1.394449)2 - (6 - y)2

This time, let’s substitute the value for x2 from the third equation into the first equation:

  • D2 = (D + 1.394449)2 - (6 - y)2 + y2

Multiplying out the brackets, that becomes:

  • D2 = D2 + 1.394449D + 1.394449D + 1.944488 - (36 - 6y - 6y + y2) + y2

If we remove the brackets:

  • D2 = D2 + 1.394449D + 1.394449D + 1.944488 - 36 + 6y + 6y - y2 + y2

Again, the D2 cancel out and so do the y2 leaving:

  • 0 = 2.788898.D + 1.944488 - 36 + 12.y

One final rearrange leaves:

  • 12.y = -2.788898.D + 34.055512

If we divide through by 12 we are left with:

  • y = -0.232408.D + 2.837959

Now we can put our values for x and y back into our first equation:

  • D2 = x2 + y2

  • D2 = (-0.222452.D + 3.802061)2 + (-0.232408.D + 2.837959)2

Multiplying out the brackets, we get:

  • D2 = 0.049485.D2 - 1.691552.D + 14.455668 + 0.054013.D2 - 1.319129.D + 8.054011

Simplifying:

  • 0.896502.D2 + 3.010681.D - 22.509679 = 0

Now, I’m sure you will remember quadratic equations from your math class too? We can solve for D using the equation:

  • ( -b +/- √(b2 - 4.a.c ) ) / 2.a

  • a = 0.896502

  • b = 3.010681
  • c = -22.509679

Inserting our values, D is:

  • ( -3.010681 +/- √(9.064200 + 80.719889) ) / 1.793004

Which equals:

  • 3.605550 or -6.963804

We can ignore the negative value since it is not within our triangle. Now we know that D is 3.605550m !

Looking back at our equations for x and y:

  • x = -0.222452.D + 3.802061
  • y = -0.232408.D + 2.837959

If we insert the value for D, we can finally calculate the x and y coordinates of S:

  • ( -0.802062 + 3.802061 , -0.837959 + 2.837959 )

Which is:

  • ( 3.000 , 2.000 )

Pictured are the calculated coordinates of point S

Fancy that!

This technique can be used on any configuration of trigger locations. They don’t have to be positioned in a neat right angle triangle. If you would like proof of that and would like to see the math to solve it, have a look at the section called Here There Be Dragons!

Now, The Good News

Thanks for sticking with us. The good news is that we’ve written more Python code to do that math for you!

The GitHub Hardware Repo contains two examples for the Sound Trigger. You can run these in the Arduino IDE. The second example is the crowd pleaser! It runs on the MicroMod Data Logging Carrier Board together with the Artemis Processor Board but it should work just fine with any of our processor boards. It communicates with our ZED-F9P Breakout and the Qwiic Sound Trigger to capture sound events and log them to SD card as u-blox UBX TIM_TM2 messages.

SparkFun MicroMod Data Logging Carrier Board

SparkFun MicroMod Data Logging Carrier Board

DEV-16829
$21.50
SparkFun MicroMod Artemis Processor

SparkFun MicroMod Artemis Processor

DEV-16401
$14.95

Once you have your three sound event UBX TIM_TM2 files, our Sound_Trigger_Analyzer_2D.py will process the files for you and calculate the location of the sound events!

  • Copy the TIM_TM2.ubx file from the SD card from the first system. Rename the file so you know which system it came from. You might want to call it TIM_TM2_A.ubx.
  • Do the same for the TIM_TM2.ubx files from the second and third systems. Again, rename them so you know which system they came from.
  • Place all three files in the same folder / directory as the Sound_Trigger_Analyzer_2D.py Python file

Run the Python code by calling:

language:python
python Sound_Trigger_Analyzer_2D.py TIM_TM2_A.ubx TIM_TM2_B.ubx TIM_TM2_C.ubx 8.0 6.0 10.0 20.0
  • Replace TIM_TM2_A.ubx with the name of the file from the first system (A)
  • Replace TIM_TM2_B.ubx with the name of the file from the second system (B)
  • Replace TIM_TM2_C.ubx with the name of the file from the third system (C)
  • Replace the 8.0, 6.0 and 10.0 with the distances between your sound triggers in metres
    • You must enter them in the correct order: A-B then A-C then B-C
  • The 20.0 is optional. It is the temperature in Celcius (Centigrade). Change this to your actual temperature for added accuracy

The Python code will calculate and display the (x,y) coordinates of any valid sound events it finds. The coordinate system uses the location of A as the origin, and the line running from A to B as the X axis. The Y axis is (of course) 90 degrees counterclockwise from X.

The calculation code assumes that the X coordinate of C is between A and B. If C lies to the left of A, or the right of B, then you need to rename your points. If C is to the left of A, then B becomes A, C becomes B, and A becomes C:

Pictured are two triangles

Enjoy your coordinates!

Here There Be Dragons!

Sound_Trigger_Analyzer_2D.py can work with any configuration of the trigger positions (so long as the X coordinate of C is between A and B). They do not need to be arranged in a nice right-angled triangle with A-C pointing North. Don’t believe me? Want proof? Good for you! OK. Brace yourself. Here we go… Here is the actual math used in Sound_Trigger_Analyzer_2D.py.

Sensor Coordinates

Let’s suppose our three sound triggers are arranged such that they form a triangle ABC:

Pictured is the triangle formed from points A B C

We know the length of the sides: AB, AC and BC.

We know the coordinates of A and B. A is our origin and is located at (0,0). B is due ‘East’ of A and defines our X axis. So we say that B is located at (AB,0). But we don’t yet know the location of C. We need to calculate it. Let’s call the location of C (CX,CY).

If we draw a line from C so it meets line A-B at right angles:

Pictured is the triangle divided into two smaller right triangles

Pythagoras’ Theorem tells us that:

  • Equation 1: AC2 = CX2 + CY2
  • Equation 2: BC2 = ( AB - CX )2 + CY2

If we rearrange those equations, bringing CY2 to the left and multiplying out the brackets, we get:

  • CY2 = AC2 - CX2
  • CY2 = BC2 - AB2 + 2.AB.CX - CX2

Because those two equations are equal, we can arrange them as:

  • AC2 - CX2 = BC2 - AB2 + 2.AB.CX - CX2

The two -CX2 cancel out. Rearranging, we get:

  • CX = ( AC2 - BC2 + AB2 ) / 2.AB

We can find CY by inserting the value for CX into one of our earlier equations:

  • CY2 = AC2 - CX2

So:

  • CY = √ ( AC2 - CX2 )

We now have the coordinates for C: ( CX , CY )

Sound Event Timings

Now, let’s suppose our sound trigger system is running and it detects a sound. Let’s call the location of the sound S and give it coordinates ( SX , SY ):

Pictured are the coordinates of point S and the triangles formed from it

Our three systems are logging the time the sound arrives at each sensor (very accurately!).

The time taken for the sound to reach A is: the distance from S to A divided by the speed of sound. Likewise for B and C.

If all three systems hear the sound at the exact same time, then we know that the sound took an equal time to travel from S to A, B and C. That’s a special case where the distances SA, SB and SC are equal (equidistant). But usually sensors B and C will hear the sound at different times to A due to the distances SB and SC being different to SA. We need to include those differences in distance in our calculation. To save some typing, let’s call the distance SA: D

  • SB = D + DdiffB
  • SC = D + DdiffC

DdiffB is the difference between SB and D. Likewise, DdiffC is the difference between SC and D. Note that DdiffB or DdiffC could be negative if the source of the sound is closer to B or C than A.

Sensors B and C will hear the sound earlier or later than A depending on DdiffB and DdiffC.

What we do not know is how long the sound took to travel from S to A, because we do not know the distance D. That’s what we need to calculate,

What we do know is the difference in the time of arrival of the sound at our three sensors. If we bring the three sets of sensor data together, we can calculate the difference in the time of arrival at each sensor. Let’s say that:

  • The time recorded by trigger A is timeA
  • The time recorded by trigger B is timeB
  • The time recorded by trigger C is timeC

Let’s calculate the differences in those times:

  • TdiffB = timeB - timeA
  • TdiffC = timeC - timeA

Again, TdiffB and TdiffC could be negative if the source of the sound is closer to B or C than A.

Sound Event Coordinates

We can convert those differences in time into differences in distance by multiplying by the speed of sound:

  • DdiffB = TdiffB * speed_of_sound
  • DdiffC = TdiffC * speed_of_sound

Now we can use Pythogoras’ Theorem as before to study our triangles:

  • D2 = SX2 + SY2
  • (D + DdiffB)2 = (AB - SX)2 + SY2

We need to add an extra triangle to include SC:

Pictured are additional right triangles formed around point S

The width of the new triangle is:

  • CX - SX

The height of the new triangle is:

  • CY - SY

So, using Pythagoras’ Theorem again:

  • (D + DdiffC)2 = (CX - SX)2 + (CY - SY)2

We now have our three equations:

  • Equation 3: D2 = SX2 + SY2
  • Equation 4: (D + DdiffB)2 = (AB - SX)2 + SY2
  • Equation 5: (D + DdiffC)2 = (CX - SX)2 + (CY - SY)2

We need to solve for D. To begin, let’s substitute the value for SY2 from the second equation into the first equation, so we can find the relationship between D and SX:

  • D2 = SX2 + (D + DdiffB)2 - (AB - SX)2

Multiplying out the brackets, that becomes:

  • D2 = SX2 + D2 + 2.DdiffB.D + DdiffB2 - AB2 + 2.AB.SX - SX2

As before, the D2 cancel and so do the SX2 leaving:

  • 0 = 2.DdiffB.D + DdiffB2 - AB2 + 2.AB.SX

Rearranging:

  • 2.AB.SX = AB2 - 2.DdiffB.D - DdiffB2

Divide through by 2.AB:

  • SX = ( AB2 - 2.DdiffB.D - DdiffB2 ) / 2.AB

Let’s make life easier for ourselves and say that:

  • Equation 6: SX = SXa.D + SXb

Where:

  • SXa = ( -2.DdiffB ) / 2.AB
  • SXb = ( AB2 - DdiffB2 ) / 2.AB

We can calculate both values because we know AB and can calculate DdiffB:

  • DdiffB = TdiffB * speed_of_sound = ( timeB - timeA ) * speed_of_sound

Looking at equation 3 again:

  • D2 = SX2 + SY2

If we replace SX with ( SXa.D + SXb ), we get:

  • D2 = ( SXa.D + SXb )2 + SY2

Rearranging:

  • SY2 = D2 - ( SXa.D + SXb )2

Multiplying out the bracket, it becomes:

  • SY2 = D2 - SXa2.D2 - 2.SXa.SXb.D - SXb2

Simplifying:

  • SY2 = ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2

So:

  • Equation 7: SY = √ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 )

That’s ever so slightly horrible, but let’s go with it…

Now we have values for SX and SY in terms of D, which we can plug into equation 5:

  • (D + DdiffC)2 = (CX - SX)2 + (CY - SY)2

Substituting for SX and SY we get:

  • (D + DdiffC)2 = (CX - ( SXa.D + SXb ) )2 + (CY - √ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 ) )2

Multiplying out the brackets gives (brace yourself!):

  • D2 + 2.DdiffC.D + DdiffC2 = CX2 - 2.CX.SXa.D - 2.CX.SXb + SXa2.D2 + 2.SXa.SXb.D + SXb2 + CY2 - 2.CY.√ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 ) + ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2

Simplifying and moving everything but the root to the left gives:

  • ( 1 - SXa2 - ( 1 - SXa2 )).D2 + ( 2.DdiffC + 2.CX.SXa - 2.SXa.SXb + 2.SXa.SXb).D + DdiffC2 - CX2 + 2.CX.SXb - SXb2 - CY2 + SXb2 = -2.CY.√ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 )

Fortunately the D2 term cancels out completely, which is good news. If it didn’t, we’d have a quartic equation to solve instead of a quadratic…

Simplifying:

  • ( 2.DdiffC + 2.CX.SXa).D + DdiffC2 - CX2 + 2.CX.SXb - CY2 = -2.CY.√ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 )

Dividing through by -2.CY gives:

  • ( ( 2.DdiffC + 2.CX.SXa ).D + DdiffC2 - CX2 + 2.CX.SXb - CY2 ) / -2.CY = √ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 )

We now need to square both sides to get rid of the root. Painful… But we need to do it… But first, to make life easier for ourselves, let’s simplify further and say:

  • da.D + db = √ ( ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2 )

Where:

  • da = ( 2.DdiffC + 2.CX.SXa ) / -2.CY
  • db = ( DdiffC2 - CX2 + 2.CX.SXb - CY2 ) / -2.CY

Remember that we can already calculate values for da and db because we know or can calculate values for: DdiffC; CX; SXb; and CY.

Square both sides:

  • da2.D2 + 2.da.db.D + db2 = ( 1 - SXa2 ).D2 - 2.SXa.SXb.D - SXb2

Moving the terms on the right over to the left:

  • ( da2 - ( 1 - SXa2 ) ).D2 + ( 2.da.db + 2.SXa.SXb ).D + db2 + SXb2 = 0

That’s a quadratic equation which we can solve in the usual way. Let’s make:

  • qa = da2 - 1 + SXa2
  • qb = 2.da.db + 2.SXa.SXb
  • qc = db2 + SXb2

Remember that we can already calculate values for qa, qb and qc because we already know: da; SXa; db; and SXb.

Solving the quadratic::

  • D = ( -qb +/- √( qb2 - 4.qa.qc ) ) / 2.qa

That gives us two possible values for D. One will be negative or otherwise invalid, leaving us with a value for D.

We can then plug the value for D into equation 6 to find the X coordinate of S because we already know the values of SXa and SXb:

  • SX = SXa.D + SXb

We can then calculate the Y coordinate of S using equation 3:

  • D2 = SX2 + SY2
  • SY = √( D2 - SX2 )

Enjoy your coordinates!