Revised NoisBlanker to have I+Q option

pull/6/merge
boblark 4 years ago
parent 17010218ce
commit 9bd938f20c
  1. BIN
      examples/TestNoiseBlanker2/TestNB.gnumeric
  2. BIN
      examples/TestNoiseBlanker2/TestNB128.gif
  3. 90
      examples/TestNoiseBlanker2/TestNoiseBlanker2.ino
  4. BIN
      examples/TestNoiseBlanker2/Voice1.wav
  5. BIN
      examples/TestNoiseBlanker2/Voice1_I16.wav
  6. 89
      radioNoiseBlanker_F32.cpp
  7. 24
      radioNoiseBlanker_F32.h

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

@ -0,0 +1,90 @@
/* TestNoiseBlanker.ino Bob Larkin 20 May 2020
*
* Sine wave plus noise spike test of radioNoiseBlanker_F32
* Feeds input to both 0 and 1 channels (I & Q or L & R).
* The 0 channel controls the noise blanking, but both paths are
* noise-blanked. The function useTwoChannels(true) enables the
* second path. The default case is to use only the 0 path for everything.
*/
#include "AudioStream_F32.h"
#include "OpenAudio_ArduinoLibrary.h"
#include "Arduino.h"
#include "Audio.h"
#include "radioNoiseBlanker_F32.h"
AudioInputI2S_F32 i2sIn1;
//AudioSynthGaussian_F32 gwn1;
AudioPlayQueue_F32 playq1;
radioNoiseBlanker_F32 nb1;
AudioRecordQueue_F32 queue1;
AudioConnection_F32 pcord0(playq1, 0, nb1, 0);
AudioConnection_F32 pcord1(playq1, 0, nb1, 1);
// The next two should be identical data outputs. Pick ONLY ONE at a time
//AudioConnection_F32 pcord2(nb1, 0, queue1, 0);
AudioConnection_F32 pcord2(nb1, 1, queue1, 0);
float32_t *pin;
float32_t dt1[128];
float32_t *pq1, *pd1;
int i;
// Fake noise pulses
float32_t pulse[] =
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.9f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.9f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9f,
0.9f, 0.9f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.9f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
void setup(void) {
AudioMemory_F32(25);
Serial.begin(300); delay(1000);
Serial.println("*** Test Noise Blanker ***");
// setNoiseBlanker(float threshold, uint16_t nAnticipation, uint16_t nDecay)
nb1.setNoiseBlanker(4.0f, 2, 3);
//nb1.showError(1);
nb1.useTwoChannel(true); // true enables a path trrough the "1" side
nb1.enable(true);
while(pin == NULL)
pin = playq1.getBuffer();
Serial.println("Input to NB:");
for(int k=0; k<128; k++) { // Signal and noise
pin[k] = pulse[k] + 0.1*sinf(0.6*(float32_t)k);
Serial.println(pin[k], 6);
}
playq1.playBuffer(); // Put 128 data into stream
queue1.begin();
i = 0;
}
void loop(void) {
// Collect 128 samples and output to Serial
if (queue1.available() >= 1) { // See if it has arrived
pq1 = queue1.readBuffer();
pd1 = &dt1[0];
for(int k=0; k<128; k++) {
*pd1++ = *pq1++;
}
i=1; // data into dt1[]
queue1.freeBuffer();
queue1.end(); // No more data to queue1
}
if(i == 1) {
// Printout 128 samples of the gated signal.
Serial.println("128 NB Output Samples: ");
for (int k=0; k<128; k++) {
Serial.println (dt1[k], 9);
}
i = 2;
}
}

