|
|
|
/*
|
|
|
|
* RadioIQMixer_F32.h
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
* This quadrature mixer block is suitable for both transmit and receive.
|
|
|
|
*
|
|
|
|
* A basic building block is a pair of mixers 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)
|
|
|
|
* 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 amplitudeC(a) allows balancing of I and Q channels.
|
|
|
|
*
|
|
|
|
* The output levels are 0.5 times the input level, unless adjusted by
|
|
|
|
* gainOut(g).
|
|
|
|
*
|
|
|
|
* 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 LO 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 I output
|
|
|
|
* 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 useTwoChannel(bool 2Ch) Uses 2 input cannels, I & Q, if true. Apr 2021
|
|
|
|
* void setGainOut(float32_t gainO) Sets gain after mixers. Often a value of 2.0 makes the
|
|
|
|
* block lossless. For both doSimple and not doSimple.
|
|
|
|
*
|
|
|
|
* Time: T3.6 For an update of a 128 sample block, doSimple=true, 46 microseconds
|
|
|
|
* T4.0 For an update of a 128 sample block, doSimple=true, 20 microseconds
|
|
|
|
*
|
|
|
|
* Rev Apr2021 Allowed for 2-channel I-Q input. Defaults to 1 Channel. "real."
|
|
|
|
* Rev 30Jan23 Corrected setSampleRate_Hz(sr) to do so! RSL
|
|
|
|
* Rev 2 Feb 2023 Added gainOut, with or without doSimple. RSL
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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(2, inputQueueArray_f32) {
|
|
|
|
// Defaults
|
|
|
|
}
|
|
|
|
RadioIQMixer_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {
|
|
|
|
setSampleRate_Hz(settings.sample_rate_Hz);
|
|
|
|
block_size = settings.audio_block_samples;
|
|
|
|
}
|
|
|
|
|
|
|
|
void frequency(float32_t fr) { // LO 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
|
|
|
|
* For convenience internally, the full circle is represented as (0.0, 512.0).
|
|
|
|
* This function allows multiple mixers to be phase coordinated (stop
|
|
|
|
* interrupts when setting).
|
|
|
|
*/
|
|
|
|
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.
|
|
|
|
// This is used to correct hardware phase unbalance
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets the gain g for the I channel.
|
|
|
|
// The Q channel is always 1.0. This is used to correct hardware
|
|
|
|
// amplitude unbalance.
|
|
|
|
void iqmAmplitude(float32_t g) {
|
|
|
|
amplitude_pk = g;
|
|
|
|
doSimple = false;
|
|
|
|
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
|
|
|
|
void useSimple(bool s) {
|
|
|
|
doSimple = s;
|
|
|
|
if(doSimple) {
|
|
|
|
phaseS_C = 128.0f;
|
|
|
|
amplitude_pk = 1.0f;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setGainOut(float32_t _gainO) { // Rev 2023
|
|
|
|
gainOut = _gainO;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setSampleRate_Hz(float32_t fs_Hz) {
|
|
|
|
sample_rate_Hz = fs_Hz; // Added 30Jan23 RSL
|
|
|
|
// 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 / sample_rate_Hz;
|
|
|
|
}
|
|
|
|
|
|
|
|
void showError(uint16_t e) { // Serial.print errors in update()
|
|
|
|
errorPrintIQM = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void update(void);
|
|
|
|
|
|
|
|
private:
|
|
|
|
audio_block_f32_t *inputQueueArray_f32[2];
|
|
|
|
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;
|
|
|
|
float32_t gainOut = 1.0f;
|
|
|
|
uint16_t block_size = AUDIO_BLOCK_SAMPLES;
|
|
|
|
uint16_t errorPrintIQM = 0; // Normally off
|
|
|
|
bool doSimple = true;
|
|
|
|
bool twoChannel = false; // Activates 2 channels for I-Q input
|
|
|
|
};
|
|
|
|
#endif
|