Add AudioFilter 90Deg and RadioIQMixer plus support for these.

pull/11/head
boblark 5 years ago
parent 825f50668b
commit ae99656d29
  1. 235
      AudioEffectDelay_OA_F32.cpp
  2. 152
      AudioEffectDelay_OA_F32.h
  3. 95
      AudioFilter90Deg_F32.cpp
  4. 153
      AudioFilter90Deg_F32.h
  5. 3
      OpenAudio_ArduinoLibrary.h
  6. 106
      RadioIQMixer_F32.cpp
  7. 138
      RadioIQMixer_F32.h
  8. 290
      examples/FineFreqShift_OA/FineFreqShift_OA.ino
  9. 123
      examples/FineFreqShift_OA/hilbert121A.h
  10. 253
      examples/FineFreqShift_OA/hilbert251A.h
  11. 114
      mathDSP_F32.cpp
  12. 50
      mathDSP_F32.h
  13. 6
      readme.md
  14. 98
      sinTable512_f32.h

@ -0,0 +1,235 @@
/*
*
*
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* Extended by Chip Audette, Open Audio, April 2018
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <Arduino.h>
#include "AudioEffectDelay_OA_F32.h"
void AudioEffectDelay_OA_F32::update(void)
{
//Serial.println("AudioEffectDelay_OA_F32: update()...");
receiveIncomingData(); //put the in-coming audio data into the queue
discardUnneededBlocksFromQueue(); //clear out queued data this is no longer needed
transmitOutgoingData(); //put the queued data into the output
return;
}
void AudioEffectDelay_OA_F32::receiveIncomingData(void) {
//Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: starting...");
//prepare the receiving queue
uint16_t head = headindex; //what block to write to
uint16_t tail = tailindex; //what block to read from
if (queue[head] == NULL) {
//if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Allocating queue[head].");
queue[head] = allocate_f32();
if (queue[head] == NULL) {
//if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 1. Returning.");
return;
}
}
//prepare target memory nto which we'll copy the incoming data into the queue
int dest_ind = writeposition; //inclusive
if (dest_ind >= (queue[head]->full_length)) {
head++; dest_ind = 0;
if (head >= DELAY_QUEUE_SIZE) head = 0;
if (queue[head] != NULL) {
if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE) tail = 0; }
AudioStream_F32::release(queue[head]);
queue[head]=NULL;
}
}
if (queue[head]==NULL) {
queue[head] = allocate_f32();
if (queue[head] == NULL) {
if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 2. Returning.");
return;
}
}
//receive the in-coming audio data block
audio_block_f32_t *input = receiveReadOnly_f32();
if (input == NULL) {
//if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Input data is NULL. Returning.");
return;
}
int n_copy = input->length;
last_received_block_id = input->id;
// Now we'll loop over the individual samples of the in-coming data
float32_t *dest = queue[head]->data;
float32_t *source = input->data;
int end_write = dest_ind + n_copy; //this may go past the end of the destination array
int end_loop = min(end_write,(int)(queue[head]->full_length)); //limit to the end of the array
int src_count=0, dest_count=dest_ind;
for (int i=dest_ind; i<end_loop; i++) dest[dest_count++] = source[src_count++];
//finish writing taking data from the next queue buffer
if (src_count < n_copy) { // there's still more input data to copy...but we need to roll-over to a new input queue
head++; dest_ind = 0;
if (head >= DELAY_QUEUE_SIZE) head = 0;
if (queue[head] != NULL) {
if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE) tail = 0; }
AudioStream_F32::release(queue[head]);
queue[head]=NULL;
}
if (queue[head]==NULL) {
queue[head] = allocate_f32();
if (queue[head] == NULL) {
Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 3. Returning.");
AudioStream_F32::release(input);
return;
}
}
float32_t *dest = queue[head]->data;
end_loop = end_write - (queue[head]->full_length);
dest_count = dest_ind;
for (int i=dest_ind; i < end_loop; i++) dest[dest_count++]=source[src_count++];
}
AudioStream_F32::release(input);
writeposition = dest_count;
headindex = head;
tailindex = tail;
return;
}
void AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue(void) {
uint16_t head = headindex; //what block to write to
uint16_t tail = tailindex; //last useful block of data
uint32_t count;
// discard unneeded blocks from the queue
if (head >= tail) {
count = head - tail;
} else {
count = DELAY_QUEUE_SIZE + head - tail;
}
/* if (head>0) {
Serial.print("AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue: head, tail, count, maxblocks, DELAY_QUEUE_SIZE: ");
Serial.print(head); Serial.print(", ");
Serial.print(tail); Serial.print(", ");
Serial.print(count); Serial.print(", ");
Serial.print(maxblocks); Serial.print(", ");
Serial.print(DELAY_QUEUE_SIZE); Serial.print(", ");
Serial.println();
} */
if (count > maxblocks) {
count -= maxblocks;
do {
if (queue[tail] != NULL) {
AudioStream_F32::release(queue[tail]);
queue[tail] = NULL;
}
if (++tail >= DELAY_QUEUE_SIZE) tail = 0;
} while (--count > 0);
}
tailindex = tail;
}
void AudioEffectDelay_OA_F32::transmitOutgoingData(void) {
uint16_t head = headindex; //what block to write to
//uint16_t tail = tailindex; //last useful block of data
audio_block_f32_t *output;
int channel; //, index, prev, offset;
//const float32_t *src, *end;
//float32_t *dst;
// transmit the delayed outputs using queue data
for (channel = 0; channel < 8; channel++) {
if (!(activemask & (1<<channel))) continue;
output = allocate_f32();
if (!output) continue;
//figure out where to start pulling the data samples from
uint32_t ref_samp_long = (head*AUDIO_BLOCK_SIZE_F32) + writeposition; //note that writepoisition has already been
//incremented by the block length by the
//receiveIncomingData method. We'll adjust it next
uint32_t offset_samp = delay_samps[channel]+output->length;
if (ref_samp_long < offset_samp) { //when (ref_samp_long - offset_samp) goes negative, the uint32_t will fail, so we do this logic check
ref_samp_long = ref_samp_long + (((uint32_t)(DELAY_QUEUE_SIZE))*((uint32_t)AUDIO_BLOCK_SIZE_F32));
}
ref_samp_long = ref_samp_long - offset_samp;
uint16_t source_queue_ind = (uint16_t)(ref_samp_long / ((uint32_t)AUDIO_BLOCK_SIZE_F32));
int source_samp = (int)(ref_samp_long - (((uint32_t)source_queue_ind)*((uint32_t)AUDIO_BLOCK_SIZE_F32)));
//pull the data from the first source data block
int dest_counter=0;
int n_output = output->length;
float32_t *dest = output->data;
audio_block_f32_t *source_block = queue[source_queue_ind];
if (source_block == NULL) {
//fill destination with zeros for this source block
int Iend = min(source_samp+n_output,AUDIO_BLOCK_SIZE_F32);
for (int Isource = source_samp; Isource < Iend; Isource++) {
dest[dest_counter++]=0.0;
}
} else {
//fill destination with this source block's values
float32_t *source = source_block->data;
int Iend = min(source_samp+n_output,AUDIO_BLOCK_SIZE_F32);
for (int Isource = source_samp; Isource < Iend; Isource++) {
dest[dest_counter++]=source[Isource];
}
}
//pull the data from the second source data block, if needed
if (dest_counter < n_output) {
//yes, we need to keep filling the output
int Iend = n_output - dest_counter; //how many more data points do we need
source_queue_ind++; source_samp = 0; //which source block will we draw from (and reset the source sample counter)
if (source_queue_ind >= DELAY_QUEUE_SIZE) source_queue_ind = 0; //wrap around on our source black.
source_block = queue[source_queue_ind]; //get the source block
if (source_block == NULL) { //does it have data?
//no, it doesn't have data. fill destination with zeros
for (int Isource = source_samp; Isource < Iend; Isource++) {
dest[dest_counter++] = 0.0;
}
} else {
//source block does have data. use this block's values
float32_t *source = source_block->data;
for (int Isource = source_samp; Isource < Iend; Isource++) {
dest[dest_counter++]=source[Isource];
}
}
}
//add the id of the last received audio block
output->id = last_received_block_id;
//transmit and release
AudioStream_F32::transmit(output, channel);
AudioStream_F32::release(output);
}
}

