|
|
@ -1,20 +1,25 @@ |
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* RadioIQMixer_F32 |
|
|
|
* RadioIQMixer_F32.h |
|
|
|
|
|
|
|
*
|
|
|
|
* 8 April 2020 Bob Larkin |
|
|
|
* 8 April 2020 Bob Larkin |
|
|
|
* With much credit to: |
|
|
|
* With much credit to: |
|
|
|
* Chip Audette (OpenAudio) Feb 2017 |
|
|
|
* Chip Audette (OpenAudio) Feb 2017 |
|
|
|
* and of course, to PJRC for the Teensy and Teensy Audio Library |
|
|
|
* and of course, to PJRC for the Teensy and Teensy Audio Library |
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* This quadrature mixer block is suitable for both transmit and receive. |
|
|
|
* |
|
|
|
* |
|
|
|
* A basic building block is a pair of mixers fed in parallel with the |
|
|
|
* A basic building block is a pair of mixers with the |
|
|
|
* LO going to the mixers at the same frequency, but differing in phase
|
|
|
|
* 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 |
|
|
|
* 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 |
|
|
|
* frequency but also 90 degrees apart in phase. The LO are included |
|
|
|
* in the block, but there are no post-mixing filters. |
|
|
|
* in the block, but there are no post-mixing filters. |
|
|
|
*
|
|
|
|
*
|
|
|
|
* The frequency is set by .frequency(float freq_Hz) |
|
|
|
* The frequency is set by .frequency(float freq_Hz) |
|
|
|
* Particularly for use in transmitting, there is provision for varying |
|
|
|
* There is provision for varying |
|
|
|
* the phase between the sine and cosine oscillators. Technically this is no |
|
|
|
* the phase between the sine and cosine oscillators. Technically this is no |
|
|
|
* longer sin and cos, but that is what real hardware needs. |
|
|
|
* longer sin and cos, but that is what real hardware needs. |
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* The amplitudeC(a) allows balancing of I and Q channels. |
|
|
|
*
|
|
|
|
*
|
|
|
|
* The output levels are 0.5 times the input level. |
|
|
|
* The output levels are 0.5 times the input level. |
|
|
|
*
|
|
|
|
*
|
|
|
@ -25,15 +30,18 @@ |
|
|
|
* Outputs: 0 is I 1 is Q |
|
|
|
* Outputs: 0 is I 1 is Q |
|
|
|
*
|
|
|
|
*
|
|
|
|
* Functions, available during operation: |
|
|
|
* Functions, available during operation: |
|
|
|
* void frequency(float32_t fr) Sets BFO frequency Hz |
|
|
|
* void frequency(float32_t fr) Sets LO frequency Hz |
|
|
|
* void iqmPhaseS(float32_t ps) Sets Phase of Sine in radians |
|
|
|
* 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 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 amplitudeC(float32_t a) Sets relative amplitude of I output |
|
|
|
* void useSimple(bool s) Faster if 1, but no phase/amplitude adjustment |
|
|
|
* 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 |
|
|
|
* void setSampleRate_Hz(float32_t fs_Hz) Allows dynamic sample rate change for this function |
|
|
|
|
|
|
|
* void useTwoChannel(bool 2Ch) Uses 2 input cannels, I & Q, if true. Apr 2021 |
|
|
|
*
|
|
|
|
*
|
|
|
|
* Time: T3.6 For an update of a 128 sample block, doSimple=1, 46 microseconds |
|
|
|
* 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 |
|
|
|
* T4.0 For an update of a 128 sample block, doSimple=1, 20 microseconds |
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Rev Apr2021 Allowed for 2-channel I-Q input. Defaults to 1 Channel. "real." |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#ifndef _radioIQMixer_f32_h |
|
|
|
#ifndef _radioIQMixer_f32_h |
|
|
@ -48,15 +56,15 @@ class RadioIQMixer_F32 : public AudioStream_F32 { |
|
|
|
//GUI: shortName: IQMixer
|
|
|
|
//GUI: shortName: IQMixer
|
|
|
|
public: |
|
|
|
public: |
|
|
|
// Option of AudioSettings_F32 change to block size or sample rate:
|
|
|
|
// Option of AudioSettings_F32 change to block size or sample rate:
|
|
|
|
RadioIQMixer_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { |
|
|
|
RadioIQMixer_F32(void) : AudioStream_F32(2, inputQueueArray_f32) { |
|
|
|
// Defaults
|
|
|
|
// Defaults
|
|
|
|
} |
|
|
|
} |
|
|
|
RadioIQMixer_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { |
|
|
|
RadioIQMixer_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) { |
|
|
|
setSampleRate_Hz(settings.sample_rate_Hz); |
|
|
|
setSampleRate_Hz(settings.sample_rate_Hz); |
|
|
|
block_size = settings.audio_block_samples; |
|
|
|
block_size = settings.audio_block_samples; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void frequency(float32_t fr) { // Frequency in Hz
|
|
|
|
void frequency(float32_t fr) { // LO Frequency in Hz
|
|
|
|
freq = fr; |
|
|
|
freq = fr; |
|
|
|
if (freq < 0.0f) freq = 0.0f; |
|
|
|
if (freq < 0.0f) freq = 0.0f; |
|
|
|
else if (freq > sample_rate_Hz/2.0f) freq = sample_rate_Hz/2.0f; |
|
|
|
else if (freq > sample_rate_Hz/2.0f) freq = sample_rate_Hz/2.0f; |
|
|
@ -64,8 +72,9 @@ public: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Externally, phase comes in the range (0,2*M_PI) keeping with C math functions
|
|
|
|
/* 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 |
|
|
|
* For convenience internally, the full circle is represented as (0.0, 512.0). |
|
|
|
* convenient for finding the entry to the sine table. |
|
|
|
* This function allows multiple mixers to be phase coordinated (stop |
|
|
|
|
|
|
|
* interrupts when setting). |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
void iqmPhaseS(float32_t a) { |
|
|
|
void iqmPhaseS(float32_t a) { |
|
|
|
while (a < 0.0f) a += MF_TWOPI; |
|
|
|
while (a < 0.0f) a += MF_TWOPI; |
|
|
@ -78,6 +87,7 @@ public: |
|
|
|
// phaseS_C_r is the number of radians that the cosine output leads the
|
|
|
|
// 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,
|
|
|
|
// sine output. The default is M_PI_2 = pi/2 = 1.57079633 radians,
|
|
|
|
// corresponding to 90.00 degrees cosine leading sine.
|
|
|
|
// corresponding to 90.00 degrees cosine leading sine.
|
|
|
|
|
|
|
|
// This is used to correct hardware phase unbalance
|
|
|
|
void iqmPhaseS_C(float32_t a) { |
|
|
|
void iqmPhaseS_C(float32_t a) { |
|
|
|
while (a < 0.0f) a += MF_TWOPI; |
|
|
|
while (a < 0.0f) a += MF_TWOPI; |
|
|
|
while (a > MF_TWOPI) a -= MF_TWOPI; |
|
|
|
while (a > MF_TWOPI) a -= MF_TWOPI; |
|
|
@ -87,14 +97,21 @@ public: |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// The amplitude, a, is the peak, as in zero-to-peak. This produces outputs
|
|
|
|
// Sets the gain g for the I channel.
|
|
|
|
// ranging from -a to +a. Both outputs are the same amplitude.
|
|
|
|
// The Q channel is always 1.0. This is used to correct hardware
|
|
|
|
void iqmAmplitude(float32_t a) { |
|
|
|
// amplitude unbalance.
|
|
|
|
amplitude_pk = a; |
|
|
|
void iqmAmplitude(float32_t g) { |
|
|
|
|
|
|
|
amplitude_pk = g; |
|
|
|
doSimple = false; |
|
|
|
doSimple = false; |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Channel 0 (left) is real for single input, or is I for
|
|
|
|
|
|
|
|
// complex input. With twoChannel===true channel 1 is Q.
|
|
|
|
|
|
|
|
void useTwoChannel(bool _2Ch) { |
|
|
|
|
|
|
|
twoChannel = _2Ch; |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Speed up calculations by setting phaseS_C=90deg, amplitude=1
|
|
|
|
// Speed up calculations by setting phaseS_C=90deg, amplitude=1
|
|
|
|
void useSimple(bool s) { |
|
|
|
void useSimple(bool s) { |
|
|
|
doSimple = s; |
|
|
|
doSimple = s; |
|
|
@ -119,7 +136,7 @@ public: |
|
|
|
virtual void update(void); |
|
|
|
virtual void update(void); |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
private: |
|
|
|
audio_block_f32_t *inputQueueArray_f32[1]; |
|
|
|
audio_block_f32_t *inputQueueArray_f32[2]; |
|
|
|
float32_t freq = 1000.0f; |
|
|
|
float32_t freq = 1000.0f; |
|
|
|
float32_t phaseS = 0.0f; |
|
|
|
float32_t phaseS = 0.0f; |
|
|
|
float32_t phaseS_C = 128.00; // 512.00 is 360 degrees
|
|
|
|
float32_t phaseS_C = 128.00; // 512.00 is 360 degrees
|
|
|
@ -128,10 +145,8 @@ private: |
|
|
|
float32_t phaseIncrement = 512.00f * freq /sample_rate_Hz; |
|
|
|
float32_t phaseIncrement = 512.00f * freq /sample_rate_Hz; |
|
|
|
uint16_t block_size = AUDIO_BLOCK_SAMPLES; |
|
|
|
uint16_t block_size = AUDIO_BLOCK_SAMPLES; |
|
|
|
uint16_t errorPrintIQM = 0; // Normally off
|
|
|
|
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; |
|
|
|
bool doSimple = true; |
|
|
|
|
|
|
|
bool twoChannel = false; // Activates 2 channels for I-Q input
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
#endif |
|
|
|