-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathRadioFMDetector_F32.cpp
155 lines (137 loc) · 6.12 KB
/
RadioFMDetector_F32.cpp
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
/*
* RadioFMDetector_F32.cpp
*
* 25 April 2022
* Bob Larkin, in support of the library:
* Chip Audette, OpenAudio, Apr 2017
* -------------------
*
* Copyright (c) 2022 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 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.
*
* See RadioFMDetector_F32.h for usage details
*/
#include "RadioFMDetector_F32.h"
// 513 values of the sine wave in a float array:
#include "sinTable512_f32.h"
// ==== UPDATE ====
void RadioFMDetector_F32::update(void) {
audio_block_f32_t *blockIn, *blockOut=NULL, *blockZero;
static float saveIn = 0.0f; // for squelch
static float saveOut = 0.0f;
uint16_t i, index_sine;
float32_t deltaPhase, a, b, dtemp1, dtemp2;
float32_t v_i[128]; // max size
float32_t v_q[128];
mathDSP_F32 mathDSP1; // Math support functions
#if TEST_TIME_FM
if (iitt++ >1000000) iitt = -10;
uint32_t t1, t2;
t1 = tElapse;
#endif
// Get input to FM Detector block
blockIn = AudioStream_F32::receiveWritable_f32(0);
if (!blockIn) {
return;
}
if (fir_Out_Coeffs == NULL) {
//if(errorPrintFM) Serial.println("FMDET-ERR: No Out FIR Coefficients");
AudioStream_F32::release(blockIn);
return;
}
// Try to get a block for the FM output
blockOut = AudioStream_F32::allocate_f32();
if (!blockOut){ // Didn't have any
//if(errorPrintFM) Serial.println("FMDET-ERR: No Output Memory");
AudioStream_F32::release(blockIn);
return;
}
blockZero = AudioStream_F32::allocate_f32();
for(int kk=0; kk<128; kk++) blockZero->data[kk] = 0.0f;
// Generate sine and cosine of center frequency and double-balance mix
// these with the input signal to produce an intermediate result
// saved as v_i[] and v_q[]
for (i=0; i < block_size; i++) {
phaseS += phaseIncrement;
if (phaseS > 512.0f)
phaseS -= 512.0f;
index_sine = (uint16_t) phaseS;
deltaPhase = phaseS -(float32_t) index_sine;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index_sine];
b = sinTable512_f32[index_sine+1];
// Linear interpolation and multiplying (DBMixer) with input
v_i[i] = blockIn->data[i] * (a + 0.001953125*(b-a)*deltaPhase);
/* Repeat for cosine by adding 90 degrees phase */
index_sine = (index_sine + 128) & 0x01ff;
/* Read two nearest values of input value from the sin table */
a = sinTable512_f32[index_sine];
b = sinTable512_f32[index_sine+1];
/* deltaPhase will be the same as used for sin */
v_q[i] = blockIn->data[i] * (a + 0.001953125*(b-a)*deltaPhase);
}
// Do I FIR and Q FIR. We can borrow blockIn and blockOut at this point
//void arm_fir_f32( const arm_fir_instance_f32* S, float32_t* pSrc, float32_t* pDst, uint32_t blockSize)
arm_fir_f32(&FMDet_I_inst, v_i, blockIn->data, (uint32_t)blockIn->length);
arm_fir_f32(&FMDet_Q_inst, v_q, blockOut->data, (uint32_t)blockOut->length);
// Do ATAN2, differentiation and de-emphasis in single loop
for(i=0; i<block_size; i++) { // y x
dtemp1 = mathDSP1.fastAtan2((float)blockOut->data[i], (float)blockIn->data[i]);
// Apply differentiator by subtracting last value of atan2
if(dtemp1>MF_PI_2 && diffLast<-MF_PI_2) // Probably a wrap around
dtemp2 = dtemp1 - diffLast - MF_TWOPI;
else if(dtemp1<-MF_PI_2 && diffLast >MF_PI_2) // Probably a reverse wrap around
dtemp2 = dtemp1 - diffLast + MF_TWOPI;
else
dtemp2 = dtemp1 - diffLast; // Differentiate
diffLast = dtemp1; // Ready for next time through loop
// Data point is now dtemp2. Apply single pole de-emphasis LPF, in place
dLast = Kdem * dtemp2 + OneMinusKdem * dLast;
blockIn->data[i] = dLast; // and save to an array
}
// Do output FIR filter. Data now in blockIn.
arm_fir_f32(&FMDet_Out_inst, blockIn->data, blockOut->data, (uint32_t)blockIn->length);
// Squelch picks the audio from before the output filter and does a 4-pole BiQuad BPF
// blockIn->data still has the data we need
arm_biquad_cascade_df1_f32(&iirSqIn_inst, blockIn->data,
blockIn->data, blockIn->length);
// Update the Squelch full-wave envelope detector and single pole LPF
float sumNoise = 0.0f;
for(i=0; i<block_size; i++)
sumNoise += fabsf(blockIn->data[i]); // Ave of rectified noise
squelchLevel = alpha*(sumNoise + saveIn) + gamma*saveOut; // 1 pole
saveIn = sumNoise;
saveOut = squelchLevel;
// Add hysteresis here <<<<
// Squelch gate sends audio to output 1 (right) if squelch is open
if(squelchLevel < squelchThreshold)
AudioStream_F32::transmit(blockOut, 1);
else
AudioStream_F32::transmit(blockZero, 1);
// The un-squelch controlled audio for tone decoding, etc., goes to output 0
AudioStream_F32::transmit(blockOut, 0);
AudioStream_F32::release(blockIn); // Give back the blocks
AudioStream_F32::release(blockOut); AudioStream_F32::release(blockZero);
#if TEST_TIME_FM
t2 = tElapse;
if(iitt++ < 0) {Serial.print("At end of FM Det "); Serial.println (t2 - t1); }
t1 = tElapse;
#endif
}