@ -0,0 +1,152 @@
/*
* AudioEffectDelay_OA_F32.h
*
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* Extended by Chip Audette, Open Audio, Apr 2018
* Isolated names for Open Audio (F32) RSL June 2020
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef AudioEffecDelay_OA_F32_h_
#define AudioEffecDelay_OA_F32_h_
#include "Arduino.h"
#include "AudioStream_F32.h"
#include "utility/dspinst.h"
#define AUDIO_BLOCK_SIZE_F32 AUDIO_BLOCK_SAMPLES //what is the maximum length of the F32 audio blocks
// Are these too big??? I think the same as I16---half as much? <<<<<<<<<<<<<<<<
#if defined(__IMXRT1062__)
// 4.00 second maximum on Teensy 4.0
#define DELAY_QUEUE_SIZE (176512 / AUDIO_BLOCK_SAMPLES)
#elif defined(__MK66FX1M0__)
// 2.41 second maximum on Teensy 3.6
#define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SIZE_F32)
#elif defined(__MK64FX512__)
// 1.67 second maximum on Teensy 3.5
#define DELAY_QUEUE_SIZE (73728 / AUDIO_BLOCK_SIZE_F32)
#elif defined(__MK20DX256__)
// 0.45 second maximum on Teensy 3.1 & 3.2
#define DELAY_QUEUE_SIZE (19826 / AUDIO_BLOCK_SIZE_F32)
#else
// 0.14 second maximum on Teensy 3.0
#define DELAY_QUEUE_SIZE (6144 / AUDIO_BLOCK_SIZE_F32)
#endif
class AudioEffectDelay_OA_F32 : public AudioStream_F32
{
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName:delay
public:
AudioEffectDelay_OA_F32() : AudioStream_F32(1, inputQueueArray) {
activemask = 0;
headindex = 0;
tailindex = 0;
maxblocks = 0;
memset(queue, 0, sizeof(queue));
}
AudioEffectDelay_OA_F32(const AudioSettings_F32 &settings) :
AudioStream_F32(1,inputQueueArray) {
activemask = 0;
headindex = 0;
tailindex = 0;
maxblocks = 0;
memset(queue, 0, sizeof(queue));
setSampleRate_Hz(settings.sample_rate_Hz);
}
void setSampleRate_Hz(float _fs_Hz) {
//Serial.print("AudioEffectDelay_OA_F32: setSampleRate_Hz to ");
//Serial.println(_fs_Hz);
sampleRate_Hz = _fs_Hz;
//audio_block_len_samples = block_size; //this is the actual size that is being used in each audio_block_f32
}
void delay(uint8_t channel, float milliseconds) {
if (channel >= 8) return;
if (milliseconds < 0.0) milliseconds = 0.0;
uint32_t n = (milliseconds*(sampleRate_Hz/1000.0))+0.5;
uint32_t nmax = AUDIO_BLOCK_SIZE_F32 * (DELAY_QUEUE_SIZE-1);
if (n > nmax) n = nmax;
uint32_t blks = (n + (AUDIO_BLOCK_SIZE_F32-1)) / AUDIO_BLOCK_SIZE_F32 + 1;
if (!(activemask & (1<<channel))) {
// enabling a previously disabled channel
delay_samps[channel] = n;
if (blks > maxblocks) maxblocks = blks;
activemask |= (1<<channel);
} else {
if (n > delay_samps[channel]) {
// new delay is greater than previous setting
if (blks > maxblocks) maxblocks = blks;
delay_samps[channel] = n;
} else {
// new delay is less than previous setting
delay_samps[channel] = n;
recompute_maxblocks();
}
}
}
void disable(uint8_t channel) {
if (channel >= 8) return;
// diable this channel
activemask &= ~(1<<channel);
// recompute maxblocks for remaining enabled channels
recompute_maxblocks();
}
virtual void update(void);
private:
void recompute_maxblocks(void) {
uint32_t max=0;
uint32_t channel = 0;
do {
if (activemask & (1<<channel)) {
uint32_t n = delay_samps[channel];
n = (n + (AUDIO_BLOCK_SIZE_F32-1)) / AUDIO_BLOCK_SIZE_F32 + 1;
if (n > max) max = n;
}
} while(++channel < 8);
maxblocks = max;
}
uint8_t activemask; // which output channels are active
uint16_t headindex; // head index (incoming) data in queue
uint16_t tailindex; // tail index (outgoing) data from queue
uint16_t maxblocks; // number of blocks needed in queue
//#if DELAY_QUEUE_SIZE * AUDIO_BLOCK_SAMPLES < 65535
// uint16_t writeposition;
// uint16_t delay_samps[8]; // # of samples to delay for each channel
//#else
int writeposition; //position within current head buffer in the queue
uint32_t delay_samps[8]; // # of samples to delay for each channel
//#endif
audio_block_f32_t *queue[DELAY_QUEUE_SIZE];
audio_block_f32_t *inputQueueArray[1];
float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h??
//int audio_block_len_samples = AUDIO_BLOCK_SAMPLES;
void receiveIncomingData(void);
void discardUnneededBlocksFromQueue(void);
void transmitOutgoingData(void);
unsigned long last_received_block_id = 0;
};
#endif

@ -0,0 +1,95 @@
/*
* AudioFilter90Deg_F32.cpp
*
* 22 March 2020
* Bob Larkin, in support of the library:
* Chip Audette, OpenAudio, Apr 2017
* -------------------
* There are two channels that update synchronously, but operate
* independently. The I-channel, coresponding to a "Left"
* audio channel, is filtered with an N coefficient
* Hilbert Transform in FIR form.
* The Q-channel, or "Right" channel, is simply
* delayed by the (N-1)/2 sample periods. The phase between
* the two outputs is 90-Degrees. The amplitude response cuts off low
* frequencies and depends on N.
*
* The I-channel FIR is a Hilbert Transform and this has every other term 0.
* These need not be multiplied-and-accumulated, but for now, we do. By the
* time the indexes are calculated, it may be quicker to process everything.
* Additionally, to not require a half-sample delay, the number of terms in
* the Hilbert FIR needs to be odd.
*
* MIT License, Use at your own risk.
*/
#include "AudioFilter90Deg_F32.h"
void AudioFilter90Deg_F32::update(void)
{
audio_block_f32_t *block_i, *block_q, *blockOut_i=NULL;
uint16_t i;
// Get first input, i, that will be filtered
block_i = AudioStream_F32::receiveWritable_f32(0);
if (!block_i) {
if(errorPrint) Serial.println("FIL90-ERR: No i input memory");
return;
}
// Get second input, q, that will be delayed
block_q = AudioStream_F32::receiveWritable_f32(1);
if (!block_q){
if(errorPrint) Serial.println("FIL90-ERR: No q input memory");
AudioStream_F32::release(block_i);
return;
}
// If there's no coefficient table, give up.
if (coeff_p == NULL) {
if(errorPrint) Serial.println("FIL90-ERR: No Hilbert FIR coefficients");
AudioStream_F32::release(block_i);
AudioStream_F32::release(block_q);
return;
}
// Try to get a block for the FIR output
blockOut_i = AudioStream_F32::allocate_f32();
if (!blockOut_i){ // Didn't have any
if(errorPrint) Serial.println("FIL90-ERR: No out 0 block");
AudioStream_F32::release(block_i);
AudioStream_F32::release(block_q);
return;
}
// Apply the Hilbert transform FIR. This includes multiplies by zero.
// Is there any way to play with the indexes and not multiply by zero
// without spending more time than is saved? How about something like
// pick right nn, then for(j=0; j<fir_length; j+=2) {sum += coef[j] * data[j+nn];}
arm_fir_f32(&Ph90Deg_inst, block_i->data, blockOut_i->data, block_i->length);
AudioStream_F32::release(block_i); // Not needed further
// Now enter block_size points to the delay loop and move earlier points to re-used block
float32_t *pin, *pout;
pin = &(block_q->data[0]); // point to beginning of block_q
for(i=0; i<block_size; i++) { // Let it wrap around at 256
in_index++;
in_index = delayBufferMask & in_index; // Index into delay buffer, wrapping
delayData[in_index] = *pin++; // Put ith q data to circular delay buffer
}
// And similarly, output with a delay, i.e., a buffer offset
pout = &(block_q->data[0]); // Re-use the input block
for(i=0; i<block_size; i++) {
out_index++;
out_index = delayBufferMask & out_index; // Index into delay buffer, wrapping
*pout++ = delayData[out_index];
}
//transmit the data
AudioStream_F32::transmit(blockOut_i, 0); // send the FIR outputs
AudioStream_F32::release (blockOut_i);
AudioStream_F32::transmit(block_q, 1); // and the delayed outputs
AudioStream_F32::release (block_q);
}

