@@ -117,7 +117,7 @@ void radioCESSBtransmit_F32::update(void) {
117117 arm_fir_f32 (&firInstWeaverQ, weaverMQ, workingDataQ, nW);
118118 // Note: Sine wave envelope gain from blockIn->data[kk] to here is gainIn
119119
120- // Mesaure input power and peak envelope, SSB before any CESSB processing
120+ // Measure input power and peak envelope, SSB before any CESSB processing
121121 for (int k=0 ; k<nW; k++)
122122 {
123123 float32_t pwrWorkingData = workingDataI[k]*workingDataI[k] + workingDataQ[k]*workingDataQ[k];
@@ -142,102 +142,110 @@ void radioCESSBtransmit_F32::update(void) {
142142 // LPF with gain of 2 built into coefficients, correct for zeros.
143143 arm_fir_f32 (&firInstInterpolate1I, workingDataI, workingDataI, nC);
144144 arm_fir_f32 (&firInstInterpolate1Q, workingDataQ, workingDataQ, nC);
145+
145146 // WorkingDataI and Q are now at 24 ksps and ready for clipping
146147 // For input 48 ksps this produces 64 numbers
147148 // Voltage gain from blockIn->data to here for small sine wave is 1.0
148149
149- for (int kk=0 ; kk<nC; kk++)
150+
151+
152+
153+ // Optional skip of CESSB processing. Filtering & SSB generation unchanged. Thanks to Greg KF5N
154+ if (cessbProcessing) // Not skipping
150155 {
151- float32_t power = workingDataI[kk]*workingDataI[kk] + workingDataQ[kk]*workingDataQ[kk];
152- float32_t mag = sqrtf (power);
153- if (mag > 1 .0f ) // This the clipping, scaled to 1.0, desired max
156+ for (int kk=0 ; kk<nC; kk++)
154157 {
155- workingDataI[kk] /= mag;
156- workingDataQ[kk] /= mag;
158+ float32_t power = workingDataI[kk]*workingDataI[kk] + workingDataQ[kk]*workingDataQ[kk];
159+ float32_t mag = sqrtf (power);
160+ if (mag > 1 .0f ) // This the clipping, scaled to 1.0, desired max
161+ {
162+ workingDataI[kk] /= mag;
163+ workingDataQ[kk] /= mag;
164+ }
165+ powerSum0 += power; // For measuring amount of clipping
166+ if (mag > maxMag0)
167+ maxMag0 = mag;
157168 }
158- powerSum0 += power; // For measuring amount of clipping
159- if (mag > maxMag0)
160- maxMag0 = mag;
161- }
162169
163- // clipperIn needs spectrum control, so LP filter it. Same filter coeffs as Weaver.
164- // Both BW of the signal and the sample rate have been doubled.
165- arm_fir_f32 (&firInstClipperI, workingDataI, workingDataI, nC);
166- arm_fir_f32 (&firInstClipperQ, workingDataQ, workingDataQ, nC);
170+ // clipperIn needs spectrum control, so LP filter it. Same filter coeffs as Weaver.
171+ // Both BW of the signal and the sample rate have been doubled.
172+ arm_fir_f32 (&firInstClipperI, workingDataI, workingDataI, nC);
173+ arm_fir_f32 (&firInstClipperQ, workingDataQ, workingDataQ, nC);
167174
168- // Ready to compensate for filter overshoots
169- for (int k=0 ; k<64 ; k++)
170- {
171- /* ======= Sidebar: Circular 2^n length delay arrays ========
172- *
173- * The length of the array, N,
174- * must be a power of 2. For example N=2^6 = 64. The minimum
175- * delay possible is the trivial case of 0 up to N-1.
176- * As in C, let i be the index of the N array elements which
177- * would range from 0 to N-1. If p is an integer, that is a power
178- * of 2 also, with p >= n, it can serve as an index to the
179- * delay array by "ANDing" it with (N-1). That is,
180- * i = p & (N-1). It can be convenient if the largest
181- * possible value of the integer p, plus 1, is an integer multiple
182- * of the arrray size N, as then the rollover of p will not cause
183- * a jump in i. For instance, if p is an uint8_t with a maximum
184- * value of pmax=255, (pmax+1)/N = (255+1)/64 = 4, which is an
185- * integer. This combination will have no problems from rollover
186- * of p.
187- *
188- * The new data point is entered at index p & (N - 1). To
189- * achieve a delay of d, the output of the delay array is taken
190- * at index ((p-d) & (N-1)). The index is then incremented by 1.
191- * ========================================================== */
192-
193- // Circular delay line for signal to align data with FIR output
194- // Put I & Q data points into the delay arrays
195- osDelayI[indexOsDelay & 0X3F ] = workingDataI[k];
196- osDelayQ[indexOsDelay & 0X3F ] = workingDataQ[k];
197- // Remove 64 points delayed data from line and save for later
198- delayedDataI[k] = osDelayI[(indexOsDelay - 63 ) & 0X3F ];
199- delayedDataQ[k] = osDelayQ[(indexOsDelay - 63 ) & 0X3F ];
200- indexOsDelay++;
201-
202- // Delay line to allow strongest envelope to be used for compensation
203- // We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
204- // Enter latest envelope to delay array
205- osEnv[indexOsEnv & 0X03 ] = sqrtf (
206- workingDataI[k]*workingDataI[k] + workingDataQ[k]*workingDataQ[k]);
207-
208- // look over the envelope curve to find the max
209- float32_t eMax = 0 .0f ;
210- if (osEnv[(indexOsEnv) & 0X03 ] > eMax) // One just entered
211- eMax = osEnv[(indexOsEnv) & 0X03 ];
212- if (osEnv[(indexOsEnv-1 ) & 0X03 ] > eMax) // Entered one before
213- eMax = osEnv[(indexOsEnv-1 ) & 0X03 ];
214- if (osEnv[(indexOsEnv-2 ) & 0X03 ] > eMax) // Entered one before that
215- eMax = osEnv[(indexOsEnv-2 ) & 0X03 ];
216- if (eMax < 1 .0f )
217- eMax = 1 .0f ; // Below clipping region
218-
219- indexOsEnv++;
220-
221- // Clip the signal to 1.0. -2 allows 1 look ahead on signal.
222- float32_t eCorrectedI = osDelayI[(indexOsDelay - 2 ) & 0X3F ] / eMax;
223- float32_t eCorrectedQ = osDelayQ[(indexOsDelay - 2 ) & 0X3F ] / eMax;
224- // Filtering is linear, so we only need to filter the difference between
225- // the signal and the clipper output. This needs less filtering, as the
226- // difference is many dB below the signal to begin with. Hershberger 2014
227- diffI[k] = osDelayI[(indexOsDelay - 2 ) & 0X3F ] - eCorrectedI;
228- diffQ[k] = osDelayQ[(indexOsDelay - 2 ) & 0X3F ] - eCorrectedQ;
229- } // End, for k=0 to 63
230-
231- // Filter the differences, osFilter has 129 taps and 64 delay
232- arm_fir_f32 (&firInstOShootI, diffI, diffI, nC);
233- arm_fir_f32 (&firInstOShootQ, diffQ, diffQ, nC);
234-
235- // Do the overshoot compensation
236- for (int k=0 ; k<64 ; k++)
237- {
238- workingDataI[k] = delayedDataI[k] - gainCompensate*diffI[k];
239- workingDataQ[k] = delayedDataQ[k] - gainCompensate*diffQ[k];
240- }
175+ // Ready to compensate for filter overshoots
176+ for (int k=0 ; k<64 ; k++)
177+ {
178+ /* ======= Sidebar: Circular 2^n length delay arrays ========
179+ *
180+ * The length of the array, N,
181+ * must be a power of 2. For example N=2^6 = 64. The minimum
182+ * delay possible is the trivial case of 0 up to N-1.
183+ * As in C, let i be the index of the N array elements which
184+ * would range from 0 to N-1. If p is an integer, that is a power
185+ * of 2 also, with p >= n, it can serve as an index to the
186+ * delay array by "ANDing" it with (N-1). That is,
187+ * i = p & (N-1). It can be convenient if the largest
188+ * possible value of the integer p, plus 1, is an integer multiple
189+ * of the arrray size N, as then the rollover of p will not cause
190+ * a jump in i. For instance, if p is an uint8_t with a maximum
191+ * value of pmax=255, (pmax+1)/N = (255+1)/64 = 4, which is an
192+ * integer. This combination will have no problems from rollover
193+ * of p.
194+ *
195+ * The new data point is entered at index p & (N - 1). To
196+ * achieve a delay of d, the output of the delay array is taken
197+ * at index ((p-d) & (N-1)). The index is then incremented by 1.
198+ * ========================================================== */
199+
200+ // Circular delay line for signal to align data with FIR output
201+ // Put I & Q data points into the delay arrays
202+ osDelayI[indexOsDelay & 0X3F ] = workingDataI[k];
203+ osDelayQ[indexOsDelay & 0X3F ] = workingDataQ[k];
204+ // Remove 64 points delayed data from line and save for later
205+ delayedDataI[k] = osDelayI[(indexOsDelay - 63 ) & 0X3F ];
206+ delayedDataQ[k] = osDelayQ[(indexOsDelay - 63 ) & 0X3F ];
207+ indexOsDelay++;
208+
209+ // Delay line to allow strongest envelope to be used for compensation
210+ // We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
211+ // Enter latest envelope to delay array
212+ osEnv[indexOsEnv & 0X03 ] = sqrtf (
213+ workingDataI[k]*workingDataI[k] + workingDataQ[k]*workingDataQ[k]);
214+
215+ // look over the envelope curve to find the max
216+ float32_t eMax = 0 .0f ;
217+ if (osEnv[(indexOsEnv) & 0X03 ] > eMax) // One just entered
218+ eMax = osEnv[(indexOsEnv) & 0X03 ];
219+ if (osEnv[(indexOsEnv-1 ) & 0X03 ] > eMax) // Entered one before
220+ eMax = osEnv[(indexOsEnv-1 ) & 0X03 ];
221+ if (osEnv[(indexOsEnv-2 ) & 0X03 ] > eMax) // Entered one before that
222+ eMax = osEnv[(indexOsEnv-2 ) & 0X03 ];
223+ if (eMax < 1 .0f )
224+ eMax = 1 .0f ; // Below clipping region
225+
226+ indexOsEnv++;
227+
228+ // Clip the signal to 1.0. -2 allows 1 look ahead on signal.
229+ float32_t eCorrectedI = osDelayI[(indexOsDelay - 2 ) & 0X3F ] / eMax;
230+ float32_t eCorrectedQ = osDelayQ[(indexOsDelay - 2 ) & 0X3F ] / eMax;
231+ // Filtering is linear, so we only need to filter the difference between
232+ // the signal and the clipper output. This needs less filtering, as the
233+ // difference is many dB below the signal to begin with. Hershberger 2014
234+ diffI[k] = osDelayI[(indexOsDelay - 2 ) & 0X3F ] - eCorrectedI;
235+ diffQ[k] = osDelayQ[(indexOsDelay - 2 ) & 0X3F ] - eCorrectedQ;
236+ } // End, for k=0 to 63
237+
238+ // Filter the differences, osFilter has 129 taps and 64 delay
239+ arm_fir_f32 (&firInstOShootI, diffI, diffI, nC);
240+ arm_fir_f32 (&firInstOShootQ, diffQ, diffQ, nC);
241+
242+ // Do the overshoot compensation
243+ for (int k=0 ; k<64 ; k++)
244+ {
245+ workingDataI[k] = delayedDataI[k] - gainCompensate*diffI[k];
246+ workingDataQ[k] = delayedDataQ[k] - gainCompensate*diffQ[k];
247+ }
248+ } // End CESSB processing
241249
242250 // Finally interpolate to 48 or 96 ksps. Data is in workingDataI[k]
243251 // and is 64 samples for audio 48 ksps.
0 commit comments