@ -12,43 +12,75 @@
#include "radioNoiseBlanker_F32.h" #include "radioNoiseBlanker_F32.h"
void radioNoiseBlanker_F32::update(void) { void radioNoiseBlanker_F32::update(void) {
audio_block_f32_t *blockIn, *blockOut=NULL; audio_block_f32_t *blockIn0=NULL;
audio_block_f32_t *blockOut0=NULL;
audio_block_f32_t *blockIn1=NULL;
audio_block_f32_t *blockOut1=NULL;
uint16_t i; uint16_t i;
float32_t absSignal; float32_t absSignal;
// Get input block // <<Writable?? // Get input block // <<Writable??
blockIn = AudioStream_F32::receiveWritable_f32(0); blockIn0 = AudioStream_F32::receiveWritable_f32(0);
if (!blockIn) { if (!blockIn0) {
if(errorPrint) Serial.println("NB-ERR: No input memory"); if(errorPrint) Serial.println("NB-ERR: No input memory");
return; return;
} }
// Are we noise blanking? if(twoChannel) {
blockIn1 = AudioStream_F32::receiveWritable_f32(1);
if (!blockIn1) {
AudioStream_F32::release(blockIn0);
if(errorPrint) Serial.println("NB-ERR: No 1 input memory");
return;
}
}
// Are we not noise blanking?
if(! runNB) { if(! runNB) {
AudioStream_F32::transmit(blockIn, 0); // send the delayed or blanked data AudioStream_F32::transmit(blockIn0, 0); // send the unchanged data
AudioStream_F32::release (blockIn); AudioStream_F32::release (blockIn0);
AudioStream_F32::transmit(blockIn1, 1);
AudioStream_F32::release (blockIn1);
return; return;
} }
// Get a block for the output // Get a block for the output
blockOut = AudioStream_F32::allocate_f32(); blockOut0 = AudioStream_F32::allocate_f32();
if (!blockOut){ // Didn't have any if (!blockOut0) { // Didn't have any
if(errorPrint) Serial.println("NB-ERR: No output memory"); if(errorPrint) Serial.println("NB-ERR: No output memory");
AudioStream_F32::release(blockIn); AudioStream_F32::release(blockIn0);
if(twoChannel)
AudioStream_F32::release(blockIn1);
return;
}
if(twoChannel) {
blockOut1 = AudioStream_F32::allocate_f32();
if (!blockOut1) { // Didn't have any
if(errorPrint) Serial.println("NB-ERR: No output 1 memory");
AudioStream_F32::release(blockOut0);
AudioStream_F32::release(blockIn0);
AudioStream_F32::release(blockIn1);
return; return;
} }
}
// delayData[] always represents 256 points of I-F data. It is pre-gate and includes noise pulses. // delayData0[], and 1, always represents 256 points of I-F data. It is pre-gate and includes noise pulses.
// Go through new data, point i at a time, entering to delay line, looking // Go through new data, point i at a time, entering to delay line, looking
// for noise pulses. Then in same loop, move data to output buffer blockOut->data // for noise pulses. Then in same loop, move data to output buffer blockOut0->data
// based on whether gate is open or not. // based on whether gate is open or not.
for(i=0; i<block_size; i++) { for(i=0; i<block_size; i++) {
float32_t datai = blockIn->data[i]; // ith data float32_t datai0 = blockIn0->data[i]; // ith data
delayData[(i+in_index) & delayBufferMask] = datai; // Put ith data to circular delay buffer delayData0[(i+in_index) & delayBufferMask] = datai0; // Put ith data to circular delay buffer
if(twoChannel) {
float32_t datai1 = blockIn1->data[i];
delayData1[(i+in_index) & delayBufferMask] = datai1;
}
absSignal = fabsf(datai); // Rectified I-F // All control comes from the 0 input (not the 1 input)
runningSum += fabsf(datai); // Update by adding one rectified point absSignal = fabsf(datai0); // Rectified I-F
runningSum -= delayData[(i + in_index - RUNNING_SUM_SIZE) & delayBufferMask]; // & subtract one runningSum += absSignal; // Update by adding one rectified point
runningSum -= delayData0[(i + in_index - RUNNING_SUM_SIZE) & delayBufferMask]; // & subtract one
pulseTime++; // This keeps track of leading and trailing delays of the gate pulse pulseTime++; // This keeps track of leading and trailing delays of the gate pulse
if (absSignal > (threshold * runningSum)) { // A noise pulse event if (absSignal > (threshold * runningSum)) { // A noise pulse event
@ -74,15 +106,26 @@ void radioNoiseBlanker_F32::update(void) {
} }
} }
// Ready to enter I-F data to output, offset in time by "nAnticipation" // Ready to enter I-F data to output, offset in time by "nAnticipation"
if (pulseTime == -9999) if (pulseTime == -9999) {
blockOut->data[i] = delayData[(256 + i - nAnticipation) & delayBufferMask]; // Need 256?? blockOut0->data[i] = delayData0[(256 + i - nAnticipation) & delayBufferMask];
else // -nAnticipation < pulseTime < nDecay i.e., blanked out if(twoChannel)
blockOut->data[i] = 0.0f; blockOut1->data[i] = delayData1[(256 + i - nAnticipation) & delayBufferMask];
}
else { // -nAnticipation < pulseTime < nDecay i.e., blanked out
blockOut0->data[i] = 0.0f;
if(twoChannel)
blockOut1->data[i] = 0.0f;
}
} // End of loop point by point over input 128 data points } // End of loop point by point over input 128 data points
AudioStream_F32::release (blockIn); AudioStream_F32::release (blockIn0);
AudioStream_F32::transmit(blockOut, 0); // send the delayed or blanked data AudioStream_F32::transmit(blockOut0, 0); // send the delayed or blanked data
AudioStream_F32::release (blockOut); AudioStream_F32::release (blockOut0);
if(twoChannel) {
AudioStream_F32::release (blockIn1);
AudioStream_F32::transmit(blockOut1, 1); // send second "Q" channel
AudioStream_F32::release (blockOut1);
}
// Update pointer in_index to delay line for next 128 update // Update pointer in_index to delay line for next 128 update
in_index = (in_index + block_size) & delayBufferMask; in_index = (in_index + block_size) & delayBufferMask;