@ -0,0 +1,153 @@
/*
* AudioFilter90Deg_F32.h
* 22 March 2020 Bob Larkin
* Parts are based on Open Audio FIR filter by Chip Audette:
*
* Chip Audette (OpenAudio) Feb 2017
* - Building from AudioFilterFIR from Teensy Audio Library
* (AudioFilterFIR credited to Pete (El Supremo))
* Copyright (c) 2020 Bob Larkin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* This consists of two uncoupled paths that almost have the same amplitude gain
* but differ in phase by exactly 90 degrees. See AudioFilter90Deg_F32.cpp
* The number of coefficients is an odd number for the FIR Hilbert transform
* as that produces an easily achievable integer sample period delay. In
* float, the ARM FIR library routine will handle odd numbers.\, so no zero padding
* is needed.
*
* No default Hilbert Transform is provided, as it is highly application dependent.
* The number of coefficients is an odd number with a maximum of 250. The Iowa
* Hills program can design a Hilbert Transform filter. Use begin(*pCoeff, nCoeff)
* in the .INO to initialize this block.
*
* Status: Tested T3.6 and T4.0. No known bugs.
* Functions:
* begin(*pCoeff, nCoeff); Initializes this block, with:
* pCoeff = pointer to array of F32 Hilbert Transform coefficients
* nCoeff = uint16_t number of Hilbert transform coefficients
* showError(e); Turns error printing in update() on (e=1) and off (e=0). For debug.
* Examples:
* ReceiverPart1.ino
* ReceiverPart2.ino
* Time: Depends on size of Hilbert FIR. Time for main body of update() including
* Hilbert FIR and compensating delay, 128 data block, running on Teensy 3.6 is:
* 19 tap Hilbert (including 0's) 74 microseconds
* 121 tap Hilbert (including 0's) 324 microseconds
* 251 tap Hilbert (including 0's) 646 microseconds
* Same 121 tap Hilbert on T4.0 is 57 microseconds per update()
* Same 251 tap Hilbert on T4.0 is 114 microseconds per update()
*/
#ifndef _filter_90deg_f32_h
#define _filter_90deg_f32_h
#include "AudioStream_F32.h"
#include "arm_math.h"
#define TEST_TIME_90D 1
// Following supports a maximum FIR Hilbert Transform of 251
#define HILBERT_MAX_COEFFS 251
class AudioFilter90Deg_F32 : public AudioStream_F32 {
//GUI: inputs:2, outputs:2 //this line used for automatic generation of GUI node
//GUI: shortName: 90DegPhase
public:
// Option of AudioSettings_F32 change to block size (no sample rate dependent variables here):
AudioFilter90Deg_F32(void) : AudioStream_F32(2, inputQueueArray_f32) {
block_size = AUDIO_BLOCK_SAMPLES;
}
AudioFilter90Deg_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {
block_size = settings.audio_block_samples;
}
// Initialize the 90Deg by giving it the filter coefficients and number of coefficients
// Then the delay line for the q (Right) channel is initialized
void begin(const float32_t *cp, const int _n_coeffs) {
coeff_p = cp;
n_coeffs = _n_coeffs;
// Initialize FIR instance (ARM DSP Math Library) (for f32 the return is always void)
if (coeff_p!=NULL && n_coeffs<252) {
arm_fir_init_f32(&Ph90Deg_inst, n_coeffs, (float32_t *)coeff_p, &StateF32[0], block_size);
}
else {
coeff_p = NULL; // Stops further FIR filtering for Hilbert
// Serial.println("Hilbert: Missing FIR Coefficients or number > 251");
}
// For the equalizing delay in q, if n_coeffs==19, n_delay=9
// Max of 251 coeffs needs a delay of 125 sample periods.
n_delay = (uint8_t)((n_coeffs-1)/2);
in_index = n_delay;
out_index = 0;
for (uint16_t i=0; i<256; i++){
delayData[i] = 0.0F;
}
} // End of begin()
void showError(uint16_t e) {
errorPrint = e;
}
void update(void);
private:
uint16_t block_size = AUDIO_BLOCK_SAMPLES;
// Two input data pointers
audio_block_f32_t *inputQueueArray_f32[2];
// One output pointer
audio_block_f32_t *blockOut_i;
#if TEST_TIME_90D
// *Temporary* - allows measuring time in microseconds for each part of the update()
elapsedMicros tElapse;
int32_t iitt = 999000; // count up to a million during startup
#endif
// Control error printing in update() 0=No print
uint16_t errorPrint = 0;
//float32_t tmpHil[5]={0.0, 1.0, 0.0, -1.0, 0.0}; coeff_p = &tmpHil[0];
// pointer to current coefficients or NULL
const float32_t *coeff_p = NULL;
uint16_t n_coeffs = 0;
// Variables for the delayed q-channel:
// For the q-channel, we need a delay of ((Ncoeff - 1) / 2) samples. This
// is 9 delay for 19 coefficient FIR. This can be implemented as a simple circular
// buffer if we make the buffer a power of 2 in length and binary-truncate the index.
// Choose 2^8 = 256. For a 251 long Hilbert this wastes 256-128-125 = 3, but
// more for shorter Hilberts.
float32_t delayData[256]; // The circular delay line
uint16_t in_index;
uint16_t out_index;
// And a mask to make the circular buffer limit to a power of 2
uint16_t delayBufferMask = 0X00FF;
uint16_t n_delay;
// ARM DSP Math library filter instance
arm_fir_instance_f32 Ph90Deg_inst;
float32_t StateF32[AUDIO_BLOCK_SAMPLES + HILBERT_MAX_COEFFS];
};
#endif

