Skip to content

Commit 8fbac35

Browse files
committed
Added cessbProcessing control. Tnx KF5N
1 parent 5113b53 commit 8fbac35

4 files changed

Lines changed: 184 additions & 147 deletions

File tree

radioCESSB_Z_transmit_F32.cpp

Lines changed: 62 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ void radioCESSB_Z_transmit_F32::update(void) {
122122
workingDataQ[k] *= gainFactor;
123123
}
124124

125-
// Mesaure input power and peak envelope, SSB before any CESSB processing
125+
// Measure input power and peak envelope, SSB before any CESSB processing
126126
for(int k=0; k<nW; k++)
127127
{
128128
float32_t pwrWorkingData = workingDataI[k]*workingDataI[k] + workingDataQ[k]*workingDataQ[k];
@@ -147,73 +147,78 @@ void radioCESSB_Z_transmit_F32::update(void) {
147147
// LPF with gain of 2 built into coefficients, correct for added zeros.
148148
arm_fir_f32(&firInstInterpolate1I, workingDataI, workingDataI, nC);
149149
arm_fir_f32(&firInstInterpolate1Q, workingDataQ, workingDataQ, nC);
150+
150151
// WorkingDataI and Q are now at 24 ksps and ready for clipping
151152
// For input 48 ksps this produces 64 numbers
152153

153-
for(int kk=0; kk<nC; kk++)
154+
// Optional skip CESSB processing. Filtering & SSB generation unchanged. Thanks to Greg KF5N
155+
if(cessbProcessing) // Not skipping
154156
{
155-
float32_t power = workingDataI[kk]*workingDataI[kk] + workingDataQ[kk]*workingDataQ[kk];
156-
float32_t mag = sqrtf(power);
157-
if(mag > 1.0f) // This the clipping, scaled to 1.0, desired max
157+
for(int kk=0; kk<nC; kk++)
158158
{
159-
workingDataI[kk] /= mag;
160-
workingDataQ[kk] /= mag;
159+
float32_t power = workingDataI[kk]*workingDataI[kk] + workingDataQ[kk]*workingDataQ[kk];
160+
float32_t mag = sqrtf(power);
161+
if(mag > 1.0f) // This the clipping, scaled to 1.0, desired max
162+
{
163+
workingDataI[kk] /= mag;
164+
workingDataQ[kk] /= mag;
165+
}
161166
}
162-
}
163167

164-
// clipperIn needs spectrum control, so LP filter it.
165-
// Both BW of the signal and the sample rate have been doubled.
166-
arm_fir_f32(&firInstClipperI, workingDataI, workingDataI, nC);
167-
arm_fir_f32(&firInstClipperQ, workingDataQ, workingDataQ, nC);
168-
// Ready to compensate for filter overshoots
169-
for (int k=0; k<nC; k++)
170-
{
171-
// Circular delay line for signal to align data with FIR output
172-
// Put I & Q data points into the delay arrays
173-
osDelayI[indexOsDelay & 0X3F] = workingDataI[k];
174-
osDelayQ[indexOsDelay & 0X3F] = workingDataQ[k];
175-
// Remove 64 points delayed data from line and save for later
176-
delayedDataI[k] = osDelayI[(indexOsDelay - 63) & 0X3F];
177-
delayedDataQ[k] = osDelayQ[(indexOsDelay - 63) & 0X3F];
178-
indexOsDelay++;
179-
// Delay line to allow strongest envelope to be used for compensation
180-
// We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
181-
// Enter latest envelope to delay array
182-
osEnv[indexOsEnv & 0X03] = sqrtf(
183-
workingDataI[k]*workingDataI[k] + workingDataQ[k]*workingDataQ[k]);
168+
// clipperIn needs spectrum control, so LP filter it.
169+
// Both BW of the signal and the sample rate have been doubled.
170+
arm_fir_f32(&firInstClipperI, workingDataI, workingDataI, nC);
171+
arm_fir_f32(&firInstClipperQ, workingDataQ, workingDataQ, nC);
172+
// Ready to compensate for filter overshoots
173+
for (int k=0; k<nC; k++)
174+
{
175+
// Circular delay line for signal to align data with FIR output
176+
// Put I & Q data points into the delay arrays
177+
osDelayI[indexOsDelay & 0X3F] = workingDataI[k];
178+
osDelayQ[indexOsDelay & 0X3F] = workingDataQ[k];
179+
// Remove 64 points delayed data from line and save for later
180+
delayedDataI[k] = osDelayI[(indexOsDelay - 63) & 0X3F];
181+
delayedDataQ[k] = osDelayQ[(indexOsDelay - 63) & 0X3F];
182+
indexOsDelay++;
183+
// Delay line to allow strongest envelope to be used for compensation
184+
// We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
185+
// Enter latest envelope to delay array
186+
osEnv[indexOsEnv & 0X03] = sqrtf(
187+
workingDataI[k]*workingDataI[k] + workingDataQ[k]*workingDataQ[k]);
184188

185-
// look over the envelope curve to find the max
186-
float32_t eMax = 0.0f;
187-
if(osEnv[(indexOsEnv) & 0X03] > eMax) // Data point just entered
188-
eMax = osEnv[(indexOsEnv) & 0X03];
189-
if(osEnv[(indexOsEnv-1) & 0X03] > eMax) // Entered one before
190-
eMax = osEnv[(indexOsEnv-1) & 0X03];
191-
if(osEnv[(indexOsEnv-2) & 0X03] > eMax) // Entered one before that
192-
eMax = osEnv[(indexOsEnv-2) & 0X03];
193-
if(eMax < 1.0f)
194-
eMax = 1.0f; // Below clipping region
195-
indexOsEnv++;
189+
// look over the envelope curve to find the max
190+
float32_t eMax = 0.0f;
191+
if(osEnv[(indexOsEnv) & 0X03] > eMax) // Data point just entered
192+
eMax = osEnv[(indexOsEnv) & 0X03];
193+
if(osEnv[(indexOsEnv-1) & 0X03] > eMax) // Entered one before
194+
eMax = osEnv[(indexOsEnv-1) & 0X03];
195+
if(osEnv[(indexOsEnv-2) & 0X03] > eMax) // Entered one before that
196+
eMax = osEnv[(indexOsEnv-2) & 0X03];
197+
if(eMax < 1.0f)
198+
eMax = 1.0f; // Below clipping region
199+
indexOsEnv++;
196200

197-
// Clip the signal to 1.0. -2 allows 1 look ahead on signal.
198-
float32_t eCorrectedI = osDelayI[(indexOsDelay - 2) & 0X3F] / eMax;
199-
float32_t eCorrectedQ = osDelayQ[(indexOsDelay - 2) & 0X3F] / eMax;
200-
// Filtering is linear, so we only need to filter the difference between
201-
// the signal and the clipper output. This needs less filtering, as the
202-
// difference is many dB below the signal to begin with. Hershberger 2014
203-
diffI[k] = osDelayI[(indexOsDelay - 2) & 0X3F] - eCorrectedI;
204-
diffQ[k] = osDelayQ[(indexOsDelay - 2) & 0X3F] - eCorrectedQ;
205-
} // End, for k=0 to 63
201+
// Clip the signal to 1.0. -2 allows 1 look ahead on signal.
202+
float32_t eCorrectedI = osDelayI[(indexOsDelay - 2) & 0X3F] / eMax;
203+
float32_t eCorrectedQ = osDelayQ[(indexOsDelay - 2) & 0X3F] / eMax;
204+
// Filtering is linear, so we only need to filter the difference between
205+
// the signal and the clipper output. This needs less filtering, as the
206+
// difference is many dB below the signal to begin with. Hershberger 2014
207+
diffI[k] = osDelayI[(indexOsDelay - 2) & 0X3F] - eCorrectedI;
208+
diffQ[k] = osDelayQ[(indexOsDelay - 2) & 0X3F] - eCorrectedQ;
209+
} // End, for k=0 to 63
206210

207-
// Filter the differences, osFilter has 123 taps and 61 delay
208-
arm_fir_f32(&firInstOShootI, diffI, diffI, nC);
209-
arm_fir_f32(&firInstOShootQ, diffQ, diffQ, nC);
211+
// Filter the differences, osFilter has 123 taps and 61 delay
212+
arm_fir_f32(&firInstOShootI, diffI, diffI, nC);
213+
arm_fir_f32(&firInstOShootQ, diffQ, diffQ, nC);
210214

211-
// Do the overshoot compensation
212-
for(int k=0; k<nC; k++)
213-
{
214-
workingDataI[k] = delayedDataI[k] - gainCompensate*diffI[k];
215-
workingDataQ[k] = delayedDataQ[k] - gainCompensate*diffQ[k];
216-
}
215+
// Do the overshoot compensation
216+
for(int k=0; k<nC; k++)
217+
{
218+
workingDataI[k] = delayedDataI[k] - gainCompensate*diffI[k];
219+
workingDataQ[k] = delayedDataQ[k] - gainCompensate*diffQ[k];
220+
}
221+
} // end CESSB processing
217222

218223
// Measure average output power and peak envelope, after CESSB
219224
// but before gainOut

radioCESSB_Z_transmit_F32.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
* such as IIR. Minimize any linear-phase filtering such as FIR.
106106
* Such activities enhance the overshoots and defeat the purpose of CESSB.
107107
*/
108+
// Rev 14Oct24 Added on/off via cessbProcessing. Tnx KF5N.
108109

109110
#ifndef _radioCESSB_Z_transmit_f32_h
110111
#define _radioCESSB_Z_transmit_f32_h
@@ -158,6 +159,16 @@ class radioCESSB_Z_transmit_F32 : public AudioStream_F32 {
158159
//setBlockLength(128); Always default 128
159160
}
160161

162+
// A "setter" and "getter" methods. If cessbProcessing==false, CESSB processing is bypassed.
163+
// This is intended for digital modes. Greg KF5N August 16 2024
164+
void setProcessing(bool cessbActive) {
165+
cessbProcessing = cessbActive;
166+
}
167+
168+
bool getProcessing(void) {
169+
return cessbProcessing;
170+
}
171+
161172
// Sample rate starts at default 44.1 ksps. That will work. Filters
162173
// are designed for 48 and 96 ksps, however. This is a *required*
163174
// function at setup().
@@ -278,7 +289,8 @@ class radioCESSB_Z_transmit_F32 : public AudioStream_F32 {
278289
struct levelsZ levelData;
279290
audio_block_f32_t *inputQueueArray_f32[1];
280291
uint32_t jjj = 0; // Used for diagnostic printing
281-
292+
bool cessbProcessing = true; // If false, CESSB processing is bypassed.
293+
// Greg KF5N August 16 2024
282294
// Input/Output is at 48 or 96 ksps. Hilbert generation is at 12 ksps.
283295
// Clipping and overshoot processing is at 24 ksps.
284296
// Next line is to indicate that setSampleRateHz() has not executed

radioCESSBtransmit_F32.cpp

Lines changed: 96 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)