Acoustic Cronograph for measuring supersonic bullet speed

Hello forum.

I am building an Acoustic cronograph with an UNO using ICR and PCINT.
Basic principle is that 3 or more mems-mics in a row detect on CHANGE of when a supersonic shockhwave from a bullet is passing over the mems-mics.
By knowing the mems-mics distances, the bulletspeed is easy to calculate hence Speed=Time/distace.

Problem:
Even if this actually works quite good, There is a additional need for me to know max ADC-Current within each interrupt without slowing the interrupt down.
How do i read/save the ADC-Value the fastest way inside the interrupt, is there a built in ADC reader in ATMEGA328p that i can use for the purpouse thats not use polling? Is ADC on each pin saved somewhere in hardware?

Example below:


ISR (PCINT0_vect) {

  if (stopLeft == 0 ) {

AT THIS POINT IN EACH INTERRUPT I WOULD LIKE TO READ THE ADC-CURRENT IF SAVED IN HARDWARE??  WHERE IN DATASHEET I GET THIS INFO?


    timeLeft = micros();

    stopLeft = 1;
    PCIFR = 0;       //Clear INTflags just in case
    PCMSK0 = B00000000; //Deactivate all pins in PCINT0-vect..PortB

  } //END IF
} //END PCINT0_vect´

Why on Earth did you post this in the "Emergency Response:Remote Learning "section of the forum ?

Your topic was MOVED to its current forum category which is more appropriate

Ooops. Very Sorry for that. Human error. No emergency.
Thank u for helping me correct my misstake.

Why not use analogueValue = analogRead( pinNr ); in the ISR?

well, Analog read takes about 9052 µs in my head... That means a lot of time spent reading and i miss ability to read on other pins simultaniously. Maybe just confusing everyone, but the speed of sound is so fast, and the mic distance is so short ( 4,5 cm max) that time gets critical on the edge of what 16t mhz is fixing with 1 mm accuracy. Ist basically that i actually read the speed of sound with about 1 mm accuracy.

Of-course i can/could change platform to 256 mhz, but im not into that, i want to use UNO.
...
Note that a supersonic bullet triggering the interrupts is actually travelling at up to 1000 m/sec so its actually three times the speed of sound. (340 m/s)

Code below is a working example of how one might/can use the PCINT to detect the soundwavefront passing by also using a tempsensor.

However, i now investigate the possibilitys with the ADMUX settings as well.
As i understand it (??) it is possible on an UNO to trigger pins A0-A5 in respect to the ADC-ref level instead, that would be nice. I need the ref-level to know if i am at the "same" place of the sinuswave.

Just dont know how to get there..yet.. :slight_smile:


  HARDWARE SETUP:
  1 * Arduino Uno
  3 * Sound-Sensors of any kind. Preferrably MEMS due to high performance, built in ADC-amplifier/Comparator, and easy or no calibration.
      Cables
  1 * Breadboard
  1 * Cable to upload
  1 * Active buzzer on pin 11 if u like beeping sounds.


  pin A0 PortC                       pin7 PortD                                 pin8 PortB

   (Mic)                                           (Mic)                                                    (Mic)
  micLeft----------------------------micCenter-----------------------------------micRight     <-----Sound/Wavefront direction

                X meter                               X meter


  Mics = MEMS ADMP 401
  common Ground -5.0 volt
  common DC     +5.0 volt

*/

//____________________________________________________________________________________________________


#define DEG_TO_RAD 0.0175
#define RAD_TO_DEG 57.295

int micLeft = 8;
int micCenter = A0;
int micRight = 7;
int buzzerpin = 11;
int tempSensor = A1;
int ECHOFILTER = 1000; // Set to appropriate millis.. 1 sec is default
int Count = 0;



float speedOfSound = 0;
float tempVal = 0;
float tempVal1 = 0;
float OLD_tempVal1 = 0;
float S = 15.2;  //In centimeter. CC micleft-micRight
float T = 0;
float V = 0;



volatile unsigned long resetcount = 0; // var to increment a fake delay if one mic is accidentally trigged.
volatile unsigned long timeLeft = 0;// Var to store times in micros inside ISR:s. Therefore volatile.
volatile unsigned long timeCenter = 0;
volatile unsigned long timeRight = 0;
volatile int stopLeft = 0;   // Flags to handle the trigs. Used in ISR:S Therefore volatile.
volatile int stopCenter = 0;
volatile int stopRight = 0;