@ -32,3 +32,6 @@
#include "AudioSwitch_OA_F32.h"
#include "FFT_Overlapped_OA_F32.h"
#include "AudioEffectFreqShiftFD_OA_F32.h"
#include "AudioEffectDelay_OA_F32.h"
#include "RadioIQMixer_F32.h"
#include "AudioFilter90Deg_F32.h"

@ -0,0 +1,106 @@
/*
* RadioIQMixer_F32.cpp
*
* 22 March 2020
* Bob Larkin, in support of the library:
* Chip Audette, OpenAudio, Apr 2017
* -------------------
* A single signal channel comes in and is multiplied (mixed) with a sin
* and cos of the same frequency. The pair of mixer outputs are
* referred to as i and q. The conversion in frequency is either
* up or down, and a pair of filters on i and q determine which is allow
* to pass to the output.
*
* The sin/cos LO is from synth_sin_cos_f32.cpp See that for details.
*
* There are two then two outputs.
*
* MIT License, Use at your own risk.
*/
#include "RadioIQMixer_F32.h"
// 513 values of the sine wave in a float array:
#include "sinTable512_f32.h"
void RadioIQMixer_F32::update(void) {
audio_block_f32_t *blockIn, *blockOut_i=NULL, *blockOut_q=NULL;
uint16_t index, i;
float32_t a, b, deltaPhase, phaseC;
// Get first input, i, that will be filtered
blockIn = AudioStream_F32::receiveWritable_f32(0);
if (!blockIn) {
if(errorPrintIQM) Serial.println("IQMIXER-ERR: No input memory");
return;
}
// Try to get a pair of blocks for the IQ output
blockOut_i = AudioStream_F32::allocate_f32();
if (!blockOut_i){ // Didn't have any
if(errorPrintIQM) Serial.println("IQMIXER-ERR: No I output memory");
AudioStream_F32::release(blockIn);
return;
}
blockOut_q = AudioStream_F32::allocate_f32();
if (!blockOut_q){
if(errorPrintIQM) Serial.println("IQMIXER-ERR: No Q output memory");
AudioStream_F32::release(blockIn);
AudioStream_F32::release(blockOut_i);
return;
}
// doSimple has amplitude (-1, 1) and sin/cos differ by 90.00 degrees.
if (doSimple) {
for (i=0; i < block_size; i++) {
phaseS += phaseIncrement;
if (phaseS > 512.0f)
phaseS -= 512.0f;
index = (uint16_t) phaseS;
deltaPhase = phaseS -(float32_t) index;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index];
b = sinTable512_f32[index+1];
// Linear interpolation and multiplying (DBMixer) with input
blockOut_i->data[i] = blockIn->data[i] * (a + 0.001953125*(b-a)*deltaPhase);
/* Repeat for cosine by adding 90 degrees phase */
index = (index + 128) & 0x01ff;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index];
b = sinTable512_f32[index+1];
/* deltaPhase will be the same as used for sin */
blockOut_q->data[i] = blockIn->data[i]*(a + 0.001953125*(b-a)*deltaPhase);
}
}
else { // Do a more flexible update, i.e., not doSimple
for (i=0; i < block_size; i++) {
phaseS += phaseIncrement;
if (phaseS > 512.0f) phaseS -= 512.0f;
index = (uint16_t) phaseS;
deltaPhase = phaseS -(float32_t) index;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index];
b = sinTable512_f32[index+1];
// We now have a sine value, so multiply with the input data and save
// Linear interpolate sine and multiply with the input and amplitude (about 1.0)
blockOut_i->data[i] = amplitude_pk * blockIn->data[i] * (a + 0.001953125*(b-a)*deltaPhase);
/* Shift forward phaseS_C and get cos. First, the calculation of index of the table */
phaseC = phaseS + phaseS_C;
if (phaseC > 512.0f) phaseC -= 512.0f;
index = (uint16_t) phaseC;
deltaPhase = phaseC -(float32_t) index;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index];
b = sinTable512_f32[index+1];
// Same as sin, but leave amplitude of LO at +/- 1.0
blockOut_q->data[i] = blockIn->data[i] * (a + 0.001953125*(b-a)*deltaPhase);
}
}
AudioStream_F32::release(blockIn); // Done with this
//transmit the data
AudioStream_F32::transmit(blockOut_i, 0); // send the I outputs
AudioStream_F32::release(blockOut_i);
AudioStream_F32::transmit(blockOut_q, 1); // and the Q outputs
AudioStream_F32::release(blockOut_q);
}

