@ -117,7 +117,7 @@ void radioCESSBtransmit_F32::update(void) {
arm_fir_f32 ( & firInstWeaverQ , weaverMQ , workingDataQ , nW ) ;
// Note: Sine wave envelope gain from blockIn->data[kk] to here is gainIn
// Mes aure input power and peak envelope, SSB before any CESSB processing
// Meas ure input power and peak envelope, SSB before any CESSB processing
for ( int k = 0 ; k < nW ; k + + )
{
float32_t pwrWorkingData = workingDataI [ k ] * workingDataI [ k ] + workingDataQ [ k ] * workingDataQ [ k ] ;
@ -142,102 +142,110 @@ void radioCESSBtransmit_F32::update(void) {
// LPF with gain of 2 built into coefficients, correct for zeros.
arm_fir_f32 ( & firInstInterpolate1I , workingDataI , workingDataI , nC ) ;
arm_fir_f32 ( & firInstInterpolate1Q , workingDataQ , workingDataQ , nC ) ;
// WorkingDataI and Q are now at 24 ksps and ready for clipping
// For input 48 ksps this produces 64 numbers
// Voltage gain from blockIn->data to here for small sine wave is 1.0
for ( int kk = 0 ; kk < nC ; kk + + )
// Optional skip of CESSB processing. Filtering & SSB generation unchanged. Thanks to Greg KF5N
if ( cessbProcessing ) // Not skipping
{
float32_t power = workingDataI [ kk ] * workingDataI [ kk ] + workingDataQ [ kk ] * workingDataQ [ kk ] ;
float32_t mag = sqrtf ( power ) ;
if ( mag > 1.0f ) // This the clipping, scaled to 1.0, desired max
for ( int kk = 0 ; kk < nC ; kk + + )
{
workingDataI [ kk ] / = mag ;
workingDataQ [ kk ] / = mag ;
float32_t power = workingDataI [ kk ] * workingDataI [ kk ] + workingDataQ [ kk ] * workingDataQ [ kk ] ;
float32_t mag = sqrtf ( power ) ;
if ( mag > 1.0f ) // This the clipping, scaled to 1.0, desired max
{
workingDataI [ kk ] / = mag ;
workingDataQ [ kk ] / = mag ;
}
powerSum0 + = power ; // For measuring amount of clipping
if ( mag > maxMag0 )
maxMag0 = mag ;
}
powerSum0 + = power ; // For measuring amount of clipping
if ( mag > maxMag0 )
maxMag0 = mag ;
}
// clipperIn needs spectrum control, so LP filter it. Same filter coeffs as Weaver.
// Both BW of the signal and the sample rate have been doubled.
arm_fir_f32 ( & firInstClipperI , workingDataI , workingDataI , nC ) ;
arm_fir_f32 ( & firInstClipperQ , workingDataQ , workingDataQ , nC ) ;
// clipperIn needs spectrum control, so LP filter it. Same filter coeffs as Weaver.
// Both BW of the signal and the sample rate have been doubled.
arm_fir_f32 ( & firInstClipperI , workingDataI , workingDataI , nC ) ;
arm_fir_f32 ( & firInstClipperQ , workingDataQ , workingDataQ , nC ) ;
// Ready to compensate for filter overshoots
for ( int k = 0 ; k < 64 ; k + + )
{
/* ======= Sidebar: Circular 2^n length delay arrays ========
*
* The length of the array , N ,
* must be a power of 2. For example N = 2 ^ 6 = 64. The minimum
* delay possible is the trivial case of 0 up to N - 1.
* As in C , let i be the index of the N array elements which
* would range from 0 to N - 1. If p is an integer , that is a power
* of 2 also , with p > = n , it can serve as an index to the
* delay array by " ANDing " it with ( N - 1 ) . That is ,
* i = p & ( N - 1 ) . It can be convenient if the largest
* possible value of the integer p , plus 1 , is an integer multiple
* of the arrray size N , as then the rollover of p will not cause
* a jump in i . For instance , if p is an uint8_t with a maximum
* value of pmax = 255 , ( pmax + 1 ) / N = ( 255 + 1 ) / 64 = 4 , which is an
* integer . This combination will have no problems from rollover
* of p .
*
* The new data point is entered at index p & ( N - 1 ) . To
* achieve a delay of d , the output of the delay array is taken
* at index ( ( p - d ) & ( N - 1 ) ) . The index is then incremented by 1.
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
// Circular delay line for signal to align data with FIR output
// Put I & Q data points into the delay arrays
osDelayI [ indexOsDelay & 0X3F ] = workingDataI [ k ] ;
osDelayQ [ indexOsDelay & 0X3F ] = workingDataQ [ k ] ;
// Remove 64 points delayed data from line and save for later
delayedDataI [ k ] = osDelayI [ ( indexOsDelay - 63 ) & 0X3F ] ;
delayedDataQ [ k ] = osDelayQ [ ( indexOsDelay - 63 ) & 0X3F ] ;
indexOsDelay + + ;
// Delay line to allow strongest envelope to be used for compensation
// We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
// Enter latest envelope to delay array
osEnv [ indexOsEnv & 0X03 ] = sqrtf (
workingDataI [ k ] * workingDataI [ k ] + workingDataQ [ k ] * workingDataQ [ k ] ) ;
// look over the envelope curve to find the max
float32_t eMax = 0.0f ;
if ( osEnv [ ( indexOsEnv ) & 0X03 ] > eMax ) // One just entered
eMax = osEnv [ ( indexOsEnv ) & 0X03 ] ;
if ( osEnv [ ( indexOsEnv - 1 ) & 0X03 ] > eMax ) // Entered one before
eMax = osEnv [ ( indexOsEnv - 1 ) & 0X03 ] ;
if ( osEnv [ ( indexOsEnv - 2 ) & 0X03 ] > eMax ) // Entered one before that
eMax = osEnv [ ( indexOsEnv - 2 ) & 0X03 ] ;
if ( eMax < 1.0f )
eMax = 1.0f ; // Below clipping region
indexOsEnv + + ;
// Clip the signal to 1.0. -2 allows 1 look ahead on signal.
float32_t eCorrectedI = osDelayI [ ( indexOsDelay - 2 ) & 0X3F ] / eMax ;
float32_t eCorrectedQ = osDelayQ [ ( indexOsDelay - 2 ) & 0X3F ] / eMax ;
// Filtering is linear, so we only need to filter the difference between
// the signal and the clipper output. This needs less filtering, as the
// difference is many dB below the signal to begin with. Hershberger 2014
diffI [ k ] = osDelayI [ ( indexOsDelay - 2 ) & 0X3F ] - eCorrectedI ;
diffQ [ k ] = osDelayQ [ ( indexOsDelay - 2 ) & 0X3F ] - eCorrectedQ ;
} // End, for k=0 to 63
// Filter the differences, osFilter has 129 taps and 64 delay
arm_fir_f32 ( & firInstOShootI , diffI , diffI , nC ) ;
arm_fir_f32 ( & firInstOShootQ , diffQ , diffQ , nC ) ;
// Do the overshoot compensation
for ( int k = 0 ; k < 64 ; k + + )
{
workingDataI [ k ] = delayedDataI [ k ] - gainCompensate * diffI [ k ] ;
workingDataQ [ k ] = delayedDataQ [ k ] - gainCompensate * diffQ [ k ] ;
}
// Ready to compensate for filter overshoots
for ( int k = 0 ; k < 64 ; k + + )
{
/* ======= Sidebar: Circular 2^n length delay arrays ========
*
* The length of the array , N ,
* must be a power of 2. For example N = 2 ^ 6 = 64. The minimum
* delay possible is the trivial case of 0 up to N - 1.
* As in C , let i be the index of the N array elements which
* would range from 0 to N - 1. If p is an integer , that is a power
* of 2 also , with p > = n , it can serve as an index to the
* delay array by " ANDing " it with ( N - 1 ) . That is ,
* i = p & ( N - 1 ) . It can be convenient if the largest
* possible value of the integer p , plus 1 , is an integer multiple
* of the arrray size N , as then the rollover of p will not cause
* a jump in i . For instance , if p is an uint8_t with a maximum
* value of pmax = 255 , ( pmax + 1 ) / N = ( 255 + 1 ) / 64 = 4 , which is an
* integer . This combination will have no problems from rollover
* of p .
*
* The new data point is entered at index p & ( N - 1 ) . To
* achieve a delay of d , the output of the delay array is taken
* at index ( ( p - d ) & ( N - 1 ) ) . The index is then incremented by 1.
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
// Circular delay line for signal to align data with FIR output
// Put I & Q data points into the delay arrays
osDelayI [ indexOsDelay & 0X3F ] = workingDataI [ k ] ;
osDelayQ [ indexOsDelay & 0X3F ] = workingDataQ [ k ] ;
// Remove 64 points delayed data from line and save for later
delayedDataI [ k ] = osDelayI [ ( indexOsDelay - 63 ) & 0X3F ] ;
delayedDataQ [ k ] = osDelayQ [ ( indexOsDelay - 63 ) & 0X3F ] ;
indexOsDelay + + ;
// Delay line to allow strongest envelope to be used for compensation
// We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
// Enter latest envelope to delay array
osEnv [ indexOsEnv & 0X03 ] = sqrtf (
workingDataI [ k ] * workingDataI [ k ] + workingDataQ [ k ] * workingDataQ [ k ] ) ;
// look over the envelope curve to find the max
float32_t eMax = 0.0f ;
if ( osEnv [ ( indexOsEnv ) & 0X03 ] > eMax ) // One just entered
eMax = osEnv [ ( indexOsEnv ) & 0X03 ] ;
if ( osEnv [ ( indexOsEnv - 1 ) & 0X03 ] > eMax ) // Entered one before
eMax = osEnv [ ( indexOsEnv - 1 ) & 0X03 ] ;
if ( osEnv [ ( indexOsEnv - 2 ) & 0X03 ] > eMax ) // Entered one before that
eMax = osEnv [ ( indexOsEnv - 2 ) & 0X03 ] ;
if ( eMax < 1.0f )
eMax = 1.0f ; // Below clipping region
indexOsEnv + + ;
// Clip the signal to 1.0. -2 allows 1 look ahead on signal.
float32_t eCorrectedI = osDelayI [ ( indexOsDelay - 2 ) & 0X3F ] / eMax ;
float32_t eCorrectedQ = osDelayQ [ ( indexOsDelay - 2 ) & 0X3F ] / eMax ;
// Filtering is linear, so we only need to filter the difference between
// the signal and the clipper output. This needs less filtering, as the
// difference is many dB below the signal to begin with. Hershberger 2014
diffI [ k ] = osDelayI [ ( indexOsDelay - 2 ) & 0X3F ] - eCorrectedI ;
diffQ [ k ] = osDelayQ [ ( indexOsDelay - 2 ) & 0X3F ] - eCorrectedQ ;
} // End, for k=0 to 63
// Filter the differences, osFilter has 129 taps and 64 delay
arm_fir_f32 ( & firInstOShootI , diffI , diffI , nC ) ;
arm_fir_f32 ( & firInstOShootQ , diffQ , diffQ , nC ) ;
// Do the overshoot compensation
for ( int k = 0 ; k < 64 ; k + + )
{
workingDataI [ k ] = delayedDataI [ k ] - gainCompensate * diffI [ k ] ;
workingDataQ [ k ] = delayedDataQ [ k ] - gainCompensate * diffQ [ k ] ;
}
} // End CESSB processing
// Finally interpolate to 48 or 96 ksps. Data is in workingDataI[k]
// and is 64 samples for audio 48 ksps.