void setup() {

  pinMode(micLeft, INPUT_PULLUP);
  pinMode(micCenter, INPUT_PULLUP);
  pinMode(micRight, INPUT_PULLUP);
  pinMode(buzzerpin, OUTPUT);//Eg Buzzer for debugging if trigged when no monitor is used.
  pinMode(tempSensor, INPUT);
  digitalWrite(buzzerpin, LOW); //Make sure Buzzer is of :)
  Serial.begin(115200);


  //PCICR = PCIF2 + PCIF1 + PCIF0; //Also all PCINT-vectors/ports B,C and D.

  PCICR |= 0b00000111;    // Activate Pin Change possibilitys for all three PCINT-vectors control registrers on Atmega 328p
  PCIFR = 0;              // Zero all INT-Flags.
  PCMSK0 = B00000001;     // Activate only pin D8 for interrupts (PCINT0) on PortB
  PCMSK1 = B00000001;     // Activate only pin A0 (PCINT8) for interrupts on PortC
  PCMSK2 = B10000000;     // Activate only pin D7 (PCINT23) for interrupts on PortD

}

void loop() {

  //Update the temperature in celcius 4 times per second...over and over again....
  tempVal = analogRead (tempSensor);
  delay(250);
  /*
    tempVal1 = analogRead(tempSensor);
    tempVal1 = ((tempVal1 * 500) / 1023);
    if ( tempVal1 != OLD_tempVal1) {
    OLD_tempVal1 = tempVal1;
    Serial.print("Temp Updated to   : ");
    Serial.println(tempVal1);
    delay(3000);

    }//END IF
  

    //WRITE TO ARDUINO IDE-SERIAL-MONITOR FOR DEBUGGING..
    Count = Count + 1;
    Serial.println("_________________________________________________");
    Serial.print("TEST-NR : ");
    Serial.println(Count);
    Serial.print("Micros Left-Center                   : ");
    Serial.println(timeLeft - timeCenter);
    Serial.print("Micros Center-Right                  : ");
    Serial.println(timeCenter - timeRight);
    Serial.print("Micros Left-Right                    : ");
    Serial.println(timeLeft - timeRight);
    Serial.println();
    //Convert the tempsensor reading to degrees in celcius.
    tempVal = (tempVal * 500) / 1023;
    //Calculate the speed of sound at current temperature in celsius
    speedOfSound = (331.3 + (0.606 * tempVal));
    Serial.print("Temp                                  : ");
    Serial.println(tempVal);
    Serial.print("Speed of sound in air at current temp : ");
    Serial.println(speedOfSound);
    Serial.print("Calculated distance Left-Center cm    : ");
    Serial.println((timeLeft - timeCenter) * (speedOfSound / 10000));
    Serial.print("Calculated distance Center-Right cm   : ");
    Serial.println((timeCenter - timeRight) * (speedOfSound / 10000));
    Serial.print("Calculated distance Left-Right cm     : ");
    Serial.println((timeLeft - timeRight) * (speedOfSound / 10000));
    S = (S);//Centimeters!
    T = ((timeLeft - timeRight));
    V = (S / T);
    V = V * 10000;
    Serial.print("Projectile speed m/s                  : ");
    Serial.println(V);
    Serial.print("Mach number                           : ");
    Serial.println(V / speedOfSound);
    // Make a buzzer noise on buzzerpin for debugging
    digitalWrite(buzzerpin, HIGH);
    delay(0);
    digitalWrite(buzzerpin, LOW);
    delay(5); //Debounce before PCICR and PCMSK Re-Activation.
    //  PCIFR = 0; //Zero all Interrupts to work again....
    //PCICR = B00000111;      //Re-Activate PCI-Possibility on vectors 0, 1, and 2...May confuse micros??

    delay (ECHOFILTER);             //Sound-Echo/bounce-filter....

  

           PCMSK0 = B00000001;     // Re-Activate only pin D8 Left nic (PCINT0)
    timeLeft = 0;
    stopLeft = 0;

    PCMSK1 = B00000001;     // Re-Activate only pin A0 Center mic (PCINT8)
    timeCenter = 0;
    stopCenter = 0;

    PCMSK2 = B10000000;     // Re-Activate only pin D7 Right mic (PCINT23)
    timeRight = 0;
    stopRight = 0;

  }//END IF
}//END LOOP

//_____________________________Trig 1 Left mic pins-D8-D13-PortB UNO___________________
ISR (PCINT0_vect) {

  if (stopLeft == 0 ) {
    timeLeft = micros();
    stopLeft = 1;
    //PCIFR = 0;       //Clear INTflags just in case?
    PCMSK0 = B00000000; //Deactivate all pins in PCINT0-vect..PortB

  } //END IF
} //END ISR0

//____________________________Trig 2 Center mic pins A0-A5-PortC UNO______________________
ISR (PCINT1_vect) {
  if (stopCenter == 0 ) {
    timeCenter = micros();
    stopCenter = 1;
    // PCIFR = 0;       //Clear INT-flags just in case?
    PCMSK1 = B00000000; //Deactivate all pins in PCINT1-vect..Port C

  } //END IF
} //END ISR1


//____________________________Trig 3 Right mic pins D0-D7-PortD UNO_______________________
ISR (PCINT2_vect) {

  if (stopRight == 0 ) {
    timeRight = micros();
    stopRight = 1;
    // PCIFR = 0;        //Clear INTflags just in case?
    PCMSK2 = B00000000; //Deactivate all pins in PCINT2-vect..PortD

  } //END IF
} //END ISR2

I would say it takes 200 microseconds for the very first read but then only 100 microseconds fot the following readings. Know You also need to store the value somehow.

U mean to read the ADC is about 100 micros?
Thats odd no?
The PCINT is about 8-12 micros without prescaling to get in and out of the interrupt.

Hmm..If the ADC is not in anyway connected to the PCINT function then i need to read more in the datasheet. Confusing. :confused:

I can't tell if an interrupt can trigger the ADC but the value needs to be read and stored somewhere.
There are fast ADC circuits on the market and they cost... In a low cost UNO a more simple conversion is used.

The ADC can perform readings much faster than 104 usec, that is simply the default for Arduino Uno.

It takes 13 cycles of the ADC clock to make one reading, and that clock is adjustable by changing the ADC clock prescaler register. At rates higher than about 20 usec/reading you start to lose accuracy and bit depth.

See Static Accuracy Tests of the Arduino Internal ADC.

I did the error analysis for acoustic chronys before I built one for measuring bullet velocities and found a problem. The errors for an acoustic chrony is strongly a function of difference in angle between the bulet's path and the line segment between the microphones, you need them to be parallel withing a degree or two to get an accurate velocity measurement.

Optical chronys exhibit the same type of error but the speed of light is so much faster than the bullet that the error due to misalignment is not measurable.

I use my acoustic chronys for finding loads that have a low extreme spread in muzzle velocity and they work great for that.

To no one in particular: how do you distinguish the bullet shockwave from the report of the muzzle blast? Or is it not an issue?

No issue since the muzzleblast is slower (340 m/s) than the bullet, (approx 700 m/s) so it will reach the mic after the sonic boom that surrounds the bullet. But yes. How do u filter two different sounds arriving at the samre time with similar characteristics is a whole other topic worth another doctor/master/ardionoforumthread.

Absolutely true. Enough science involved here to adress basically any master in correlation/acoustics/math/Physics/AOA /DOA/MUSIC/triangulation, multipath-interferrence, autocorrelation, etc.
Not an easy topic if we take it that far...

Lets therefore start with the ADC in ATMEGA328p- UNO. How do we by best practice get the UNO down to the mentioned 13 usec to register a change, and compare it to a var and stamp the time the fastes way.

Thats very interresting information.
Can u possible give us a code snippet or example code of some kind that adress the challenge.

More info in this link: ATmega ADC tutorial | Open Music Labs

What challenge?

Problably i do just confuse stuff all together and make my self to hard to understand.
I do struggle with the settings for the ADC on the UNO and dont understand the datasheet as good as i hoped i would.

What i want.. (The challenge)

I only want to get a close as possible reading of a timestamp of when a pin is at its highest ADC value. I want to perform a simple sort of autocorrelation, not implementing math if i dont need to. I will do so by comparing different arrays of the ADC value on different pins and find the time difference between PIN-1 and PIN-2 and PIN-3 and so on....Any pins and more than 3 of them prefferably 6 or 7 of them.
The PCINT works ok, but PCINT does not tell me precicely WHERE on the sinewave i am at triggering. Just if i am on falling rising or change. That gives me a possible fault on a 1/2-1/4 of a wavelength.
Basically i just want to stress the UNO to it max. Not having to worry about if timing is the problem later on in the topic when i do other stuff.
So i decided to build this simple setup it as it just seemed easy as a test.

I realize now that i have to do my homework much better before i ask others strange stuff.
This task requier obviously a lot of settings to be changed from the defaults and there are not a few registrers to change in...
ADIE ?
ADPS2, ADPS1 and ADPS0 ?
ADCH and ADCL ?
ADLAR ?

My conclusion is this so far:

  1. Set the ADSC bit in ADCSRA to a one.
  2. Read the adc value in an interrupt from ADCH (8-bit) or ADC (10-bit).?
    No clue if this is fastest but ill just keep trying.. :slight_smile:

That link a was very useful link for me btw, thank-U. I will just have to dig further into the datasheet until i drown in it.. :slight_smile:

You should avoid using interrupts, and for autocorrelation analysis just run the ADC as fast as "reasonable" (that is, as fast as you can, consistent with required accuracy).

As described in the data sheet, to change the ADC clock rate on AVR Arduinos, you change the ADPS2..0 bits in ADCSRA.

By default the setting is ADPS2..0 = 7 for 125 kHz. Set ADPS2..0 = 4 for 1 MHz.

Boom. Thank U. Issue resolved :slight_smile:

Probably not, but worth a try.