@ -0,0 +1,138 @@
/*
* RadioIQMixer_F32
* 8 April 2020 Bob Larkin
* With much credit to:
* Chip Audette (OpenAudio) Feb 2017
* and of course, to PJRC for the Teensy and Teensy Audio Library
*
* A basic building block is a pair of mixers fed in parallel with the
* LO going to the mixers at the same frequency, but differing in phase
* by 90 degrees. This provides two outputs I and Q that are offset in
* frequency but also 90 degrees apart in phase. The LO are included
* in the block, but there are no post-mixing filters.
*
* The frequency is set by .frequency(float freq_Hz)
* Particularly for use in transmitting, there is provision for varying
* the phase between the sine and cosine oscillators. Technically this is no
* longer sin and cos, but that is what real hardware needs.
*
* The output levels are 0.5 times the input level.
*
* Status: Tested in doSimple==1
* Tested in FineFreqShift_OA.ino, T3.6 and T4.0
*
* Inputs: 0 is signal
* Outputs: 0 is I 1 is Q
*
* Functions, available during operation:
* void frequency(float32_t fr) Sets BFO frequency Hz
* void iqmPhaseS(float32_t ps) Sets Phase of Sine in radians
* void phaseS_C_r(float32_t pc) Sets relative phase of Cosine in radians, approximately pi/2
* void amplitudeC(float32_t a) Sets relative amplitude of Sine, approximately 1.0
* void useSimple(bool s) Faster if 1, but no phase/amplitude adjustment
* void setSampleRate_Hz(float32_t fs_Hz) Allows dynamic sample rate change for this function
*
* Time: T3.6 For an update of a 128 sample block, doSimple=1, 46 microseconds
* T4.0 For an update of a 128 sample block, doSimple=1, 20 microseconds
*/
#ifndef _radioIQMixer_f32_h
#define _radioIQMixer_f32_h
#include "AudioStream_F32.h"
#include "arm_math.h"
#include "mathDSP_F32.h"
class RadioIQMixer_F32 : public AudioStream_F32 {
//GUI: inputs:2, outputs:2 //this line used for automatic generation of GUI node
//GUI: shortName: IQMixer
public:
// Option of AudioSettings_F32 change to block size or sample rate:
RadioIQMixer_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {
// Defaults
}
RadioIQMixer_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) {
setSampleRate_Hz(settings.sample_rate_Hz);
block_size = settings.audio_block_samples;
}
void frequency(float32_t fr) { // Frequency in Hz
freq = fr;
if (freq < 0.0f) freq = 0.0f;
else if (freq > sample_rate_Hz/2.0f) freq = sample_rate_Hz/2.0f;
phaseIncrement = 512.0f * freq / sample_rate_Hz;
}
/* Externally, phase comes in the range (0,2*M_PI) keeping with C math functions
* Internally, the full circle is represented as (0.0, 512.0). This is
* convenient for finding the entry to the sine table.
*/
void iqmPhaseS(float32_t a) {
while (a < 0.0f) a += MF_TWOPI;
while (a > MF_TWOPI) a -= MF_TWOPI;
phaseS = 512.0f * a / MF_TWOPI;
doSimple = false;
return;
}
// phaseS_C_r is the number of radians that the cosine output leads the
// sine output. The default is M_PI_2 = pi/2 = 1.57079633 radians,
// corresponding to 90.00 degrees cosine leading sine.
void iqmPhaseS_C(float32_t a) {
while (a < 0.0f) a += MF_TWOPI;
while (a > MF_TWOPI) a -= MF_TWOPI;
// Internally a full circle is 512.00 of phase
phaseS_C = 512.0f * a / MF_TWOPI;
doSimple = false;
return;
}
// The amplitude, a, is the peak, as in zero-to-peak. This produces outputs
// ranging from -a to +a. Both outputs are the same amplitude.
void iqmAmplitude(float32_t a) {
amplitude_pk = a;
doSimple = false;
return;
}
// Speed up calculations by setting phaseS_C=90deg, amplitude=1
void useSimple(bool s) {
doSimple = s;
if(doSimple) {
phaseS_C = 128.0f;
amplitude_pk = 1.0f;
}
return;
}
void setSampleRate_Hz(float32_t fs_Hz) {
// Check freq range
if (freq > sample_rate_Hz/2.0f) freq = sample_rate_Hz/2.f;
// update phase increment for new frequency
phaseIncrement = 512.0f * freq / fs_Hz;
}
void showError(uint16_t e) { // Serial.print errors in update()
errorPrintIQM = e;
}
virtual void update(void);
private:
audio_block_f32_t *inputQueueArray_f32[1];
float32_t freq = 1000.0f;
float32_t phaseS = 0.0f;
float32_t phaseS_C = 128.00; // 512.00 is 360 degrees
float32_t amplitude_pk = 1.0f;
float32_t sample_rate_Hz = AUDIO_SAMPLE_RATE_EXACT;
float32_t phaseIncrement = 512.00f * freq /sample_rate_Hz;
uint16_t block_size = AUDIO_BLOCK_SAMPLES;
uint16_t errorPrintIQM = 0; // Normally off
// if only freq() is used, the complexities of phase, phaseS_C,
// and amplitude are not used, speeding up the sin and cos:
bool doSimple = true;
};
#endif

@ -0,0 +1,290 @@
/* FineFreqShift_OA.ino
*
* 1 Hz Resolution Frequency Shift and Synthetic Stereo
*
* Written for Open Audio Bob Larkin June 2020
*
* This sketch demonstrates the use of dual multipliers along with broad-band 90-degree
* phase shifters (Hilbert transforms) and sine/cosine generator to shift all frequencies
* by a fixed amount. The shift amount is set by a digital oscillator frequency
* and can be made arbitrarily precise. Thus the name "Fine Frequency Shifter."
*
* Two blocks do most of the work of shifting the frequency band. The RadioIQMixer_F32
* has a pair of multipliers that have one of their inputs from an oscillator that produces
* a sine and a cosine waveform of the same frequency. This oscillator phase difference
* is transferred to the output producing phase differences of 90-degrees. The multipliers,
* called double-balanced mixers in the analog world, produce sum and difference frequencies
* but the original input frequencies are suppressed. In the analog case, this suppression
* is limited by circuit balance, but for digital multipliers, the suppression is
* close-to-perfect. Thus the outputs of the I-Q Mixers is a pair of frequency sum and
* difference signals. Since the oscillator can be a low frequency, such as 100 Hz,
* the frequency band of sum and difference signals will include much overlap.
*
* The next block, the "AudioFilter90Deg_F32" applies a constant phase difference between
* the two inputs, which are then added or subtracted. That turns out to cause either the
* frequency sum or difference to be cancelled out. At that point only one audio signal
* remains and it is frequency shifted by the oscillator frequency.
*
* This INO also demonstrates the conversion of monaural to stereo sound by
* delaying one channel. This technique has been used for many years to de-correlate
* monaural noise before sending it to both ears. This can engage the the human brain
* to hear correlated signals better. It also adds a stereo presence to monaural voice
* that is pleasant to hear. It is tossed in here for experimentation. In general,
* delays up to 20 msec give the illusion of presence, whereas larger delays start to
* sound like loud echos! Play with it. Headphones vs. speakers change the perception.
*
* Control is done over the "USB Serial" using the Serial Monitor of the Arduino
* IDE. The commands control most functions and a list can be seen by typing
* "h<enter>", or looking in the listing below, at printHelp().
*
* Refs - The phasing method of SSB generation goes way back.
* https://en.wikipedia.org/wiki/Single-sideband_modulation#Hartley_modulator
* The precision of DSP makes it practical for overlapping input and output bands.
* I first encountered this in conversation with friend Johan Forrer:
* https://www.arrl.org/files/file/Technology/tis/info/pdf/9609x008.pdf
* I need to find the German description of delay stereo from the 1980's. Both audio
* shifting and delay stereo were put into the DSP-10 audio processor:
* http://www.janbob.com/electron/dsp10/dsp10.htm
*
* Tested OK for Teensy 3.6 and 4.0.
* For settings:
* sample rate (Hz) = 44117.00
* block size (samples) = 128
* N_FFT = 512
* Hilbert 251 taps
* CPU Cur/Peak, Teensy 3.6: 27.28%/27.49%
* CPU Cur/Peak, Teensy 4.0: 5.82%/5.84%
* Memory useage is 4 for I16 Memory
* Memory for F32 is 6 plus 1 more for every 2.9 mSec of Stereo Delay.
*
* This INO sketch is in the public domain.
*/
#include "Audio.h"
#include "AudioStream_F32.h"
#include "OpenAudio_ArduinoLibrary.h"
//set the sample rate and block size
const float sample_rate_Hz = 44117.f;
const int audio_block_samples = 128;
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);
//create audio library objects for handling the audio
AudioInputI2S i2sIn; // This I16 input/output is T4.x compatible
AudioConvert_I16toF32 cnvrt1; // Convert to float
RadioIQMixer_F32 iqmixer1;
AudioFilter90Deg_F32 hilbert1;
AudioMixer4_F32 sum1; // Summing node for the SSB receiver
AudioFilterFIR_F32 fir1; // Low Pass Filter to frequency limit the SSB
AudioEffectDelay_OA_F32 delay1; // Pleasant and useful sound between L & R
AudioConvert_F32toI16 cnvrt2;
AudioConvert_F32toI16 cnvrt3;
AudioOutputI2S i2sOut;
AudioControlSGTL5000 codec;
//Make all of the audio connections
AudioConnection patchCord0(i2sIn, 0, cnvrt1, 0); // connect to Left codec, 16-bit
AudioConnection_F32 patchCord1(cnvrt1, 0, iqmixer1, 0); // Input to 2 mixers
AudioConnection_F32 patchCord2(cnvrt1, 0, iqmixer1, 1);
AudioConnection_F32 patchCord3(iqmixer1, 0, hilbert1, 0); // Broadband 90 deg phase
AudioConnection_F32 patchCord4(iqmixer1, 1, hilbert1, 1);
AudioConnection_F32 patchCord5(hilbert1, 0, sum1, 0); // Sideband select
AudioConnection_F32 patchCord6(hilbert1, 1, sum1, 1);
AudioConnection_F32 patchCord7(sum1, 0, delay1, 0); // delay channel 0
AudioConnection_F32 patchCord9(sum1, 0, cnvrt2, 0); // connect to the left output
AudioConnection_F32 patchCordA(delay1, 0, cnvrt3, 0); // right output
AudioConnection patchCordB(cnvrt2, 0, i2sOut, 0);
AudioConnection patchCordC(cnvrt3, 0, i2sOut, 1);
//control display and serial interaction
bool enable_printCPUandMemory = false;
void togglePrintMemoryAndCPU(void) { enable_printCPUandMemory = !enable_printCPUandMemory; };
// Filter for AudioFilter90Deg_F32 hilbert1
#include "hilbert251A.h"
//inputs and levels
float gain_dB = -15.0f;
float gain = 0.177828f; // Same as -15 dB
float sign = 1.0f;
float deltaGain_dB = 2.5f;
float frequencyLO = 100.0f;
float delayms = 1.0f;
// *************** SETUP **********************************
void setup() {
Serial.begin(1); delay(1000);
Serial.println("*** Fine Frequency Shifter - June 2020 ***");
Serial.print("Sample Rate in Hz = ");
Serial.println(audio_settings.sample_rate_Hz, 0);
Serial.print("Block size, samples = ");
Serial.println(audio_settings.audio_block_samples);
AudioMemory(10); // I16 type
AudioMemory_F32(200, audio_settings);
//Enable the codec to start the audio flowing!
codec.enable();
codec.adcHighPassFilterEnable();
codec.inputSelect(AUDIO_INPUT_LINEIN);
iqmixer1.frequency(frequencyLO); // Frequency shift, Hz
deltaFrequency(0.0f); // Print freq
hilbert1.begin(hilbert251A, 251); // Set the Hilbert transform FIR filter
sum1.gain(0, gain*sign); // Set gains
sum1.gain(1, gain);
delay1.delay(0, delayms); // Delay right channel
deltaDelay(0.0f); // Print delay
//finish the setup by printing the help menu to the serial connections
printHelp();
}
// ************************* LOOP ****************************
void loop() {
//respond to Serial commands
while (Serial.available()) respondToByte((char)Serial.read());
//check to see whether to print the CPU and Memory Usage
if (enable_printCPUandMemory) printCPUandMemory(millis(), 3000); //print every 3000 msec
} //end loop();
//This routine prints the current and maximum CPU usage and the current usage of the AudioMemory that has been allocated
void printCPUandMemory(unsigned long curTime_millis, unsigned long updatePeriod_millis) {
//static unsigned long updatePeriod_millis = 3000; //how many milliseconds between updating gain reading?
static unsigned long lastUpdate_millis = 0;
//has enough time passed to update everything?
if (curTime_millis < lastUpdate_millis) lastUpdate_millis = 0; //handle wrap-around of the clock
if ((curTime_millis - lastUpdate_millis) > updatePeriod_millis) { //is it time to update the user interface?
Serial.print("CPU Cur/Peak: ");
Serial.print(audio_settings.processorUsage());
Serial.print("%/");
Serial.print(audio_settings.processorUsageMax());
Serial.println("%");
Serial.print(" Audio MEM Float32 Cur/Peak: ");
Serial.print(AudioMemoryUsage_F32());
Serial.print("/");
Serial.println(AudioMemoryUsageMax_F32());
Serial.print(" Audio MEM Int16 Cur/Peak: ");
Serial.print(AudioMemoryUsage());
Serial.print("/");
Serial.println(AudioMemoryUsageMax());
lastUpdate_millis = curTime_millis; //we will use this value the next time around.
}
}
void incrementGain(float increment_dB) {
gain_dB += increment_dB;
// gain is set in the "mixer" block, including sign for raise/lower freq
gain = powf(10.0f, 0.05f * gain_dB);
sum1.gain(0, gain*sign);
sum1.gain(1, gain);
printGainSettings();
}
void printGainSettings(void) {
Serial.print("Gain in dB = "); Serial.println(gain_dB, 1);
}
void deltaFrequency(float dfr) {
frequencyLO += dfr;
Serial.print("Frequency shift in Hz = ");
Serial.println(frequencyLO, 1);
if(frequencyLO < 0.0f)
sign = 1.0f;
else
sign = -1.0f;
iqmixer1.frequency(fabsf(frequencyLO));
incrementGain(0.0f);
}
void deltaDelay(float dtau) {
delayms += dtau;
if (delayms < 0.0f)
delayms = 0.0f;
delay1.delay(0, delayms); // Delay right channel
Serial.print("Delay in milliseconds = ");
Serial.println(delayms, 1);
}
//switch yard to determine the desired action
void respondToByte(char c) {
char s[2];
s[0] = c; s[1] = 0;
if( !isalpha((int)c) && c!='?') return;
switch (c) {
case 'h': case '?':
printHelp();
break;
case 'g': case 'G':
printGainSettings();
break;
case 'k':
incrementGain(deltaGain_dB);
break;
case 'K': // which is "shift k"
incrementGain(-deltaGain_dB);
break;
case 'C': case 'c':
Serial.println("Toggle printing of memory and CPU usage.");
togglePrintMemoryAndCPU();
break;
case 'd':
deltaFrequency(1.0f);
break;
case 'D':
deltaFrequency(-1.0f);
break;
case 'e':
deltaFrequency(10.0f);
break;
case 'E':
deltaFrequency(-10.0f);
break;
case 'f':
deltaFrequency(100.0f);
break;
case 'F':
deltaFrequency(-100.0f);
break;
case 't':
deltaDelay(1.0f);
break;
case 'T':
deltaDelay(-1.0f);
break;
case 'u':
deltaDelay(10.0f);
break;
case 'U':
deltaDelay(-10.0f);
break;
default:
Serial.print("You typed "); Serial.print(s);
Serial.println(". What command?");
}
}
void printHelp(void) {
Serial.println();
Serial.println("Help: Available Commands:");
Serial.println(" h: Print this help");
Serial.println(" g: Print the gain settings of the device.");
Serial.println(" C: Toggle printing of CPU and Memory usage");
Serial.print( " k: Increase the gain of both channels by ");
Serial.print(deltaGain_dB); Serial.println(" dB");
Serial.print( " K: Decrease the gain of both channels by ");
Serial.print(-deltaGain_dB); Serial.println(" dB");
Serial.println(" d: Raise frequency by 1 Hz");
Serial.println(" D: Lower frequency by 1 Hz");
Serial.println(" e: Raise frequency by 10 Hz");
Serial.println(" E: Lower frequency by 10 Hz");
Serial.println(" f: Raise frequency by 100 Hz");
Serial.println(" F: Lower frequency by 100 Hz");
Serial.println(" t: Raise stereo delay by 1 msec");
Serial.println(" T: Lower stereo delay by 1 msec");
Serial.println(" u: Raise stereo delay by 10 msec");
Serial.println(" U: Lower stereo delay by 10 msec");
}