@ -51,6 +51,9 @@
* Examples: * Examples:
* TestNoiseBlanker1.ino 128 data for plotting and examination * TestNoiseBlanker1.ino 128 data for plotting and examination
* Time: Update() of 128 samples 32 microseconds * Time: Update() of 128 samples 32 microseconds
*
* Allow two channels, for I-Q receivers. Input 0 operates gate.
* Paths 0 & 1 are gated. 31 March 2021. Bob L.
*/ */
#ifndef _radio_noise_blanker_f32_h #ifndef _radio_noise_blanker_f32_h
@ -62,14 +65,14 @@
#define RUNNING_SUM_SIZE 125 #define RUNNING_SUM_SIZE 125
class radioNoiseBlanker_F32 : public AudioStream_F32 { class radioNoiseBlanker_F32 : public AudioStream_F32 {
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:2, outputs:2 //this line used for automatic generation of GUI node
//GUI: shortName: NoiseBlanker //GUI: shortName: NoiseBlanker
public: public:
// Option of AudioSettings_F32 change to block size (no sample rate dependent variables here): // Option of AudioSettings_F32 change to block size (no sample rate dependent variables here):
radioNoiseBlanker_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { radioNoiseBlanker_F32(void) : AudioStream_F32(2, inputQueueArray_f32) {
block_size = AUDIO_BLOCK_SAMPLES; block_size = AUDIO_BLOCK_SAMPLES;
} }
radioNoiseBlanker_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { radioNoiseBlanker_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {
block_size = settings.audio_block_samples; block_size = settings.audio_block_samples;
} }
@ -77,6 +80,11 @@ public:
runNB = _runNB; runNB = _runNB;
} }
// Channel 0 gates both Channel 0 & 1
void useTwoChannel(bool _2Ch) {
twoChannel = _2Ch;
}
void setNoiseBlanker(float32_t _threshold, uint16_t _nAnticipation, uint16_t _nDecay) { void setNoiseBlanker(float32_t _threshold, uint16_t _nAnticipation, uint16_t _nDecay) {
if (_threshold < 0.0) threshold = 0.0; if (_threshold < 0.0) threshold = 0.0;
else threshold = _threshold/(float32_t)RUNNING_SUM_SIZE; else threshold = _threshold/(float32_t)RUNNING_SUM_SIZE;
@ -99,22 +107,23 @@ public:
private: private:
uint16_t block_size = AUDIO_BLOCK_SAMPLES; uint16_t block_size = AUDIO_BLOCK_SAMPLES;
// Input data pointers // Input data pointers
audio_block_f32_t *inputQueueArray_f32[1]; audio_block_f32_t *inputQueueArray_f32[2];
// Control error printing in update() 0=No print // Control error printing in update() 0=No print
uint16_t errorPrint = 0; uint16_t errorPrint = 0;
// To look ahead we need a delay of up to 128 samples. Too much removes good data. // To look ahead we need a delay of up to 256 samples. Too much removes good data.
// Too little enters noise pulse data to the output. This can be a simple circular // Too little enters noise pulse data to the output. This can be a simple circular
// buffer if we make the buffer a power of 2 in length and binary-truncate the index. // buffer if we make the buffer a power of 2 in length and binary-truncate the index.
// Choose 2^8 = 256. // Choose 2^8 = 256.
float32_t delayData[256]; // The circular delay line float32_t delayData0[256]; // The circular delay lines
float32_t delayData1[256];
uint16_t in_index = 0; // Pointer to next block update entry uint16_t in_index = 0; // Pointer to next block update entry
// And a mask to make the circular buffer limit to a power of 2 // And a mask to make the circular buffer limit to a power of 2
uint16_t delayBufferMask = 0X00FF; uint16_t delayBufferMask = 0X00FF;
// Three variables to allow .INO control of Noise Blanker // Three variables to allow .INO control of Noise Blanker
float32_t threshold = 1.0E6f; // Start disabled float32_t threshold = 1.0E3f; // Start disabled
uint16_t nAnticipation = 5; uint16_t nAnticipation = 5;
uint16_t nDecay = 8; uint16_t nDecay = 8;
@ -122,5 +131,6 @@ private:
bool gateOn = true; // Signals going through NB bool gateOn = true; // Signals going through NB
float32_t runningSum = 0.0; float32_t runningSum = 0.0;
bool runNB = false; bool runNB = false;
bool twoChannel = false; // Activates 2 channels for I-Q.
}; };
#endif #endif

Loading…
Cancel
Save