|
|
|
/* ------------------------------------------------------------------------------------
|
|
|
|
AudioAlignLR_F32.h 21 Mar 2022
|
|
|
|
|
|
|
|
Function: Waits for CODEC startup and measures L-R time alignment errors.
|
|
|
|
Automatically corrects delay errors. Requires extra control to disable
|
|
|
|
analog audio path from DAC Q to both ADC I and ADC Q.
|
|
|
|
See the following for more background on the problem being solved:
|
|
|
|
https://forum.pjrc.com/threads/42336-Reset-audio-board-codec-SGTL5000-in-realtime-processing/page2
|
|
|
|
https://forum.pjrc.com/threads/57362-AudioSDR-A-single-Audio-block-SDR-(software-defined-radio)-processor-demodulator/page3
|
|
|
|
"Twin Peaks", or TP, comes from Frank, DD4WH who found, in hsi Convolution SDR, double spectral responses when
|
|
|
|
the L and R channels were out of time alignment.
|
|
|
|
|
|
|
|
Author: Bob Larkin W7PUA
|
|
|
|
Date: 28 Feb 2022
|
|
|
|
|
|
|
|
Input: data_I, data_Q
|
|
|
|
Outputs: dataOut_I, dataOut_Q, dataTransmit_Q
|
|
|
|
|
|
|
|
Update() time: About 12 microseconds for a T4.x.
|
|
|
|
|
|
|
|
Copyright (c) 2022 Robert 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 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.
|
|
|
|
------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
//REF:
|
|
|
|
// Manual I2S codec Phase Correction Utility by Ron Carr, Updated for F32, K7MDL
|
|
|
|
// Reference: [URL]https://forum.pjrc.com/threads/57362-AudioSDR-A-single-Audio-block-SDR-(software-defined-radio)-processor-demodulator?p=263048&viewfull=1#post263048[/URL]
|
|
|
|
//
|
|
|
|
// The variable, currentTPinfo.TPsignalHardware, changes source of
|
|
|
|
// the signal for going to L&R for correlation.
|
|
|
|
// If TP_SIGNAL_CODEC there needs to be a switchable hardware link between
|
|
|
|
// the DAC right channel and both of ADC right and ADC left.
|
|
|
|
// If TP_SIGNAL_IO_PIN the INO must update a roughly fs/4
|
|
|
|
// square wave using an i/o pin. That signal is generated by
|
|
|
|
// the .INO as it is not possible in an audio object.
|
|
|
|
// Default is TP_SIGNAL_CODEC.
|
|
|
|
//
|
|
|
|
// BETA NOTE That calls and operations may change. BETA
|
|
|
|
|
|
|
|
// Rev 12 Mar 2022 Added normalized decisions to eliminate need for a threshold.
|
|
|
|
/* This is best placed in the setup() part of the INO. A signal needs to be generated
|
|
|
|
* and this would look like:
|
|
|
|
|
|
|
|
uint32_t timeSquareWave = 0;
|
|
|
|
#define PIN_FOR_TP 2
|
|
|
|
pinMode (PIN_FOR_TP, OUTPUT); // Digital output pin
|
|
|
|
|
|
|
|
pData = TwinPeak.read(); // Base data to check error
|
|
|
|
while (pData->TPerror < 0 && millis()-tMillis < 2000) // with timeout
|
|
|
|
{
|
|
|
|
if(micros()-timeSquareWave >= 45 && pData->TPstate==TP_MEASURE)
|
|
|
|
{
|
|
|
|
timeSquareWave = micros();
|
|
|
|
squareWave = squareWave^1;
|
|
|
|
digitalWrite(PIN_FOR_TP, squareWave);
|
|
|
|
}
|
|
|
|
pData = TwinPeak.read();
|
|
|
|
}
|
|
|
|
digitalWrite(PIN_FOR_TP, 0); // Set pin to zero
|
|
|
|
|
|
|
|
* where the 45 microseconds needs to be adjusted for the sample rate of the Codec
|
|
|
|
*/
|
|
|
|
// Rev 21 Mar 2022 Added BP Filters on two inputs, pass fs/4.
|
|
|
|
|
|
|
|
#ifndef audio_align_lr_f32_h_
|
|
|
|
#define audio_align_lr_f32_h_
|
|
|
|
|
|
|
|
#include "AudioStream_F32.h"
|
|
|
|
#include "Arduino.h"
|
|
|
|
|
|
|
|
#define TP_IDLE 0
|
|
|
|
#define TP_MEASURE 1
|
|
|
|
#define TP_RUN 2
|
|
|
|
|
|
|
|
#define TP_SIGNAL_CODEC 0
|
|
|
|
#define TP_SIGNAL_IO_PIN 1
|
|
|
|
|
|
|
|
#define ERROR_TP_EARLY -2
|
|
|
|
#define ERROR_TP_BAD_DATA -1
|
|
|
|
#define ERROR_TP_NONE 0
|
|
|
|
|
|
|
|
// Needs to be available to INO and AudioAlignLR_F32 also.
|
|
|
|
struct TPinfo{
|
|
|
|
uint16_t TPstate;
|
|
|
|
uint32_t nMeas;
|
|
|
|
float32_t xcVal[4]; // I-Q cross-correlation sums
|
|
|
|
float32_t xNorm;
|
|
|
|
int16_t neededShift;
|
|
|
|
int16_t TPerror;
|
|
|
|
uint16_t TPsignalHardware;
|
|
|
|
};
|
|
|
|
|
|
|
|
class AudioAlignLR_F32 : public AudioStream_F32
|
|
|
|
{
|
|
|
|
//GUI: inputs:2, outputs:3 //this line used for automatic generation of GUI node
|
|
|
|
//GUI: shortName: AlignLR
|
|
|
|
public:
|
|
|
|
AudioAlignLR_F32(uint16_t _hardware, uint16_t _controlPinNumber, bool _controlPinInvert):
|
|
|
|
AudioStream_F32(2,inputQueueArray)
|
|
|
|
{
|
|
|
|
currentTPinfo.TPsignalHardware = _hardware;
|
|
|
|
controlPinNumber = _controlPinNumber;
|
|
|
|
controlPinInvert = _controlPinInvert;
|
|
|
|
sample_rate_Hz = AUDIO_SAMPLE_RATE;
|
|
|
|
// Changes would be needed for block data sizes other than 128
|
|
|
|
block_size = AUDIO_BLOCK_SAMPLES;
|
|
|
|
initTP();
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioAlignLR_F32
|
|
|
|
(uint16_t _hardware, uint16_t _controlPinNumber, bool _controlPinInvert, const AudioSettings_F32 &settings):
|
|
|
|
AudioStream_F32(2,inputQueueArray)
|
|
|
|
{
|
|
|
|
currentTPinfo.TPsignalHardware = _hardware;
|
|
|
|
controlPinNumber = _controlPinNumber;
|
|
|
|
controlPinInvert = _controlPinInvert;
|
|
|
|
sample_rate_Hz = settings.sample_rate_Hz;
|
|
|
|
block_size = settings.audio_block_samples;
|
|
|
|
initTP();
|
|
|
|
}
|
|
|
|
|
|
|
|
void initTP(void) { // Common calls for constructors, not INO's
|
|
|
|
currentTPinfo.TPstate = TP_IDLE;
|
|
|
|
currentTPinfo.neededShift = 0;
|
|
|
|
currentTPinfo.TPerror = ERROR_TP_EARLY;
|
|
|
|
needOneMore = false;
|
|
|
|
// Initialize FIR instance (ARM DSP Math Library)
|
|
|
|
arm_fir_init_f32(&fir_instL, 101, firBP, &StateF32L[0], block_size);
|
|
|
|
arm_fir_init_f32(&fir_instR, 101, firBP, &StateF32R[0], block_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns all the status info, available anytime
|
|
|
|
TPinfo *read(void) {
|
|
|
|
return ¤tTPinfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stateAlignLR(int _TPstate) {
|
|
|
|
currentTPinfo.TPstate = _TPstate;
|
|
|
|
if(currentTPinfo.TPstate==TP_MEASURE)
|
|
|
|
{
|
|
|
|
currentTPinfo.nMeas = 0; // Timing for the DAC & ADC delays
|
|
|
|
currentTPinfo.neededShift = 0;
|
|
|
|
currentTPinfo.TPerror = ERROR_TP_EARLY;
|
|
|
|
needOneMore = true;
|
|
|
|
if(currentTPinfo.TPsignalHardware==TP_SIGNAL_CODEC)
|
|
|
|
{
|
|
|
|
pinMode(controlPinNumber, OUTPUT); // Turn on special audio path
|
|
|
|
digitalWrite(controlPinNumber, 1 & (uint16_t)controlPinInvert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
void setThreshold(float32_t _TPthreshold) {
|
|
|
|
//TPthreshold = _TPthreshold;
|
|
|
|
Serial.println("ERROR: Threshold is no longer used. 12 Mar 2022");
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Do we need this after experiments are done? <<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
void setLRfilter(bool _useLRfilter) {
|
|
|
|
useLRfilter = _useLRfilter;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void update(void);
|
|
|
|
|
|
|
|
private:
|
|
|
|
audio_block_f32_t *inputQueueArray[2];
|
|
|
|
float32_t sample_rate_Hz = AUDIO_SAMPLE_RATE;
|
|
|
|
uint16_t controlPinNumber = 0;
|
|
|
|
bool controlPinInvert = true;
|
|
|
|
uint16_t block_size = 128;
|
|
|
|
bool needOneMore = false;
|
|
|
|
// float32_t TPthreshold = 1.0f;
|
|
|
|
float32_t TPextraL = 0.0f;
|
|
|
|
float32_t TPextraR = 0.0f;
|
|
|
|
TPinfo currentTPinfo;
|
|
|
|
bool outputFlag = false;
|
|
|
|
uint16_t count = 0;
|
|
|
|
bool useLRfilter = true;
|
|
|
|
|
|
|
|
// ARM DSP Math library filter instances
|
|
|
|
arm_fir_instance_f32 fir_instL;
|
|
|
|
float32_t StateF32L[229];
|
|
|
|
|
|
|
|
arm_fir_instance_f32 fir_instR;
|
|
|
|
float32_t StateF32R[229];
|
|
|
|
|
|
|
|
/* Sampling frequency: 48000 Hz
|
|
|
|
* 0 Hz - 10100 Hz attenuation = 39.7 dB
|
|
|
|
* 10800 Hz - 13200 Hz ripple = 1.2 dB
|
|
|
|
* 13900 Hz - 24000 Hz attenuation = 39.7 dB
|
|
|
|
* NOTE: This will scale with sample rate and stay withpassband at fs/4.
|
|
|
|
*/
|
|
|
|
float32_t firBP[101] = {
|
|
|
|
0.0010784874240328994,
|
|
|
|
-1.8511940285394994e-15,
|
|
|
|
0.007173020540888694,
|
|
|
|
-2.446906631093228e-15,
|
|
|
|
-0.007077486809913102,
|
|
|
|
-2.3412063121164944e-15,
|
|
|
|
0.009011618946946028,
|
|
|
|
-2.5923206852409818e-15,
|
|
|
|
-0.010712293622384057,
|
|
|
|
-2.3964501699408722e-15,
|
|
|
|
0.011482971456733491,
|
|
|
|
-2.666527614775755e-15,
|
|
|
|
-0.010885141080842342,
|
|
|
|
-2.4082964205096483e-15,
|
|
|
|
0.008647733991415402,
|
|
|
|
-2.4633438337819814e-15,
|
|
|
|
-0.004721228640000285,
|
|
|
|
-2.479178552837757e-15,
|
|
|
|
-0.0006641071730367476,
|
|
|
|
-2.5286128045656088e-15,
|
|
|
|
0.006973730987499941,
|
|
|
|
-2.477532927164074e-15,
|
|
|
|
-0.01339871502510467,
|
|
|
|
-2.431267551488873e-15,
|
|
|
|
0.01892093480750003,
|
|
|
|
-2.4098508014460433e-15,
|
|
|
|
-0.022430541924076213,
|
|
|
|
-2.405917616535165e-15,
|
|
|
|
0.02285350193041076,
|
|
|
|
-2.509429940385098e-15,
|
|
|
|
-0.01935677493053742,
|
|
|
|
-2.483893222086823e-15,
|
|
|
|
0.01141749520487472,
|
|
|
|
-2.601900577555639e-15,
|
|
|
|
0.000985366589667628,
|
|
|
|
-2.5322636675246416e-15,
|
|
|
|
-0.01735448964139692,
|
|
|
|
-2.592937123951427e-15,
|
|
|
|
0.03668097751462642,
|
|
|
|
-2.617655860016599e-15,
|
|
|
|
-0.057518688568419914,
|
|
|
|
-2.3833597705424817e-15,
|
|
|
|
0.07812082879163866,
|
|
|
|
-2.532991478487954e-15,
|
|
|
|
-0.09663455293743198,
|
|
|
|
-2.412807130934187e-15,
|
|
|
|
0.11132156511296272,
|
|
|
|
-2.5145289109982724e-15,
|
|
|
|
-0.12075435023719062,
|
|
|
|
-2.4314403797559724e-15,
|
|
|
|
0.12400702102359241,
|
|
|
|
-2.4314403797559724e-15,
|
|
|
|
-0.12075435023719062,
|
|
|
|
-2.5145289109982724e-15,
|
|
|
|
0.11132156511296272,
|
|
|
|
-2.412807130934187e-15,
|
|
|
|
-0.09663455293743198,
|
|
|
|
-2.532991478487954e-15,
|
|
|
|
0.07812082879163866,
|
|
|
|
-2.3833597705424817e-15,
|
|
|
|
-0.057518688568419914,
|
|
|
|
-2.617655860016599e-15,
|
|
|
|
0.03668097751462642,
|
|
|
|
-2.592937123951427e-15,
|
|
|
|
-0.01735448964139692,
|
|
|
|
-2.5322636675246416e-15,
|
|
|
|
0.000985366589667628,
|
|
|
|
-2.601900577555639e-15,
|
|
|
|
0.01141749520487472,
|
|
|
|
-2.483893222086823e-15,
|
|
|
|
-0.01935677493053742,
|
|
|
|
-2.509429940385098e-15,
|
|
|
|
0.02285350193041076,
|
|
|
|
-2.405917616535165e-15,
|
|
|
|
-0.022430541924076213,
|
|
|
|
-2.4098508014460433e-15,
|
|
|
|
0.01892093480750003,
|
|
|
|
-2.431267551488873e-15,
|
|
|
|
-0.01339871502510467,
|
|
|
|
-2.477532927164074e-15,
|
|
|
|
0.006973730987499941,
|
|
|
|
-2.5286128045656088e-15,
|
|
|
|
-0.0006641071730367476,
|
|
|
|
-2.479178552837757e-15,
|
|
|
|
-0.004721228640000285,
|
|
|
|
-2.4633438337819814e-15,
|
|
|
|
0.008647733991415402,
|
|
|
|
-2.4082964205096483e-15,
|
|
|
|
-0.010885141080842342,
|
|
|
|
-2.666527614775755e-15,
|
|
|
|
0.011482971456733491,
|
|
|
|
-2.3964501699408722e-15,
|
|
|
|
-0.010712293622384057,
|
|
|
|
-2.5923206852409818e-15,
|
|
|
|
0.009011618946946028,
|
|
|
|
-2.3412063121164944e-15,
|
|
|
|
-0.007077486809913102,
|
|
|
|
-2.446906631093228e-15,
|
|
|
|
0.007173020540888694,
|
|
|
|
-1.8511940285394994e-15,
|
|
|
|
0.0010784874240328994};
|
|
|
|
|
|
|
|
};
|
|
|
|
#endif
|