@ -0,0 +1,123 @@
// Following is 121 term Hilbert FIR filter
float32_t hilbert121A[121] = {
0.000000000000000000,
0.000773378567767513,
0.000000000000000000,
0.001046207887980644,
0.000000000000000000,
0.001368896533613985,
0.000000000000000000,
0.001746769975247667,
0.000000000000000000,
0.002185555845922462,
0.000000000000000000,
0.002691457154069645,
0.000000000000000000,
0.003271251311125927,
0.000000000000000000,
0.003932423233774751,
0.000000000000000000,
0.004683343721596901,
0.000000000000000000,
0.005533508538632429,
0.000000000000000000,
0.006493859804516438,
0.000000000000000000,
0.007577220484233372,
0.000000000000000000,
0.008798886675905997,
0.000000000000000000,
0.010177443901536392,
0.000000000000000000,
0.011735907609641917,
0.000000000000000000,
0.013503343224246872,
0.000000000000000000,
0.015517212970554440,
0.000000000000000000,
0.017826854793349920,
0.000000000000000000,
0.020498780519188083,
0.000000000000000000,
0.023625003856774591,
0.000000000000000000,
0.027336628208641155,
0.000000000000000000,
0.031827023036304102,
0.000000000000000000,
0.037393534868609392,
0.000000000000000000,
0.044517689704988733,
0.000000000000000000,
0.054032871748808158,
0.000000000000000000,
0.067515548043274365,
0.000000000000000000,
0.088347125250410385,
0.000000000000000000,
0.125324201622410869,
0.000000000000000000,
0.210709715079613419,
0.000000000000000000,
0.634897508268964295,
0.000000000000000000,
-0.634897508268964295,
0.000000000000000000,
-0.210709715079613419,
0.000000000000000000,
-0.125324201622410869,
0.000000000000000000,
-0.088347125250410385,
0.000000000000000000,
-0.067515548043274365,
0.000000000000000000,
-0.054032871748808158,
0.000000000000000000,
-0.044517689704988733,
0.000000000000000000,
-0.037393534868609392,
0.000000000000000000,
-0.031827023036304102,
0.000000000000000000,
-0.027336628208641155,
0.000000000000000000,
-0.023625003856774591,
0.000000000000000000,
-0.020498780519188083,
0.000000000000000000,
-0.017826854793349920,
0.000000000000000000,
-0.015517212970554440,
0.000000000000000000,
-0.013503343224246872,
0.000000000000000000,
-0.011735907609641917,
0.000000000000000000,
-0.010177443901536392,
0.000000000000000000,
-0.008798886675905997,
0.000000000000000000,
-0.007577220484233372,
0.000000000000000000,
-0.006493859804516438,
0.000000000000000000,
-0.005533508538632429,
0.000000000000000000,
-0.004683343721596901,
0.000000000000000000,
-0.003932423233774751,
0.000000000000000000,
-0.003271251311125927,
0.000000000000000000,
-0.002691457154069645,
0.000000000000000000,
-0.002185555845922462,
0.000000000000000000,
-0.001746769975247667,
0.000000000000000000,
-0.001368896533613985,
0.000000000000000000,
-0.001046207887980644,
0.000000000000000000,
-0.000773378567767513,
0.000000000000000000};

@ -0,0 +1,253 @@
// Following is 251 term Hilbert FIR filter
float32_t hilbert251A[]={
0.0000003255,
0.0000000000,
0.0000030702,
0.0000000000,
0.0000089286,
0.0000000000,
0.0000183061,
0.0000000000,
0.0000316287,
0.0000000000,
0.0000493436,
0.0000000000,
0.0000719193,
0.0000000000,
0.0000998451,
0.0000000000,
0.0001336320,
0.0000000000,
0.0001738120,
0.0000000000,
0.0002209393,
0.0000000000,
0.0002755899,
0.0000000000,
0.0003383625,
0.0000000000,
0.0004098790,
0.0000000000,
0.0004907853,
0.0000000000,
0.0005817525,
0.0000000000,
0.0006834782,
0.0000000000,
0.0007966881,
0.0000000000,
0.0009221383,
0.0000000000,
0.0010606178,
0.0000000000,
0.0012129515,
0.0000000000,
0.0013800041,
0.0000000000,
0.0015626848,
0.0000000000,
0.0017619529,
0.0000000000,
0.0019