📄 transmit.c
字号:
//==========================================================================================
// Filename: transmit.c
//
// Description: Functions related to the data transmition of a power line modem.
//
// Copyright (C) 2001 - 2003 Texas Instruments Incorporated
// Texas Instruments Proprietary Information
// Use subject to terms and conditions of TI Software License Agreement
//
// Revision History:
//==========================================================================================
#include "ofdm_modem.h"
#define WINDOW_SCALE 8
// When RAMP_TEST is defined, we replace the real preamble and data with a ramp
// waveform to make DMA debug easier
//#define RAMP_TEST
#if COMPILE_MODE == DSP_COMPILE
#include "intr.h" // Needed for interrupt enable/disable macros
#else // MEX: Fake macros for interrupt enable/disable
#define INTR_ENABLE(x)
#define INTR_DISABLE(x)
#endif
// When PEAK_DEBUG is TRUE, we spit out trace messages with the peak amplitude in each transmit frame
#define PEAK_DEBUG TRUE
//#define PEAK_DEBUG FALSE
// When PEAK_DEBUG is TRUE, scale the transmitted waveform to have constant peaks, boosting average
#define ADJUST_TX_PEAKS TRUE
//#define ADJUST_TX_PEAKS FALSE
//==========================================================================================
// global var for this file
//==========================================================================================
static const iCplx complexMap[4] =
{ 1, 0,
0, 1,
-1, 0,
0, -1 };
//--- Matlab code to generate FrameWindow values: -----------------------
// wLen = 16; wScale=8; win = round((.5 - .5*cos([0:wLen-1]/wLen*pi))*2^wScale); sprintf('%5.0f,',win)
static const i16 FrameWindow[WINDOW_LEN] =
// { 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256 }; // Unity gain throughout. Zero postfix.
{ 0, 2, 10, 22, 37, 57, 79, 103, 128, 153, 177, 199, 219, 234, 246, 254 }; // Raised cosine window
// { 0, 0, 0, 0, 0, 0, 0, 0, 256, 256, 256, 256, 256, 256, 256, 256 }; // Abrupt cutoff
//==========================================================================================
// Function: initCRCtable()
//
// Description: Init CRC-16 table
//
// Revision History:
//==========================================================================================
void initCRCtable(u16 *CRCtable)
{
u16 reg; // working register
i16 n, b; // counters
#if COMPILE_MODE == MEX_COMPILE
mexPrintf("CRC table init.\n");
#endif
reg = 0;
for( n = 0; n < 256; n++ )
{
reg = n << 8;
for( b = 0; b < 8; b++ )
{
if( reg & CRC_TOPBIT )
reg = (reg << 1) ^ CRC_POLYNOMIAL;
else
reg <<= 1;
}
*CRCtable++ = reg;
}
return;
}
//==========================================================================================
// Function: viterbiZero()
//
// Description: Force the end of the user data buffer to zero so that the receiving
// trelis returnes to zero at the end of the message.
//
// Revision History:
//==========================================================================================
void viterbiZero(u16 *pUserData)
{ // zeros for Viterbi zero rest of buffer
i16 zeroBits = (2*VITERBI_K + 1) + ( DATA_BUFFER_LEN*WORD_LEN - NUM_SYMBOLS);
pUserData += (DATA_BUFFER_LEN - 1); // point to last word in data buffer
while( zeroBits >= WORD_LEN )
{
*pUserData = 0; // zero this word
pUserData--; // point to previous word
zeroBits -= WORD_LEN;
//mexPrintf("zeroBits loop = %d\n", zeroBits);
}
*pUserData >>= zeroBits; // zero final bits
*pUserData <<= zeroBits;
return;
}
//==========================================================================================
// Function: appendParityCheckBytes()
//
// Description: Calculate parity check bytes for outer RS code and append.
// Data come in as 16 bit words. Take each byte and calc parity bytes
// Calc CRC on whole words.
//
// Revision History:
//==========================================================================================
void appendParityCheckBytes(u16 *pUserData, i16 numWords)
{
i16 wordCount = 0;
u16 dataByte;
u16 reg;
u16 *parity;
//---- init parity table ----------------------
if( CRCtableArray[1] == 0 )
initCRCtable( CRCtableArray );
//---- point to appended parity part of data buffer and zero out ---
parity = pUserData + numWords;
memset(parity, 0, (DATA_BUFFER_LEN-numWords)*sizeof(u16));
/*--------------------------------------------------------------
Read through the data to generate the CRC word.
Append the CRC word at the end of the user data in the data buffer
--------------------------------------------------------------*/
reg = CRC_REG_INIT;
for( wordCount = 0; wordCount < (numWords+1); wordCount++ )
{
dataByte = (*pUserData) >> BYTE_LEN;// high data byte
reg = (reg << BYTE_LEN)
^ CRCtableArray[ (reg >> (CRC_LEN-BYTE_LEN)) ] ^ dataByte;
dataByte = (*pUserData++) & 0x00FF; // low data byte
reg = (reg << BYTE_LEN)
^ CRCtableArray[ (reg >> (CRC_LEN-BYTE_LEN)) ] ^ dataByte;
}
*parity = reg;
return;
}
//==========================================================================================
// Function: scramble()
//
// Description: Scrambler using linear shift register with polynomial
// S(x) x^7 + x^4 + 1
// The default inital state is 127 (all ones).
//
// input data |
// |--------------| |
// | V V
// ->D-->D-->D-->D-.>D-->D-->D--> + -.-> +
// | 1 2 3 4 5 6 7 | |
// |---------------------------------| |
// output data V
//
// Revision History:
//==========================================================================================
void scramble(u16 *pUserData, u16 userDataLen)
{
i16 n;
u16 wordCount = 0;
while( wordCount < userDataLen )
for( n = 0; n < SCRAMBLER_LEN; n++ )
{
*pUserData++ ^= scramblerLookup[n];
wordCount++;
if( wordCount >= userDataLen )
break;
}
}
//==========================================================================================
// Function: CalcTxStartAddr()
//
// Description: Calculate the starting address in the TxSignal array, given the
// present pointer position and the requested block size.
//
// Revision History:
//==========================================================================================
#if COMPILE_MODE == DSP_COMPILE
TXDATA* CalcTxStartAddr(TXDATA* uCurrAddr, u16 uCnt)
{
TXDATA* uStart;
if ( (uCurrAddr + (uCnt * AFE_SIZE)) > (txSignalArray + TX_BUFFER_LEN))
//if ( ((uCurrAddr-txSignalArray) + (uCnt * AFE_SIZE)) > TX_BUFFER_LEN ) // fishing here
{
uStart = txSignalArray; // Too close to end; re-start at beginning
}
else
{
uStart = uCurrAddr; // Plenty of room; start transfer from present pointer
}
return (uStart);
}
#else
#define CalcTxStartAddr(x,y) x // MEX: Make a fake function that returns its own argument
#endif
#define TX_RAW_CLIP (12000) // 12000 = Crest factor of 3.59 (13.5 bits)
#define TX_PEAK_SETPOINT (32767L) // Max values allowed by transmit DAC
#define TX_GAIN_SCALE 11 // that is: 2048
#define TX_GAIN 2400 // (i16)(1.17 * (1<<TXGAIN_SCALE) )
#define TX_PEAK_CLIP (12000L) // Truncate at this level digitally
// instead of saturating the driver
//==========================================================================================
// Function: setTxPower()
//
// Description: Adjust the gain of the transmitted signal to get the desired power
// out of the codec.
//
// This function provides fine control of the amplitude of the signal generated by the IFFT.
// It also clips the signal at the level set by TX_PEAK_CLIP. This is useful in preventing the
// analog line driver from going into saturation since it takes the line driver amplifier a while
// to come out of saturation. That is, it's better to saturate the digital signal then the
// analog signal.
//
// Revision History:
//==========================================================================================
void setTxPower(iCplx* ifftBuf)
{
#if (ADJUST_TX_PEAKS == TRUE)
u16 n; // loop counter
i16 sample;
u16 uSample;
u16 magMax = 0;
u16 magMaxCnt = 0;
for(n=0; n<FFT_LEN; n++)
{
#if COMPILE_MODE == MEX_COMPILE
sample = (i16)(((i32)ifftBuf->re * TX_GAIN)>>TX_GAIN_SCALE);
#else // DSP_COMPILE
sample = (ifftBuf->re * TX_GAIN)>>TX_GAIN_SCALE;
#endif
uSample = abs(sample);
if( uSample > TX_PEAK_CLIP )
{
magMaxCnt++;
if( uSample > magMax )
{
magMax = uSample;
}
ifftBuf->re = (i16)Saturate( (i32)sample, -TX_PEAK_CLIP, TX_PEAK_CLIP);
}
else
{
ifftBuf->re = sample;
}
ifftBuf++;
}
#if SAVETRACE == TRUE
if( magMax > 0 )
{
PostErrorCode(0xBAE1, "setTxPower", "transmit.c", "Clipped transmit waveform");
SaveTraceData(0xDF00);
SaveTraceData(magMax);
SaveTraceData(magMaxCnt);
}
DebugDelay();
#endif
#endif
return;
}
//==========================================================================================
// Function: AdjustTxPeaks()
//
// Description: Adjust the gain of the transmitted signal to maintain constant peaks.
//
// ---------- CALCULATION OF RMS POWER AND CREST FACTOR ----------------------------------
// RMS value for each entire frame (except cyclic prefix) is
// RMSideal = FFT_LEN * sqrt(2 * CARRIER_LEN) = 256 * sqrt( 2 * 60) = 2804
// Because the cyclic prefix repeats just a portion of the frame, the actual RMS could be
// as little as
// RMSmin = (FFT_LEN / (FFT_LEN + CYCLIC_PREFIX_LEN)) = (256/(256+26)) = 0.91 * RMSideal
// or as high as
// RMSmax = 2 * RMSideal.
// However, for most waveforms, the RMS of the packet with cyclic prefix will be pretty
// close to RMSideal.
//
// The crest factor is defined as the ratio of the peak value divided by the RMS value.
// A sine wave has a crest factor of sqrt(2) = 1.414. An OFDM waveform could have a crest
// factor as high as CARRIER_LEN*sqrt(2) = 60*1.414 = 84 worst case. In general, however
// the crest factor looks like a gaussian distributed variable, with crest factors above 3.5
// occurring only about once per 1000 frames.
//
// In this function, we want to boost the RMS as high as possible without truncating the
// peak values. On the occassional rare frame with a huge crest factor that exceeds the
// limit, we will adjust the RMS gain as if the crest factor were right at the upper limit
// and then clip the peak that exceeds that limit.
//
//
// Revision History:
//==========================================================================================
void AdjustTxPeaks(iCplx* ifftBuf, u16 block, u16 frame)
{
u16 n; // loop counter
u16 magSample; // magnitude (abs) of waveform sample
u16 magMax = 0; // Peak magnitude
i16 txGain; // Gain to multiply tx signal
iCplx* fftBuf; // fft buffer pointer
//---- Find biggest sample. Its index indicates offset from beginning of frame --------
fftBuf = ifftBuf;
for(n=0; n<FFT_LEN; n++)
{
magSample = abs((fftBuf++)->re);
if( magSample > magMax ) // find the max real sample
{
magMax = magSample;
}
}
DebugDelay();
//----------------------------------------------------------------------------
// Calculate the gain that we will use to multiply the entire signal.
// The saturate term in the denominator limits the range of gains.
// Lower limit sets upper limit of gain to keep it from rolling over.
// Upper limit keeps us from squelching frames with really large peaks;
// on those, we will clip the peaks.
//----------------------------------------------------------------------------
txGain = (TX_PEAK_SETPOINT<<TX_GAIN_SCALE) / Saturate(magMax, 1<<TX_GAIN_SCALE, TX_RAW_CLIP);
//mexPrintf(" %d %8.4f\n", magMax, ((double)txGain)/((double)(1<<TX_GAIN_SCALE)) );
DebugDelay();
#if (ADJUST_TX_PEAKS == TRUE)
fftBuf = ifftBuf;
if (magMax < TX_RAW_CLIP)
{ // Reasonable input signal (common). No saturation needed, so just multiply gain.
for(n=0; n<FFT_LEN; n++)
{
#if COMPILE_MODE == MEX_COMPILE
(fftBuf++)->re = (i16)( ((i32)(fftBuf)->re * (i32)txGain)>>TX_GAIN_SCALE );
#else // DSP_COMPILE
(fftBuf++)->re = ((fftBuf)->re * txGain)>>TX_GAIN_SCALE; // Takes 1802 ticks. Uses MPY instruction.
//(fftBuf++)->re = ((i32)(fftBuf)->re * (i32)txGain)>>13; // Takes 10003 ticks. Uses L$$MPY function.
#endif
}
}
else // Big input signal (rare). Saturate each entry. Use 32-bit for intermediate results.
{
//mexPrintf("saturating..." );
for(n=0; n<FFT_LEN; n++)
{
(fftBuf++)->re = (i16)Saturate( ((i32)(fftBuf)->re * (i32)txGain)>>TX_GAIN_SCALE,
-TX_PEAK_SETPOINT,
TX_PEAK_SETPOINT);
}
// Keep track of every time we need to clip a frame
PostErrorCode(0xBAE1, "AdjustTxPeaks", "transmit.c", "Clipped transmit waveform");
#if SAVETRACE == TRUE
SaveTraceData(0xDF00 + (block<<4) + frame); //!!!DEBUG Put a marker in the trace buffer
SaveTraceData((u16)magMax); //!!!DEBUG Put a marker in the trace buffer
SaveTraceData((u16)txGain); //!!!DEBUG Put a marker in the trace buffer
#endif
DebugDelay();
}
#endif
#if (PEAK_DEBUG==TRUE)
#if SAVETRACE == TRUE
SaveTraceData(0xDE00 + (block<<4) + frame); //!!!DEBUG Put a marker in the trace buffer
SaveTraceData((u16)magMax); //!!!DEBUG Put a marker in the trace buffer
SaveTraceData((u16)txGain); //!!!DEBUG Put a marker in the trace buffer
#endif
#endif
DebugDelay();
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -