Commit 7520ae6e authored by Raymond Knopp's avatar Raymond Knopp

Merge branch 'enhancement-10-harmony' of...

Merge branch 'enhancement-10-harmony' of https://gitlab.eurecom.fr/oai/openairinterface5g into enhancement-10-harmony
parents b96a6e4e d5d1dd0e
...@@ -38,40 +38,44 @@ ...@@ -38,40 +38,44 @@
* \warning * \warning
*/ */
#ifndef USER_MODE
#include "if4_tools.h"
#include <stdint.h>
#else
#include "PHY/LTE_TRANSPORT/if4_tools.h" #include "PHY/LTE_TRANSPORT/if4_tools.h"
#endif
// Define how data blocks are stored and transferred // Define how data blocks are stored and transferred
//void send_IF4(PHY_VARS_eNB *eNB, int subframe){
void send_IF4(PHY_VARS_eNB *eNB, int subframe){ //eNB_proc_t *proc = &eNB->proc;
eNB_proc_t *proc = &eNB->proc;
//int frame=proc->frame_tx; //int frame=proc->frame_tx;
//int subframe=proc->subframe_tx; //int subframe=proc->subframe_tx;
LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; //LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms;
uint32_t i,j; //uint32_t i,j;
float *data_block = malloc(length*sizeof(long)); //float *data_block = malloc(length*sizeof(long));
// Generate IF4 packet (for now DL) with frame status information // Generate IF4 packet (for now DL) with frame status information
dl_packet = gen_IF4_dl_packet( /* ADD INFO and data_block pointer */ ); //dl_packet = gen_IF4_dl_packet( /* ADD INFO and data_block pointer */ );
for(i=0; i<fp->symbols_per_tti; i++) { //for(i=0; i<fp->symbols_per_tti; i++) {
// Do compression of the two parts and generate data blocks // Do compression of the two parts and generate data blocks
symbol = eNB->common_vars.txdataF[0][0 /*antenna number*/][subframe*fp->ofdm_symbol_size*(fp->symbols_per_tti)] //symbol = eNB->common_vars.txdataF[0][0 /*antenna number*/][subframe*fp->ofdm_symbol_size*(fp->symbols_per_tti)]
data_block[j] = Atan(symbol[fp->ofmd_symbol_size - NrOfNonZeroValues + j -1])<<16 + Atan(symbol[fp->ofmd_symbol_size - NrOfNonZeroValues + j]); //data_block[j] = Atan(symbol[fp->ofmd_symbol_size - NrOfNonZeroValues + j -1])<<16 + Atan(symbol[fp->ofmd_symbol_size - NrOfNonZeroValues + j]);
data_block[j+NrOfNonZeroValues] = Atan(subframe[i][j+1])<<16 + Atan(subframe[i][j+2]); //data_block[j+NrOfNonZeroValues] = Atan(subframe[i][j+1])<<16 + Atan(subframe[i][j+2]);
// Set data blocks and update subframe no./other information to generated packet // Set data blocks and update subframe no./other information to generated packet
// Write the packet(s) to the fronthaul // Write the packet(s) to the fronthaul
} //}
} //}
void recv_IF4( /* ADD INFO and data_block pointer */ ) { void recv_IF4( /* ADD INFO and data_block pointer */ ) {
...@@ -83,83 +87,83 @@ void recv_IF4( /* ADD INFO and data_block pointer */ ) { ...@@ -83,83 +87,83 @@ void recv_IF4( /* ADD INFO and data_block pointer */ ) {
} }
IF4_dl_packet gen_IF4_dl_packet( /* ADD INFO and data_block pointer */ ) { struct IF4_dl_packet gen_IF4_dl_packet( /* ADD INFO and data_block pointer */ ) {
IF4_dl_packet dl_packet; struct IF4_dl_packet dl_packet;
// Set destination and source address // Set destination and source address
// Set Type and Sub-Type // Set Type and Sub-Type
dl_packet.type = ; //08_0A ? dl_packet.type = 0x080A;
dl_packet.sub_type = 0x0020; dl_packet.sub_type = 0x0020;
// Leave reserved as it is // Leave reserved as it is
//dl_packet.rsvd = ; //dl_packet.rsvd = ;
// Set frame status // Set frame status
dl_packet.frame_status.ant_num = ; dl_packet.frame_status.ant_num = 0;
dl_packet.frame_status.ant_start = ; dl_packet.frame_status.ant_start = 0;
dl_packet.frame_status.rf_num = ; dl_packet.frame_status.rf_num = 0;
dl_packet.frame_status.sf_num = ; dl_packet.frame_status.sf_num = 0;
dl_packet.frame_status.sym_num = ; dl_packet.frame_status.sym_num = 0;
//dl_packet.frame_status.rsvd = ; //dl_packet.frame_status.rsvd = ;
// Set data blocks if sent // Set data blocks if sent
if (data_block != NULL) { //if (data_block != NULL) {
//
} else { //} else {
//
} //}
// Set frame check sequence // Set frame check sequence
dl_packet.fcs = ; dl_packet.fcs = 0;
return dl_packet; return dl_packet;
} }
IF4_ul_packet gen_IF4_ul_packet( /* ADD INFO and data_block pointer */ ) { struct IF4_ul_packet gen_IF4_ul_packet( /* ADD INFO and data_block pointer */ ) {
IF4_ul_packet ul_packet; struct IF4_ul_packet ul_packet;
// Set destination and source address // Set destination and source address
// Set Type and Sub-Type // Set Type and Sub-Type
ul_packet.type = ; //08_0A ? ul_packet.type = 0x080A;
ul_packet.sub_type = 0x0019; ul_packet.sub_type = 0x0019;
// Leave reserved as it is // Leave reserved as it is
//ul_packet.rsvd = ; //ul_packet.rsvd = ;
// Set frame status // Set frame status
ul_packet.frame_status.ant_num = ; ul_packet.frame_status.ant_num = 0;
ul_packet.frame_status.ant_start = ; ul_packet.frame_status.ant_start = 0;
ul_packet.frame_status.rf_num = ; ul_packet.frame_status.rf_num = 0;
ul_packet.frame_status.sf_num = ; ul_packet.frame_status.sf_num = 0;
ul_packet.frame_status.sym_num = ; ul_packet.frame_status.sym_num = 0;
//ul_packet.frame_status.rsvd = ; //ul_packet.frame_status.rsvd = ;
// Set antenna specific gain // Set antenna specific gain
ul_packet.gain0.exponent = ; ul_packet.gain0.exponent = 0;
//ul_packet.gain0.rsvd = ; //ul_packet.gain0.rsvd = ;
// Set data blocks if sent // Set data blocks if sent
if (data_block != NULL) { //if (data_block != NULL) {
//
} else { //} else {
//
} //}
// Set frame check sequence // Set frame check sequence
ul_packet.fcs = ; ul_packet.fcs = 0;
return ul_packet; return ul_packet;
} }
IF4_prach_packet gen_IF4_prach_packet( /* ADD INFO and data_block pointer */ ) { struct IF4_prach_packet gen_IF4_prach_packet( /* ADD INFO and data_block pointer */ ) {
IF4_prach_packet prach_packet; struct IF4_prach_packet prach_packet;
// Set destination and source address // Set destination and source address
// Set Type and Sub-Type // Set Type and Sub-Type
prach_packet.type = ; //08_0A ? prach_packet.type = 0x080A;
prach_packet.sub_type = 0x0021; prach_packet.sub_type = 0x0021;
// Leave reserved as it is // Leave reserved as it is
...@@ -167,20 +171,27 @@ IF4_prach_packet gen_IF4_prach_packet( /* ADD INFO and data_block pointer */ ) { ...@@ -167,20 +171,27 @@ IF4_prach_packet gen_IF4_prach_packet( /* ADD INFO and data_block pointer */ ) {
// Set LTE Prach configuration // Set LTE Prach configuration
//prach_packet.prach_conf.rsvd = ; //prach_packet.prach_conf.rsvd = ;
prach_packet.prach_conf.ant = ; prach_packet.prach_conf.ant = 0;
prach_packet.prach_conf.rf_num = ; prach_packet.prach_conf.rf_num = 0;
prach_packet.prach_conf.sf_num = ; prach_packet.prach_conf.sf_num = 0;
prach_packet.prach_conf.exponent = ; prach_packet.prach_conf.exponent = 0;
// Set data blocks if sent // Set data blocks if sent
if (data_block != NULL) { //if (data_block != NULL) {
//
} else { //} else {
//
} //}
// Set frame check sequence // Set frame check sequence
prach_packet.fcs = ; prach_packet.fcs = 0;
return prach_packet; return prach_packet;
} }
int main(){
uint32_t i=0;
return 0;
}
...@@ -38,8 +38,6 @@ ...@@ -38,8 +38,6 @@
* \warning * \warning
*/ */
#include <stdint.h>
/// IF4 Frame Status (32 bits) /// IF4 Frame Status (32 bits)
struct IF4_frame_status { struct IF4_frame_status {
/// Antenna Numbers /// Antenna Numbers
...@@ -59,9 +57,9 @@ struct IF4_frame_status { ...@@ -59,9 +57,9 @@ struct IF4_frame_status {
/// IF4 Antenna Gain (16 bits) /// IF4 Antenna Gain (16 bits)
struct IF4_gain { struct IF4_gain {
/// Reserved /// Reserved
uint32_t rsvd:10; uint16_t rsvd:10;
/// FFT Exponent Output /// FFT Exponent Output
uint32_t exponent:6; uint16_t exponent:6;
}; };
/// IF4 LTE PRACH Configuration (32 bits) /// IF4 LTE PRACH Configuration (32 bits)
...@@ -77,7 +75,7 @@ struct IF4_lte_prach_conf { ...@@ -77,7 +75,7 @@ struct IF4_lte_prach_conf {
/// FFT Exponent Output /// FFT Exponent Output
uint32_t exponent:6; uint32_t exponent:6;
}; };
struct IF4_dl_packet { struct IF4_dl_packet {
/// Destination Address /// Destination Address
...@@ -90,13 +88,14 @@ struct IF4_dl_packet { ...@@ -90,13 +88,14 @@ struct IF4_dl_packet {
/// Reserved /// Reserved
uint32_t rsvd; uint32_t rsvd;
/// Frame Status /// Frame Status
IF4_frame_status frame_status; struct IF4_frame_status frame_status;
/// Data Blocks /// Data Blocks
/// Frame Check Sequence /// Frame Check Sequence
uint32_t fcs; uint32_t fcs;
}; };
struct IF4_ul_packet { struct IF4_ul_packet {
/// Destination Address /// Destination Address
...@@ -109,23 +108,23 @@ struct IF4_ul_packet { ...@@ -109,23 +108,23 @@ struct IF4_ul_packet {
/// Reserved /// Reserved
uint32_t rsvd; uint32_t rsvd;
/// Frame Status /// Frame Status
IF4_frame_status frame_status; struct IF4_frame_status frame_status;
/// Gain 0 /// Gain 0
IF4_gain gain0; struct IF4_gain gain0;
/// Gain 1 /// Gain 1
IF4_gain gain1; struct IF4_gain gain1;
/// Gain 2 /// Gain 2
IF4_gain gain2; struct IF4_gain gain2;
/// Gain 3 /// Gain 3
IF4_gain gain3; struct IF4_gain gain3;
/// Gain 4 /// Gain 4
IF4_gain gain4; struct IF4_gain gain4;
/// Gain 5 /// Gain 5
IF4_gain gain5; struct IF4_gain gain5;
/// Gain 6 /// Gain 6
IF4_gain gain6; struct IF4_gain gain6;
/// Gain 7 /// Gain 7
IF4_gain gain7; struct IF4_gain gain7;
/// Data Blocks /// Data Blocks
/// Frame Check Sequence /// Frame Check Sequence
...@@ -144,7 +143,7 @@ struct IF4_prach_packet { ...@@ -144,7 +143,7 @@ struct IF4_prach_packet {
/// Reserved /// Reserved
uint32_t rsvd; uint32_t rsvd;
/// LTE Prach Configuration /// LTE Prach Configuration
IF4_lte_prach_conf prach_conf; struct IF4_lte_prach_conf prach_conf;
/// Prach Data Block (one antenna) /// Prach Data Block (one antenna)
/// Frame Check Sequence /// Frame Check Sequence
...@@ -153,12 +152,12 @@ struct IF4_prach_packet { ...@@ -153,12 +152,12 @@ struct IF4_prach_packet {
// Needs to be checked // Needs to be checked
IF4_dl_packet gen_IF4_dl_packet(); struct IF4_dl_packet gen_IF4_dl_packet();
IF4_ul_packet gen_IF4_ul_packet(); struct IF4_ul_packet gen_IF4_ul_packet();
IF4_prach_packet gen_IF4_prach_packet(); struct IF4_prach_packet gen_IF4_prach_packet();
void send_IF4(PHY_VARS_eNB *eNB, int subframe); void send_IF4( /* ADD INFO */ );
void recv_IF4(PHY_VARS_eNB *eNB, int subframe); void recv_IF4( /* ADD INFO */ );
/**
* @companders.c - implementation
*
* @copy Copyright (C) <2012> <M. A. Chatterjee>
* @author M A Chatterjee <deftio [at] deftio [dot] com>
* @version 1.01 M. A. Chatterjee, cleaned up naming
*
* This file contains integer math compander functions for analog/audio representations on
* embedded systems. Linear2Alaw derived from Mark Spencer, Sun Microsystem and from
* the A Law specification
*
* @license:
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*/
#include "companders.h"
DIO_s8 DIO_LinearToALaw(DIO_s16 sample)
{
const DIO_s16 cClip = 32635;
const static DIO_s8 LogTable[128] =
{
1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};
DIO_s32 sign, exponent, mantissa;
DIO_s8 compandedValue;
sample = (sample ==-32768) ? -32767 : sample;
sign = ((~sample) >> 8) & 0x80;
if (!sign)
sample = (short)-sample;
if (sample > cClip)
sample = cClip;
if (sample >= 256)
{
exponent = (int)LogTable[(sample >> 8) & 0x7F];
mantissa = (sample >> (exponent + 3) ) & 0x0F;
compandedValue = ((exponent << 4) | mantissa);
}
else
{
compandedValue = (unsigned char)(sample >> 4);
}
compandedValue ^= (sign ^ 0x55);
return compandedValue;
}
DIO_s16 DIO_ALawToLinear(DIO_s8 aLawByte)
{
const static DIO_s16 ALawDecompTable[256]={
5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
344, 328, 376, 360, 280, 264, 312, 296,
472, 456, 504, 488, 408, 392, 440, 424,
88, 72, 120, 104, 24, 8, 56, 40,
216, 200, 248, 232, 152, 136, 184, 168,
1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
688, 656, 752, 720, 560, 528, 624, 592,
944, 912, 1008, 976, 816, 784, 880, 848,
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
-3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
-22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
-30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
-11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
-15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
-344, -328, -376, -360, -280, -264, -312, -296,
-472, -456, -504, -488, -408, -392, -440, -424,
-88, -72, -120, -104, -24, -8, -56, -40,
-216, -200, -248, -232, -152, -136, -184, -168,
-1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
-1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
-688, -656, -752, -720, -560, -528, -624, -592,
-944, -912, -1008, -976, -816, -784, -880, -848};
DIO_s16 addr = ((DIO_s16)aLawByte)+128; // done for compilers with poor expr type enforcement
return ALawDecompTable[addr];
}
// see companders.h
// fixed-radix IIR averager implementation supporting arbitrarily chosen windows
DIO_s32 DIO_IIRavgFR (DIO_s32 prevAvg, DIO_u16 windowLen, DIO_s16 newSample, DIO_u8 radix)
{
DIO_s32 iirAvg=0;
iirAvg = ((prevAvg * (windowLen-1)) + (DIO_I2FR(newSample,radix)))/windowLen;
return iirAvg;
}
// see companders.h
// fixed-radix IIR averager implementation using power-of-2 sized windows
// and only shift operations for cpu efficiency
DIO_s32 DIO_IIRavgPower2FR (DIO_s32 prevAvg, DIO_u8 windowLenInBits, DIO_s16 newSample, DIO_u8 radix)
{
DIO_s32 iirAvg=0;
iirAvg = (((prevAvg<<windowLenInBits)-prevAvg) + (DIO_I2FR(newSample,radix)))>>windowLenInBits;
return iirAvg;
}
/**
* @companders.h - header definition file for embedded companding routines
*
* @copy Copyright (C) <2001-2012> <M. A. Chatterjee>
* @author M A Chatterjee <deftio [at] deftio [dot] com>
* @version 1.01 M. A. Chatterjee, cleaned up naming
*
* This file contains integer math settable fixed point radix math routines for
* use on systems in which floating point is not desired or unavailable.
*
* @license:
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*/
#ifndef __DIO_Compand_h__
#define __DIO_Compand_h__
#ifdef __cplusplus
extern "C"
{
#endif
//"DIO" prefixes are to assist in prevent name collisions if global namespace is used.
//typedefs ... change for platform dependant register size
// u/s = unsigned/signed 8/16/32 = num of bits
// keywords "int" and "long" in C/C++ are platform dependant
typedef unsigned char DIO_u8 ;
typedef signed char DIO_s8 ;
typedef unsigned short DIO_u16 ;
typedef signed short DIO_s16 ;
typedef unsigned long DIO_u32 ;
typedef signed long DIO_s32 ;
// macros for converting from Fixed-Radix to integer, vice-versa
// r represents radix precision in bits, converts to/from integer w truncation
#define DIO_I2FR(x,r) ((x)<<(r))
#define DIO_FR2I(x,r) ((x)>>(r))
// convert FR to double, this is for debug only and WILL NOT compile under many embedded systems.
// use this in test harnesses. Since this is a macro if its not used it won't expand / link
#define DIO_FR2D(x,r) ((double)(((double)(x))/((double)(1<<(r)))))
//convert signed linear 16 bit sample to an 8 bit A-Law companded sample
DIO_s8 DIO_LinearToALaw(DIO_s16 sample);
//convert 8bit Alaw companded representation back to linear 16 bit
DIO_s16 DIO_ALawToLinear(DIO_s8 aLawByte);
//DC Offset correction for integer companders
//IIR: y_0=(y_1*(w-1)+x_0)/(w)
//where w is the window length
//below are fixed radix precision IIR averagers which allow runtime tradeoffs for windowLen & precision
//Note that (windowLen)*(1<<radix) must < 32767
//DIO_IIRavgPower2FR() allows any window length but uses a divide instruction.
//output is in radix number of bits
DIO_s32 DIO_IIRavgFR (DIO_s32 prevAvg, DIO_u16 windowLen, DIO_s16 newSample, DIO_u8 radix);
//DIO_IIRavgPower2FR() similar to above, but window length is specified as a number of bits
//which removes the need for a divide in the implementation
//outpit is in radix number of bits
DIO_s32 DIO_IIRavgPower2FR (DIO_s32 prevAvg, DIO_u8 windowLenInBits, DIO_s16 newSample, DIO_u8 radix);
#ifdef __cplusplus
}
#endif
#endif /* __DIO_Compand_h__ */
/**
* @file compandit.c - implementation test file for integer companding with
* compand/uncompand support & IIR correction
*
* @copy Copyright (C) <2012> <M. A. Chatterjee>
* @author M A Chatterjee <deftio [at] deftio [dot] com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*/
#include <stdio.h>
#include <string.h>
#include "companders.h"
//===============================================
//main program for testing the functions
int main (int argc, char *argv[])
{
int ret_val = 0;
int i=0,j=0;
printf("\n============================================================\n");
printf("compandit library quikie program\n");
printf("M. A. Chatterjee (c) 2012\n\n");
printf("These routines were developed for use on embedded projects\n");
printf("for more info see the accompanying compand.txt\n");
printf("\n");
//uncomment to
//show how linear-->alaw-->linear-->alaw progression / quantization error works
/*
{
char alaw=0,alaw2;
short rev=0;
for (i= -32768; i <= 32767; i++)
{
alaw = DIO_LinearToALaw((short)i);
rev = DIO_ALawToLinear(alaw);
alaw2 =DIO_LinearToALaw(rev);
if (alaw != alaw2)
printf("***********");
printf("%7d %7d %7d %7d\n",i,(int)alaw,(int)rev,(int)alaw2);
}
}
// IIR averager examples
//IIR window length of 8 samples, using fractional precision of 4 bits
{
int a3=0, a4=0;
unsigned char rad=4; //4 bits fixed-radix fractional precision
printf(" index wave IIRav(i) IIRav(f) IIRavP2(i) IIRavP2(f)\n");
for (i=0; i < 300; i++)
{
j=(i&0x3f)-20; // triangle wave with range -20 to + 43
a3= DIO_IIRavgFR(a3,8,j,rad);
a4= DIO_IIRavgPower2FR(a4,3,j,rad);
printf("%6d %6d %6d %9.4f %6d %9.4f \n",i,j,
DIO_FR2I(a3,rad),DIO_FR2D(a3,rad) ,DIO_FR2I(a4,rad),DIO_FR2D(a4,rad));
}
}
//IIR window length of 64 samples
{
int a3=0, a4=0;
unsigned char rad=6; //rad is the number of bits of precision
printf(" index wave IIRav(i) IIRav(f) IIRavP2(i) IIRavP2(f)\n");
for (i=0; i < 300; i++)
{
j=(i&0x3f)-20; // triangle wave with range -20 to + 43
a3= DIO_IIRavgFR(a3,64,j,rad);
a4= DIO_IIRavgPower2FR(a4,6,j,rad);
printf("%6d %6d %6d %9.4f %6d %9.4f \n",i,j,
DIO_FR2I(a3,rad),DIO_FR2D(a3,rad) ,DIO_FR2I(a4,rad),DIO_FR2D(a4,rad));
}
}
// */
//Typical microcontroller application. See readme-companders.txt
// the input here simulates an A/D which has a range 0..3.3V mapped as 12 bits (0..4095)
// with a DC bias of 1.55V ==> (1.55/3.3)*4095 counts = 1923 counts
//now window length of 256 is used for rejecting the resistor bias. at 8KHz this window
// would be approx 8000/256 ~= 31 Hz (not quite but explaining Z xforms is beyond what
// can be explained in this small space.)
//we seed the DC average at 3.3/2 = 1.65V (we guess its in the middle) and let the long window
//length hone in on the correct value. (1.65V/3.3V) *4095 = 2048 counts
{
int actualDCbias =1923;
int calculatedDCbias =2048; //2048 is our initial estimate as outlined above
unsigned char windowLenPow2InBits = 8; // 8 bit long window = 256 sample long window
unsigned char rad=6; //rad is the number of bits of precision
calculatedDCbias = DIO_I2FR(calculatedDCbias,rad);
printf(" index wave actDCbias calcDCbias calcDCbias(f) alaw\n");
for (i=0; i < 1000; i++) // if 8000 hz sample rate this represents the number of samples captured
{
j=(((i&0x3f)<<1)-63)+1923; // triangle wave w range 0..127 with a bias set at actualDCbias
calculatedDCbias = DIO_IIRavgPower2FR(calculatedDCbias,windowLenPow2InBits,j,rad);
printf("%6d %6d %6d %6d %9.4f %3d\n",i,j,actualDCbias,
DIO_FR2I(calculatedDCbias,rad),DIO_FR2D(calculatedDCbias,rad),
(int)(DIO_LinearToALaw(j-DIO_FR2I(calculatedDCbias,rad)) ));
}
}
printf("\n");
return ret_val;
}
/**
* @readme-companders.txt - using compander.c / .h and related functions
*
* @copy Copyright (C) <2001-2012> <M. A. Chatterjee>
* @author M A Chatterjee <deftio [at] deftio [dot] com>
*
* This document is a brief overview of the simple compander library with
* a small introductory disucssion about fixed radix math IIRs for microcontrollers
*
* @license:
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*/
Welcome...
The accompanying companders.c contains a small set of functions written in C using only integer math for companding operations. I developed this several years ago for use in several embedded interized math projects and this small, slightly cleaned up version is made available for the public here. It worked very well on M*CORE, 80x86 and ARM processors for small embedded systems.
usage:
#include "companders.h"
no other dependancies are required.
compile and link companders.c
***********************
About companding...
Companding is a special type of lossy compression in which linear format samples are converted to logarithmic representation in order to preserve dynamic range of the original linear sample at the expense of uniform precision. Companding is an old method and is free of patents (all have expired).
Theory of how companding works:
Suppose that we have a signed 16 bit linear sample in 2's complement math. The range of this sample can vary from -32768 to + 32767 in integer steps. Now lets put the constraint that we only have 8 bits with which to represent the data not the full 16 bits of the original linear number. A simple way to preserve high order information would be to linearly truncate off the lower 8 bits giving us a signed number from -128 to +127. We could make a mental note to keep track of the fact that we lost the lower 8 bits and so when use this representation we multiply by 8 bits (or 256) to preserve the input range.
so:
-128 * 256 = -32768
-127 * 256 = -32512
...
-1 * 256 = -256
0 * 256 = 0
1 * 256 = 256
2 * 256 = 512
...
126 * 256 = 32256
127 * 256 = 32512
Notice that the steps between these linearly rounded off samples are quite large. This truncated 8 bit representation would be very good at representing a linear quantity system such as linear displacement transducer which moves through its whole range as part of normal operation (like a door). but terrible at logarithmic phenomonen such as audio. Audio information tends to be grouped around zero with occaisonaly peaks of loudness. So with linear quantization soft audio would be lost due to the large quanitization steps at low volumes. To address this companders were developed for use in landline telephony for compressing audio data logarithmically instead of linearly. Hence we use more bits for audio samples near zero and less with audio samples near the integer max extremes.
A-Law and it cousin mu-Law are companders. Rather than represent samples in linear steps the more bits are allocated to samples near zero while larger magnitude (positive or negative) samples are represented with proportionately larger interval sizes.
Differential encoding (taking the difference of neighboring samples) can greatly help with compressing data to where we "have more bits" as well, provided samples rates are fast enough. Telephony typically operated with 8 bit non-differential (companded) samples at 8KHz.
In A-Law the following table (in bits) shows how a 13 bit signed linear integer is companded in to A-Law:
(source is Sun microsystems, a similar table is on wikipedia or the G.711 specification).
* Linear Input Code Compressed Code
* ------------------------ ---------------
* 0000000wxyza 000wxyz
* 0000001wxyza 001wxyz
* 000001wxyzab 010wxyz
* 00001wxyzabc 011wxyz
* 0001wxyzabcd 100wxyz
* 001wxyzabcde 101wxyz
* 01wxyzabcdef 110wxyz
* 1wxyzabcdefg 111wxyz
About this library:
This free library supports A-Law and IIR averages. A-Law is used for the companding while the IIR averagers can be used for for embedded ADCs where the zero point is set loosely. Since the companders are sensitive, and allocate more bits, to values near zero its important to define a good zero. For example a microcontroller has a pin with an ADC which is fed digitized audio signal. The smallest value for the microtroncoller is 0V = 0x00 and the 3.3V = 0x3ff for 10 effective bits of resolution. A resisitive analog divider is used to center the ADC near the half-input range or about 1.6V while the audio is capacitively coupled as shown here:
+3.3V
|
R
|
uC_ADC_Pin-----------C----audio_input
|
R
|
GND
However cheap resistors have tolerances 5 to 10%, so the setpoint voltage could easily be anywhere from 1.4 to 1.8V. To address this software should read the ADC before audio is coming in and determine the actual DC bias voltage set by the resistors. Then when audio is coming in this value is subtracted from the ADC input to give a signed number which is fed to the LinearToAlaw encoder.
If it not easily possible to turn off the analog / audio source then the long term DC average can be inferred by using one of the IIR functions included here with a long window length (perhaps 2000 samples if sampling at 8 KHz or about 1/4 second). These IIR functions are cheap to run in realtime even with reasonably high sample rates as they take little memory and are simple integer-math IIRs.
About the Fixed Radix (FR) Math IIR averages
The (Infinite Impulse Reponse) IIR functions included here use fixed radix math to represent fractional parts of an integer. By providing a settable radix (amount of bits devoted to fractional resolution), the programmer can make tradeoffs between resolution and dynamic range. The larger the radix specified the more bits that will be used in the fractional portion of the representation, which for smaller window sizes may not by necessary. There are more comprehensive ways to deal with fractional representation in binary systems (floating point, bigNum / arbitrary length registers, separation of numerator/denomators etc) but these incur much larger compute/code/memory overhead. The simple system used here avoids the need for testing for overflow/underflow which allows for low foot print code/cpu/memory bandwidth.
To calculate how many bits of fractional part while preventing overflow use the following formulas:
Nb = number of bits in use = ceiling(log2(highest_number_represented))
radix = 32-Nb(sample_resolution)-Nb(WindowLength)-2
for example if you have a ADC generating counts from 0 to 1023 then the Nb(ADC-range) =
Nb = ceiling(log2(1023)) = 10 bits
If you use a DC average window length of 2000
Nb = ceiling(log2(2000)) = 11 bits
so the max radix that should be specified is:
radix = 32 - 10 - 11 - 2 = 32 - 23 = 9 bits
with 9 bits in the radix fractional precision of 512 units per integer (e.g 1/512) is possible. The "2" in for formula comes from reserving bits for the sign bit and the additional operation in averager.
The function DIO_s32 DIO_IIRavgFR() allows any integer length window size to be used, while the function DIO_IIRavgPower2FR() specifies window lengths in powers of two but all calculations are based on shift operations which can be significantly faster on lower end microprocessors.
Embedded Systems
Now back to an embedded microcontroller example. It has a ADC which maps the voltage on 1 pin from 0-3.3V in to 0-4095 counts (12 bit). We capacitively couple the input voltage and use the bias resistors to set mid point in the center of the range. Lets say the the our resistors set the bias at 1.55V. This equals our "zero point". Below this point is negative from the incoming capacitively coupled audio and above this is positive.
Our "guess" as to the bias = both resistors are the same = (3.3V/2) =1.65V = (1.65V)/(4095 counts/3.3V)= 2048 counts
Resistor actual set bias "zero point" = 1.55V = (1.55V) *(4095 counts/3.3V)) = 1923 counts
We want this to be "zero" we use for the companded A/D process.
To do this we start our ADC to periodically sample for sometime before we start "recording" audio. We will feed the ADC values in to our IIR average to find the actual zero point. Note that even when we decide not to "record" we still can still run the A/D and IIR averager so its adapting to the real zero point.
//C-like psuedo code
#include "companders.h"
static volatile int32 gIIRavg= 2048; // global static var holds DC average
static volatile int gDoSomethingWithCompandedValue=0;
void processAudioSample() //interrupt handler
{
short adcValue;
char aLawValue;
//read the raw uncorrected sample
adcValue= inputPin.read_u16(); // read a 16 bit unsigned sample
// this updates the IIR average everytime the adc returns a sample
gIIRavg = DIO_IIRavgPower2FR(gIIRavg,11,adcValue,8);
//now compute companded value with DC offset correction
if (gDoSomethingWithCompandedValue)
{
aLawValue = DIO_LinearToALaw(adcValue-DIO_FR2I(gIIRavg));
doSomethingWithALaw(aLawValue); // store it to a buffer, send it etc
}
}
main()
{
gDoSomethingWithCompandedValue=0;
inputPin.attachInterrupt(&processAudioSample,8000); //attach interrupt handler, 8000 Hz sample rate
// ... somepoint later in the code
inputPin.start(); // start the averager
// ... some time later
gDoSomethingWithCompandedValue=1; //now the interrupt routine will call the doSomethingWithALaw() function
// ... some time later
gDoSomethingWithCompandedValue=0; //we're no longer collecting ALaw companded date, but we're
// still running the IIR averager
}
Finally, it can be in some systems that we can't turn off the audio input source it may be hard wired to some sensor or mic or perhaps the A/D center bias circuit (the 2 resistors) always is on when the audio is on. In this case running the IIR with a long filter length all the time can remove the bias even when the audio is running. For example in an 8KHz sampling system with an IIR length of 1024 is about 1/8 of a second or a cutoff freq well below 10Hz and costs almost nothing to run.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment