-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathRadioIQMixer_F32.h
162 lines (148 loc) · 5.83 KB
/
RadioIQMixer_F32.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* 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