//Continuous Time Fourier Series Spectral Coefficients of
//a periodic sine signal x(t) = sin(Wot)
clear;
close;
clc;
t = 0:0.01:1;
T = 1;
Wo = 2*%pi/T;
xt = sin(Wo*t);
for k =0:5
C(k+1,:) = exp(-sqrt(-1)*Wo*t.*k);
a(k+1) = xt*C(k+1,:)'/length(t); //fourier series is done
if(abs(a(k+1))<=0.01)
a(k+1)=0;
end
end
a =a';
ak = [-a($:-1:1),a(2:$)];
disp(ak,'Continuous Time Fourier Series Coefficients are:')
//Result
//Continuous Time Fourier Series Coefficients are:
// column 1 to 11
// 0 0 0 0 -1.010D-18+0.4950495i 0 1.010D-18-0.4950495i 0 0 0 0
/* **********************************************************
* Purpose::
* heuristic multi-user waterfilling algorithm implementation
* example invocation included; compile and run with
* gcc -Wall (thisFile.c); ./a.out
*
* PLEASE NOTE:
* This is a quick rewrite that hasn't been extensively tested.
* Please verify independently that the code does what it says.
*/
/* **********************************************************
* Header file
* **********************************************************/
#ifndef WATERFILL_H
#define WATERFILL_H
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
/* **********************************************************
* Waterfilling algorithm data structure
* In C++ this would become an object.
* Usage:
* wfalg_new(...);
* while (need to invoke it multiple times on changing data){
* wfalg_run(...);
* (process results)
* }
* wfalg_free(...); now the results become invalid
* **********************************************************/
typedef struct wfalg_t_ wfalg_t;
/* Purpose:
* Create data structure for multiuser waterfiller "object"
* Use for multiple invocations on varying data
*
* Parameters:
* nRB: Number of "resource blocks" (frequency bins)
*
* nUser: number of simultaneous transmissions to allocate
*
* pmode: 0 conventional multi-user waterfilling: Per-resource power based on
* channel quality estimate
* otherwise: select resources based on channel quality, but distribute the
* per-user power evenly
*/
wfalg_t* wfalg_new(int /*nRB*/, int /*nUser*/, int /*mode*/);
/* Purpose:
* Invoke multi-user waterfilling for the following parameters:
* Rmax: maximum number of RBs that may be allocated to each individual user.
* in "ideal" waterfilling, a single user with a very good channel hogs up
* most of the resources. While this (ideally) maximizes sum throughput, there are
* usually other criteria that are equally or more important.
* The caller can limit the number of resources for a particular user, and thus
* control the "fairness" of allocation.
* Set Rmax >= nRB for each user to disable this limitation.
*
* SNR_threshold: Do not allocate a resource if the signal-to-noise ratio falls below the
* threshold. For example, even heavily coded QPSK doesn't lead to any meaningful
* throughput when the signal-to-noise ratio is below -3 dB
* if so, choose SNR_threshold=10*log10(-3/10) = 0.5
*
* Tinv: inverse of channel quality, noise-to-signal ratio N/S where "signal" includes
* the channel |H(f)| with f the frequency of a resource.
* Matrix: first user resource 0..nRB-1, second user resources 0..nRB-1, last user
*
* porder: Process users in the given order (array of user indices 0..nUser-1 with length nUser)
* The order is important in pathological cases, such as "more users than RBs."
* If so, the scheduling decision is effectively made by the caller, via the processing order:
* first come, first served, and the later ones get nothing.
*
* After execution,
* - resultAlloc points to a size-nRB array containing the index of the allocated user for each resource block.
- resultPower points to a size-nRB array with the power allocated to a resource.
* All powers for user j sum up to pUser[j]
* The number of resources where resultAlloc==j is <= Rmax[j].
* There is only one storage space per wfalg_t "object". Earlier results become invalid with
* the next invocation.
*/
void wfalg_run(wfalg_t* /*obj*/, const double* /*pUser*/, const int* /*Rmax*/, const int* /*porder*/, const double* /*TinvMatrix*/, double /*SNR_threshold*/, int** /*resultAlloc*/, double** /*resultPower*/);
/* Purpose:
* Release allocated memory.
* obj becomes invalid.
* any space provided by wfalg_run becomes invalid.
*/
void wfalg_free(wfalg_t* /* obj */);
#endif
/* **********************************************************
* .c file
* **********************************************************/
/* Data structure for multiple invocations without unnecessary malloc / free */
struct wfalg_t_ {
/* parameters */
int nRB;
int nUser;
/* Storage space for results */
int* RB_alloc;
double* RB_power;
/* Internal temporary storage space
* all Tinv values for the single user that is
* processed by waterfill() */
double* temp_singleUserTinv;
/* internal temporary storage: How many resources are allocated per user? */
int* temp_userNResources;
int* order;
/* Mode switch parameter */
int mode;
};
wfalg_t* wfalg_new(int nRB, int nUser, int mode){
wfalg_t* obj=(wfalg_t*)malloc(sizeof(wfalg_t)); assert(obj);
obj->nUser=nUser;
obj->nRB=nRB;
obj->mode=mode;
obj->RB_alloc=(int*)malloc(sizeof(int) * nRB); assert(obj->RB_alloc);
obj->RB_power=(double*)malloc(sizeof(double) * nRB); assert(obj->RB_power);
obj->temp_singleUserTinv=(double*)malloc(sizeof(double) * nRB); assert(obj->temp_singleUserTinv);
obj->temp_userNResources=(int*)malloc(sizeof(int) * nUser); assert(obj->temp_userNResources);
memset((void*)obj->RB_power, 0, nRB * sizeof(double));
return obj;
}
void wfalg_free(wfalg_t* obj){
free(obj->RB_alloc);
free(obj->RB_power);
free(obj->temp_singleUserTinv);
free(obj->temp_userNResources);
free(obj);
}
/* ************************************************************************************
* Conventional multi-user waterfilling on the set of resources where
* RB_alloc[]==userindex
* expects temp_singleUserTinv to be filled with the Tinv for the allocating user!
* ************************************************************************************/
static void waterfill(wfalg_t* obj, int userindex, double pUser){
const int nRB=obj->nRB;
const double E0=pUser;
const double threshold=1e-12* E0;
/* ************************************************************************************
* Count resource allocated to this user
* ************************************************************************************/
int nRB_user=0;
int j_RB;
for(j_RB=0; j_RB < nRB; ++j_RB){
if (*(obj->RB_alloc+j_RB)==userindex){
++nRB_user;
}
}
if (nRB_user == 0){
/* no resources allocated - can't waterfill */
return;
}
double E=E0;
double waterlevel=0;
while (E > threshold){
/* ************************************************************************************
* Distribute remaining energy evenly over all RBs
* since some of them have greater-than-zero Tinv, this approximation will always stay
* below target (standard waterfilling algorithm)
* ************************************************************************************/
E *= 0.999;
waterlevel += E/(double)nRB_user;
/* ************************************************************************************
* Determine energy that remains with current allocation
* ************************************************************************************/
E=E0;
/* Note: temp_singleUserTinv contains the Tinv for the user who allocates the RB.
* This avoids many repeated table lookups from the nUser*nRB Tinv matrix. */
double* pTinv=obj->temp_singleUserTinv;
double* pRB_power=obj->RB_power;
int* palloc=obj->RB_alloc;
int j_RB;
for(j_RB=0; j_RB < nRB; ++j_RB){
int alloc=*(palloc++);
double Tinv=*(pTinv++);
/* resource is allocated to current user */
if (alloc==userindex){
/* determine actual per-resource energy */
double d=waterlevel - Tinv;
d=(d > 0.0 ? d : 0.0);
E -= d;
*(pRB_power)=d;
} /* if allocated */
++pRB_power;
} /* for RB */
} /* while energy remains */
assert(E >= 0.0);
}
/* picks the best unallocated RB for the given user */
static int find_best_RB(wfalg_t* obj, int userindex, const double* thisUserTinv){
/* find RB with lowest Tinv for this user */
int i_RB;
int bestRB_index=-1;
double bestRB_Tinv=9e99;
int valid=0;
const double* pTinv=thisUserTinv;
for (i_RB=0; i_RB < obj->nRB; ++i_RB){
double Tinv=*(pTinv++);
int alloc=*(obj->RB_alloc+i_RB);
if ((alloc < 0) && (Tinv < bestRB_Tinv)){
bestRB_index=i_RB;
bestRB_Tinv=Tinv;
valid=1;
}
}
if (!valid){
bestRB_index=-1;
}
return bestRB_index; /* -1 means: none */
}
static int try_alloc_one_RB(wfalg_t* obj, int userindex, double pThisUser, const double* thisUserTinv, double SNR_threshold){
int RBindex=find_best_RB(obj, userindex, thisUserTinv);
if (RBindex >= 0){
/* channel quality on resource RBindex */
double Tinv=*(thisUserTinv+RBindex);
/* allocate RB, at least temporarily */
*(obj->RB_alloc+RBindex)=userindex;
double Tinv2=Tinv;
if (obj->mode){
/* Equal power allocation - disregard "floor" level in waterfilling
* We'll use the original Tinv for the SNR check later */
Tinv2=0.0;
}
*(obj->temp_singleUserTinv+RBindex) = Tinv2;
waterfill(obj, userindex, pThisUser);
/* check SNR of last RB */
double P=*(obj->RB_power + RBindex);
double SNR=P/Tinv;
if (SNR >= SNR_threshold){
return 1; /* success */
} else {
/* too low power to be useful.
* undo allocation. */
*(obj->RB_alloc+RBindex)=-1;
}
}
return 0; /* failure */
}
static void zero_unallocated_RBs(const wfalg_t* obj){
int* ap=obj->RB_alloc;
double* pp=obj->RB_power;
int iRB;
for (iRB=0; iRB < obj->nRB; ++iRB){
*pp=(*ap >= 0 ? *pp : 0.0);
++pp;
++ap;
}
}
void wfalg_run(wfalg_t* obj, const double* pUser, const int* Rmax, const int* porder, const double* TinvMatrix, double SNR_threshold, int** resultAlloc, double** resultPower){
/* Deallocate all RBs */
int* p=obj->RB_alloc;
int i_RB;
for (i_RB=0; i_RB < obj->nRB; ++i_RB){
*(p++) = -1;
}
memset((void*)obj->temp_userNResources, 0, obj->nUser * sizeof(int));
int nAllocResourcesThisRound=-1; /* any nonzero value to enter the loop */
/* Repeat until we fail to allocate another resource */
while (nAllocResourcesThisRound != 0){
nAllocResourcesThisRound=0;
/* Iterate over all users ... */
int jj_user;
for (jj_user=0; jj_user < obj->nUser; ++jj_user){
/* ... in the specified order */
int j_user=*(porder+jj_user);
/* power budget of current user */
double p_jUser=*(pUser+j_user);
/* Are we allowed to add another resource to this user? (Rmax constraint) */
if (*(obj->temp_userNResources+j_user) < *(Rmax+j_user)){
/* Look up the channel quality array for j_user */
const double* thisUserTinv=TinvMatrix + j_user * obj->nRB;
/* Try to allocate one more resource to this user */
if (try_alloc_one_RB(obj, j_user, p_jUser, thisUserTinv, SNR_threshold)){
/* Successfully allocated */
++ *(obj->temp_userNResources+j_user); /* count resources allocated to this user */
++nAllocResourcesThisRound; /* continue iterating */
} else {
/* this user can't allocate any more resources
* There is no need to try again in future iterations,
* disregard this user by making the Rmax test fail
*
* note, power allocation is not valid at this point */
*(obj->temp_userNResources+j_user) = *(Rmax+j_user);
}
}
} /* for j_user */
} /* while allocations succeed */
/* The previous step fails exactly once per user, making the
* power allocation invalid.
*
* Recalculate.
* This is single-user waterfilling without any interaction, therefore order does not matter
* Note that waterfill() requires a valid temp_singleUserTinv (which is the case at this point):
* For each resource, it contains the Tinv of the allocating user
*/
int j_user;
for (j_user=0; j_user < obj->nUser; ++j_user){
double p_jUser=*(pUser+j_user);
waterfill(obj, j_user, p_jUser);
}
/* Set zero power allocation for resources that aren't allocated to any user */
zero_unallocated_RBs(obj);
*resultAlloc=obj->RB_alloc;
*resultPower=obj->RB_power;
}
/* **********************************************************
* Example use (still .c file)
* **********************************************************/
#if 1
int main(void){
const int nUser=5;
const int nRB=40;
/* allocate and create random multi-user channel data */
double* Tinv=(double*)malloc(nUser*nRB*sizeof(double));
int i; int j;
double* p=Tinv;
for (i=0; i < nUser; ++i){
for (j=0; j < nRB; ++j){
*(p++)=(double)random() / (double)RAND_MAX;
}
}
/* power per user */
double pUser[]={1, 2, 3, 4, 5};
/* processing order */
int order[]={0, 1, 2, 3, 4};
/* maximum number of resources per user */
const int RMax[]={nRB, nRB, nRB, nRB, nRB};
/* create waterfiller "object" */
wfalg_t* wfalg=wfalg_new(nRB, nUser, /* mode */0);
/* Invoke waterfilling */
int* RB_alloc;
double* RB_power;
wfalg_run(wfalg, pUser, RMax, order, Tinv, /* SNR_threshold */1.0, &RB_alloc, &RB_power);
/* print result */
int j_user; int i_RB;
for (j_user=0; j_user < nUser; ++j_user){
printf("user %i ", j_user);
double sum=0;
for (i_RB=0; i_RB < nRB; ++i_RB){
if (*(RB_alloc+i_RB)==j_user){
double p=*(RB_power+i_RB);
printf("%i=%lf ", i_RB, p);
sum += p;
}
}
printf("sum=%lf\n", sum);
}
/* clean up */
wfalg_free(wfalg);
free(Tinv);
return EXIT_SUCCESS;
}
#endif
% ************************************************************
% Spectrum model for GSM signal
% Markus Nentwig, 2011
% based on 3GPP TS 45.004 section 2 "Modulation format for GMSK",
% assuming no additional filtering and an infinite-length
% symbol stream (no slot structure)
% ************************************************************
% ************************************************************
% Parameters
% The "baseline" serves as a gentle reminder that the model
% is only valid near the center frequency.
% For large frequency offsets, one would need more information on
% the particular transmitter used.
% If not available, assume that spectral emission mask requirements
% are met.
% ************************************************************
BW = 285e3;
BW2 = 186e3;
baseline_dB = -76;
% baseline_dB = -999 % disable the constant term
% ************************************************************
% empirical GSM (GMSK narrow-bandwidth pulse) model equation
% ************************************************************
f = (-500e3:1e3:500e3)+1e-3;
gaussPart = exp(-(2*f/BW) .^2);
sincPart = sin(pi*f/BW2) ./ (pi*f/BW2);
flatPart = 10^(baseline_dB/20);
H_dB = 20*log10(abs(gaussPart .* sincPart) + flatPart);
% ************************************************************
% plot the spectrum
% ************************************************************
figure(); grid on; hold on;
h = plot(f/1e6, H_dB, 'b'); set(h, 'linewidth', 2);
% ************************************************************
% plot 'a' GSM spectral emission mask
% note, this is only "an" example
% See 3GPP TS 45.005 section 4.2.1
% "Spectrum due to the modulation and wide band noise"
% section 4.2.1.1
% "General requirements for all types of Base stations and MS"
% note the warning regarding measuring the nominal signal level!
% ************************************************************
maskX_MHz = [0, 100e3, 200e3, 250e3, 400e3, 600e3]/1e6;
maskY_dB = [0.5, 0.5, -30, -33, -60, -60];
h = plot(maskX_MHz, maskY_dB, 'k'); set(h, 'linewidth', 3);
legend('|H(f)|', 'GSM mask example');
xlabel('f/MHz');
ylabel('PSD/dB');
title('GSM spectrum');
/* ****************************************************
* Farrow resampler (with optional bank switching)
* M. Nentwig, 2011
* Input stream is taken from stdin
* Output stream goes to stdout
* Main target was readability, is not optimized for efficiency.
* ****************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#if 1
/* **************************************************************
* example coefficients.
* Each column [c0; c1; c2; ...] describes a polynomial for one tap coefficent in fractional time ft [0, 1]:
* tapCoeff = c0 + c1 * ft + c2 * ft ^ 2 + ...
* Each column corresponds to one tap.
* The example filters is based on a 6th order Chebyshev Laplace-domain prototype.
* This version uses three sub-segments per tap (NBANKS = 3)
* **************************************************************/
const double cMatrix[] = {
2.87810386e-4, 4.70096244e-3, 7.93412570e-2, 4.39824536e-1, 1.31192924e+000, 2.67892232e+000, 4.16465421e+000, 5.16499621e+000, 5.15592605e+000, 3.99000369e+000, 2.00785470e+000, -7.42377060e-2, -1.52569354e+000, -1.94402804e+000, -1.40915797e+000, -3.86484652e-1, 5.44712939e-1, 9.77559688e-1, 8.32191447e-1, 3.22691788e-1, -2.13133045e-1, -5.08501962e-1, -4.82928807e-1, -2.36313854e-1, 4.76034568e-2, 2.16891966e-1, 2.20894063e-1, 1.08361553e-1, -2.63421832e-2, -1.06276015e-1, -1.07491548e-1, -5.53793711e-2, 4.86314061e-3, 3.94357182e-2, 4.06217506e-2, 2.17199064e-2, 1.60318761e-3, -8.40370106e-3, -8.10525279e-3, -3.62112499e-3, -4.13413072e-4, 2.33101911e-4,
-3.26760325e-3, -6.46028234e-3, 1.46793247e-1, 5.90235537e-1, 1.18931309e+000, 1.57853546e+000, 1.40402774e+000, 5.76506323e-1, -6.33522788e-1, -1.74564700e+000, -2.24153717e+000, -1.91309453e+000, -9.55568978e-1, 1.58239169e-1, 9.36193787e-1, 1.10969783e+000, 7.33284446e-1, 1.06542194e-1, -4.15412084e-1, -6.06616434e-1, -4.54898908e-1, -1.20841199e-1, 1.82941623e-1, 3.12543429e-1, 2.49935829e-1, 8.05376898e-2, -7.83213666e-2, -1.47769751e-1, -1.18735248e-1, -3.70656555e-2, 3.72608374e-2, 6.71425397e-2, 5.17812605e-2, 1.55564930e-2, -1.40896327e-2, -2.35058137e-2, -1.59635057e-2, -3.44701792e-3, 4.14108065e-3, 4.56234829e-3, 1.59503132e-3, -3.17301882e-4,
5.64310141e-3, 7.74786707e-2, 2.11791763e-1, 2.84703201e-1, 1.85158633e-1, -8.41118142e-2, -3.98497442e-1, -5.86821615e-1, -5.40397941e-1, -2.47558080e-1, 1.50864737e-1, 4.59312895e-1, 5.41539400e-1, 3.84673917e-1, 9.39576331e-2, -1.74932542e-1, -3.01635463e-1, -2.56239225e-1, -9.87146864e-2, 6.82216764e-2, 1.59795852e-1, 1.48668245e-1, 6.62563431e-2, -2.71234898e-2, -8.07045577e-2, -7.76841351e-2, -3.55333136e-2, 1.23206602e-2, 3.88535040e-2, 3.64199073e-2, 1.54608563e-2, -6.59814558e-3, -1.72735099e-2, -1.46307777e-2, -5.04363288e-3, 3.31049461e-3, 6.01267607e-3, 3.83904192e-3, 3.92549958e-4, -1.36315264e-3, -9.76017430e-4, 7.46699178e-5};
#define NTAPS (14)
#define NBANKS (3)
#define ORDER (2)
#else
/* Alternative example
* Similar impulse response as above
* A conventional Farrow design (no subdivisions => NBANKS = 1), order 3
*/
const double cMatrix[] = {
-8.57738278e-3, 7.82989032e-1, 7.19303539e+000, 6.90955718e+000, -2.62377450e+000, -6.85327127e-1, 1.44681608e+000, -8.79147907e-1, 7.82633997e-2, 1.91318985e-1, -1.88573400e-1, 6.91790782e-2, 3.07723786e-3, -6.74800912e-3,
2.32448021e-1, 2.52624309e+000, 7.67543936e+000, -8.83951796e+000, -5.49838636e+000, 6.07298348e+000, -2.16053205e+000, -7.59142947e-1, 1.41269409e+000, -8.17735712e-1, 1.98119464e-1, 9.15904145e-2, -9.18092030e-2, 2.74136108e-2,
-1.14183319e+000, 6.86126458e+000, -6.86015957e+000, -6.35135894e+000, 1.10745051e+001, -3.34847578e+000, -2.22405694e+000, 3.14374725e+000, -1.68249886e+000, 2.54083065e-1, 3.22275037e-1, -3.04794927e-1, 1.29393976e-1, -3.32026332e-2,
1.67363115e+000, -2.93090391e+000, -1.13549165e+000, 5.65274939e+000, -3.60291782e+000, -6.20715544e-1, 2.06619782e+000, -1.42159644e+000, 3.75075865e-1, 1.88433333e-1, -2.64135123e-1, 1.47117661e-1, -4.71871047e-2, 1.24921920e-2};
#define NTAPS (14)
#define NBANKS (1)
#define ORDER (3)
#endif
/* Set here the ratio between output and input sample rate.
* It could be changed even during runtime! */
#define INCR (1.0 / 6.28 / 3)
/* delay line storage */
double delayLine[NTAPS];
/* Coefficient lookup "table" */
static double c(int ixTap, int ixBank, int ixPower){
return cMatrix[ixPower * (NTAPS * NBANKS) + ixTap * NBANKS + ixBank];
}
int main (void){
/* clear delay line */
int ix;
for (ix = 0; ix < NTAPS; ++ix)
delayLine[ix] = 0;
/* Index of last input sample that was read
* First valid sample starts at 0 */
int sampleIndexInput = -1;
/* Position of next output sample in input stream */
double sampleIndexOutput = 0.0;
/* loop forever. Terminate, once out of input data. */
while (1){
/* Split output sample location into integer and fractional part:
* Integer part corresponds to sample index in input stream
* fractional part [0, 1[ spans one tap (covering NBANKS segments) */
int sio_int = floor(sampleIndexOutput);
double sio_fracInTap = sampleIndexOutput - (double)sio_int;
assert((sio_fracInTap >= 0) && (sio_fracInTap < 1));
/* Further split the fractional part into
* - bank index
* - fractional part within the bank */
int sio_intBank = floor(sio_fracInTap * (double) NBANKS);
double sio_fracInBank = sio_fracInTap * (double) NBANKS - (double)sio_intBank;
assert((sio_fracInBank >= 0) && (sio_fracInBank < 1));
/* ****************************************************
* load new samples into the delay line, until the last
* processed input sample (sampleIndexInput) catches
* up with the integer part of the output stream position (sio_int)
* ***************************************************/
while (sampleIndexInput < sio_int){
/* Advance the delay line one step */
++sampleIndexInput;
for (ix = NTAPS-1; ix > 0; --ix)
delayLine[ix] = delayLine[ix-1];
/* Read one input sample */
int flag = scanf("%lf", &delayLine[0]);
if (flag != 1)
goto done; /* there's nothing wrong with "goto" as "break" for multiple loops ... */
} /* while delay line behind output data */
/* ****************************************************
* Calculate one output sample:
* "out" sums up the contribution of each tap
* ***************************************************/
double out = 0;
int ixTap;
for (ixTap = 0; ixTap < NTAPS; ++ixTap){
/* ****************************************************
* Contribution of tap "ixTap" to output:
* ***************************************************/
/* Evaluate polynomial in sio_fracInBank:
* c(ixTap, sio_intBank, 0) is the constant coefficient
* c(ixTap, sio_intBank, 1) is the linear coefficient etc
*/
double hornerSum = c(ixTap, sio_intBank, ORDER);
int ixPower;
for (ixPower = ORDER-1; ixPower >= 0; --ixPower){
hornerSum *= sio_fracInBank;
hornerSum += c(ixTap, sio_intBank, ixPower);
} /* for ixPower */
/* ****************************************************
* Weigh the delay line sample of this tap with the
* polynomial result and add to output
* ***************************************************/
out += hornerSum * delayLine[ixTap];
} /* for ixTap */
/* ****************************************************
* Generate output sample and advance the position of
* the next output sample in the input data stream
* ***************************************************/
printf("%1.12le\n", out);
sampleIndexOutput += INCR;
} /* loop until out of input data */
done: /* out of input data => break loops and continue here */
return 0;
} /* main */
% ************************************************************
% * Construct a continuous-time impulse response based on a discrete-time filter
% ************************************************************
close all; clear all;
% ************************************************************
% * example filter
% * f = [0 0.7 0.8 1]; a = [1 1 0 0];
% * b = remez(45, f, a);
% * h = b .';
% Illustrates rather clearly the difficulty of "interpolating" (in a geometric sense,
% via polynomials, splines etc) between impulse response samples
% ************************************************************
h = [2.64186706e-003 2.50796828e-003 -4.25450455e-003 4.82982358e-003 -2.51252769e-003 -2.52706568e-003 7.73569146e-003 -9.30398382e-003 4.65100223e-003 5.17152459e-003 -1.49409856e-002 1.76001904e-002 -8.65545521e-003 -9.78478603e-003 2.82103205e-002 -3.36173643e-002 1.68597129e-002 2.01914744e-002 -6.17486493e-002 8.13362871e-002 -4.80981494e-002 -8.05143565e-002 5.87677665e-001 5.87677665e-001 -8.05143565e-002 -4.80981494e-002 8.13362871e-002 -6.17486493e-002 2.01914744e-002 1.68597129e-002 -3.36173643e-002 2.82103205e-002 -9.78478603e-003 -8.65545521e-003 1.76001904e-002 -1.49409856e-002 5.17152459e-003 4.65100223e-003 -9.30398382e-003 7.73569146e-003 -2.52706568e-003 -2.51252769e-003 4.82982358e-003 -4.25450455e-003 2.50796828e-003 2.64186706e-003];
% ************************************************************
% zero-pad the impulse response of the FIR filter to 5x its original length
% (time domain)
% The filter remains functionally identical, since appending zero taps changes nothing
% ************************************************************
timePaddingFactor = 5;
n1 = timePaddingFactor * size(h, 2);
nh = size(h, 2);
nPad = n1 - nh;
nPad1 = floor(nPad / 2);
nPad2 = nPad - nPad1;
h = [zeros(1, nPad1), h, zeros(1, nPad2)];
hOrig = h;
% ************************************************************
% Determine equivalent Fourier series coefficients (frequency domain)
% assume that the impulse response is bandlimited (time-discrete signal by definition)
% and periodic (period length see above, can be extended arbitrarily)
% ************************************************************
h = fft(h); % Fourier transform time domain to frequency domain
% ************************************************************
% Evaluate the Fourier series on an arbitrarily dense grid
% this allows to resample the impulse response on an arbitrarily dense grid
% ************************************************************
% zero-pad Fourier transform
% ideal band-limited oversampling of the impulse response to n2 samples
n2 = 10 * n1;
h = [h(1:ceil(n1/2)), zeros(1, n2-n1), h(ceil(n1/2)+1:end)];
h = ifft(h); % back to time domain
% Note: One may write out the definition of ifft() and evaluate the exp(...) term at an
% arbitrary time to acquire a true continuous-time equation.
% numerical inaccuracy in (i)fft causes some imaginary part ~1e-15
assert(max(abs(imag(h))) / max(abs(real(h))) < 1e-14);
h = real(h);
% scale to maintain amplitude level
h = h * n2 / n1;
% construct x-axis [0, 1[
xOrig = linspace(-timePaddingFactor/2, timePaddingFactor/2, size(hOrig, 2) + 1); xOrig = xOrig(1:end-1);
x = linspace(-timePaddingFactor/2, timePaddingFactor/2, size(h, 2) + 1); x = x(1:end-1);
% ************************************************************
% Plot
% ************************************************************
% ... on a linear scale
% find points where original impulse response is defined (for illustration)
ixOrig = find(abs(hOrig) > 0);
figure(); grid on; hold on;
stem(xOrig(ixOrig), hOrig(ixOrig), 'k');
plot(x, h, 'b');
legend('discrete-time FIR filter', 'continuous-time equivalent');
title('equivalent discrete- and continuous-time filters');
xlabel('time (relative to discrete time IR duration)'); ylabel('h(t)');
% ... and again on a logarithmic scale
myEps = 1e-15;
figure(); grid on; hold on;
u = plot(xOrig(ixOrig), 20*log10(abs(hOrig(ixOrig)) + myEps), 'k+'); set(u, 'lineWidth', 3);
plot(x, 20*log10(abs(h) + myEps), 'b');
legend('discrete-time FIR filter', 'continuous-time equivalent');
title('equivalent discrete- and continuous-time filters');
xlabel('time (relative to discrete time IR duration)'); ylabel('|h(t)| / dB');
% *************************************************************
% Raised cosine window for OFDM intersymbol transitions
% *************************************************************
% Use the window for the guard period between adjacent symbols
% to smoothen the waveform transition, limit sidelobes and prevent
% unwanted emissions into adjacent frequency ranges
%
% reference:
% Data transmission by Frequency Division Multiplexing
% using the Discrete Fourier Transform
% S.B. Weinstein and Paul M. Ebert
% IEEE transactions on Communications technology, No. 5,
% October 1971
% *************************************************************
% Length of the guard period in samples
n=64;
index=linspace(0, 1, n+2);
index=index(2:end-1);
windowNextSymbol=(1-cos(pi*index))/2;
windowPreviousSymbol=fliplr(windowNextSymbol);
% plot
figure(); title('raised cosine window for OFDM symbol transitions');
xlabel('sample index into the guard period between symbols');
ylabel('window weight');
hold on;
plot(windowPreviousSymbol, 'r+');
plot(windowNextSymbol, 'b+');
legend({'cyclic extension of symbol i-1', 'cyclic extension of symbol i'});
function [a] =ifft2d(a2)
//a2 = 2D-DFT of any real or complex 2D matrix
//a = 2D-IDFT of a2
m=size(a2,1)
n=size(a2,2)
//Inverse Fourier transform along the rows
for i=1:n
a1(:,i)=exp(2*%i*%pi*(0:m-1)'.*.(0:m-1)/m)*a2(:,i)
end
//Inverse fourier transform along the columns
for j=1:m
atemp=exp(2*%i*%pi*(0:n-1)'.*.(0:n-1)/n)*(a1(j,:)).'
a(j,:)=atemp.'
end
a = a/(m*n)
a = real(a)
endfunction
function [D1,D2] = Dec_OptimTwoStage_Factors(D_Total,Passband_width,Fstop)
%
% When breaking a single large decimation factor (D_Total) into
% two stages of decimation (to minimize the orders of the
% necessary lowpass digital filtering), an optimum first
% stage of decimation factor (D1) can be found. The second
% stage's decimation factor is then D_Total/D1.
%
% Inputs:
% D_Total = original single-stage decimation factor.
% Passband_width = desired passband width of original
% single-stage lowpass filter (Hz).
% Fstop = desired beginning of stopband freq of original
% single-stage lowpass filter (Hz).
%
% Outputs:
% D1 = optimum first-stage decimation factor.
% D2 = optimum second-stage decimation factor.
%
% Example: Assume you want to decimate a sequence by D_Total=100,
% your original single-stage lowpass filter's passband
% width and stopband frequency are 1800 and 2200 Hz
% respectively. We use:
%
% [D1,D2] = Dec_OptimTwoStage_Factors(100, 1800, 2200)
%
% giving us the desired D1 = 25, and D2 = 4.
% (That is, D1*D2 = 25*4 = 100 = D_Total.)
%
% Author: Richard Lyons, November, 2011
% Start of processing
DeltaF = (Fstop -Passband_width)/Fstop;
Radical = (D_Total*DeltaF)/(2 -DeltaF); % Terms under sqrt sign.
Numer = 2*D_Total*(1 -sqrt(Radical)); % Numerator.
Denom = 2 -DeltaF*(D_Total + 1); % Denominator.
D1_estimated = Numer/Denom; %Optimum D1 factor, but not
% an integer.
% Find all factors of 'D_Total'
Factors_of_D_Total = Find_Factors_of_D_Total(D_Total);
% Find the one factor of 'D_Total' that's closest to 'D1_estimated'
Temp = abs(Factors_of_D_Total -D1_estimated);
Index_of_Min = find(Temp == min(Temp)); % Index of optim. D1
D1 = Factors_of_D_Total(Index_of_Min); % Optimum first
% decimation factor
D2 = D_Total/D1; % Second decimation factor.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [allfactors] = Find_Factors_of_D_Total(X)
% Finds all integer factors of intger X.
% Filename was originally 'listfactors.m', written
% by Jeff Miller. Downloaded from Matlab Central.
[b,m,n] = unique(factor(X));
%b is all prime factors
occurences = [m(1) diff(m)];
current_factors = [b(1).^(0:occurences(1))]';
for index = 2:length(b)
currentprime = b(index).^(0:occurences(index));
current_factors = current_factors*currentprime;
current_factors = current_factors(:);
end
allfactors = sort(current_factors);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Demo
while 1
%Frequency grid with arbitrary start value, spacing, and number of elements. ...
%Mathematically, the grid is
%
% fo+Df*m, m=0, 1,...,M-1.
%
fo = 10*rand(1)-0.5; %Start value.
Df = rand(1)+0.01; %Frequency spacing.
M = round(rand(1)*1000); %Number of frequencies.
%Time grid with arbitrary start value, spacing, and number of elements. Mathematically, ...
%the grid is
%
% to+Dt*n, n=0, 1,...,N-1.
%
to = 10*rand(1)-0.5; %Start value; (instant of first sample).
Dt = rand(1)+0.01; %Time spacing.
N = round(rand(1)*1000); %Number of samples.
%Random vector of samples.
x = randn(1,N)+1i*randn(1,N);
%x(n) is the sample at instant to+Dt*(n-1), n=1,2,...,N.
%We proceed to compute the DFT of x at frequencies fo+m*Df, m=0, 1,..., M-1.
%Compute DFT using the direct and chirp-z methods.
tic, XD = DirectGridDFT(x,to,Dt,fo,Df,M); tmD = toc;
tic, XF = chirpzDFT(x,to,Dt,fo,Df,M); tmF = toc;
disp(['Timing direct method: ', num2str(tmD), ' sec.'])
disp(['Timing chirp z: ',num2str(tmF),' sec.'])
disp(['Relative difference: ', num2str(max(abs(XD-XF))/max(abs(XD)))])
disp(' ')
input('(Press a key for another trial or Ctrl-C) ')
disp(' ')
end
function X = chirpzDFT(x,to,Dt,fo,Df,M)
%Author: J. Selva.
%Date: November 2011.
%
%This function computes the DFT for two regular time and frequency grids with arbitrary
%starting points and spacings, using the Chirp-z Transform. Specifically, it computes
%
% N
% X(k) = sum x(n)*exp(-j*2*pi*(fo+Df*(k-1))*(to+Dt*(n-1))), 1 <= k <= M.
% n=1
%
%Input parameters:
%
% x: Signal samples.
% to: Instant of first sample in x.
% Dt: Time spacing between consecutive samples of x.
% fo: First frequency in which the Fourier sum is computed.
% Df: Spacing of the desired frequency grid.
% M: Desired number of output samples.
%
%The algorithm is explained in Sec. 9.6.2, page 656 of
%
% A.V. Oppenheim, R. W. Schafer, J. R. Buck, "Discrete-time signal processing," second
% edition, 1998.
%
x = x(:).';
N = numel(x);
P = 2^ceil(log2(M+N-1));
%The next three lines do not depend on the vector x, and so they can be pre-computed if
%the time and frequency grids do not change, (i.e, for computing the transform of
%additional vectors). Thus, this algorithm just involves two FFTs for fixed grids and
%three if the grids change.
a = exp(-1i*2*pi*((1:N)*Dt*(fo-Df)+Dt*Df*(1:N).^2/2));
b = exp(-1i*2*pi*((to-Dt)*(fo+(0:M-1)*Df)+Dt*Df*(1:M).^2/2));
phi = fft(exp(1i*2*pi*Dt*Df*(1-N:M-1).^2/2),P); %FFT of chirp pulse.
%Weigh x using a and perform FFT convolution with phi.
X = ifft( fft(x.*a,P) .* phi );
%Truncate the convolution tails and weigh using b.
X = X(N:M+N-1) .* b;
function X = DirectGridDFT(x,to,Dt,fo,Df,M)
%Direct (and slow) version of the Fourier sum with arbitrary and regular time and
%frequency grids. Used for validation of chirpzDFT.
x = x(:).';
N = length(x);
X = zeros(1,M);
for k = 1:M
for n = 1:N
X(k) = X(k) + x(n)*exp(-1i*2*pi*(to+Dt*(n-1))*(fo+Df*(k-1)));
end
end
% ****************************************************************
% RMS phase error from phase noise spectrum
% Markus Nentwig, 2011
%
% - integrates RMS phase error from a phase noise power spectrum
% - generates an example signal and determines the RMS-average
% phase error (alternative method)
% ****************************************************************
function pn_snippet()
close all;
% ****************************************************************
% Phase noise spectrum model
% --------------------------
% Notes:
% - Generally, a phase noise spectrum tends to follow
% |H(f)| = c0 + c1 f^n1 + c2 f^n2 + c3 f^n3 + ...
% Therefore, linear interpolation should be performed in dB
% on a LOGARITHMIC frequency axis.
% - Single-/double sideband definition:
% The phase noise model is defined for -inf <= f <= inf
% in other words, it contributes the given noise density at
% both positive AND negative frequencies.
% Assuming a symmetrical PN spectrum, the model is evaluated
% for |f| => no need to explicitly write out the negative frequency
% side.
% ****************************************************************
% PN model
% first column:
% frequency offset
% second column:
% spectral phase noise density, dB relative to carrier in a 1 Hz
% observation bandwidth
d = [0 -80
10e3 -80
1e6 -140
9e9 -140];
f_Hz = d(:, 1) .';
g_dBc1Hz = d(:, 2) .' -3;
% get RMS phase error by integrating the power spectrum
% (alternative 1)
e1_degRMS = integratePN_degRMS(f_Hz, g_dBc1Hz)
% get RMS phase error based on a test signal
% (alternative 2)
n = 2 ^ 20;
deltaF_Hz = 2;
e2_degRMS = simulatePN_degRMS(f_Hz, g_dBc1Hz, n, deltaF_Hz)
end
% ****************************************************************
% Integrate the RMS phase error from the power spectrum
% ****************************************************************
function r = integratePN_degRMS(f1_Hz, g1_dBc1Hz)
1;
% integration step size
deltaF_Hz = 100;
% list of integration frequencies
f_Hz = deltaF_Hz:deltaF_Hz:5e6;
% interpolate spectrum on logarithmic frequency, dB scale
% unit is dBc in 1 Hz, relative to a unity carrier
fr_dB = interp1(log(f1_Hz+eps), g1_dBc1Hz, log(f_Hz+eps), 'linear');
% convert to power in 1 Hz, relative to a unity carrier
fr_pwr = 10 .^ (fr_dB/10);
% scale to integration step size
% unit: power in deltaF_Hz, relative to unity carrier
fr_pwr = fr_pwr * deltaF_Hz;
% evaluation frequencies are positive only
% phase noise is two-sided
% (note the IEEE definition: one-half the double sideband PSD)
fr_pwr = fr_pwr * 2;
% sum up relative power over all frequencies
pow_relToCarrier = sum(fr_pwr);
% convert the phase noise power to an RMS magnitude, relative to the carrier
pnMagnitude = sqrt(pow_relToCarrier);
% convert from radians to degrees
r = pnMagnitude * 180 / pi;
end
% ****************************************************************
% Generate a PN signal with the given power spectrum and determine
% the RMS phase error
% ****************************************************************
function r = simulatePN_degRMS(f_Hz, g_dBc1Hz, n, deltaF_Hz);
A = 1; % unity amplitude, arbitrary
% indices of positive-frequency FFT bins.
% Does not include DC
ixPos = 2:(n/2);
% indices of negative-frequency FFT bins.
% Does not include the bin on the Nyquist limit
assert(mod(n, 2) == 0, 'n must be even');
ixNeg = n - ixPos + 2;
% Generate signal in the frequency domain
sig = zeros(1, n);
% positive frequencies
sig(ixPos) = randn(size(ixPos)) + 1i * randn(size(ixPos));
% for purely real-valued signal: conj()
% for purely imaginary-valued signal such as phase noise: -conj()
% Note:
% Small-signals are assumed. For higher "modulation indices",
% phase noise would become a circle in the complex plane
sig(ixNeg) = -conj(sig(ixPos));
% normalize energy to unity amplitude A per bin
sig = sig * A / RMS(sig);
% normalize energy to 0 dBc in 1 Hz
sig = sig * sqrt(deltaF_Hz);
% frequency vector corresponding to the FFT bins (0, positive, negative)
fb_Hz = FFT_freqbase(n, deltaF_Hz);
% interpolate phase noise spectrum on frequency vector
% interpolate dB on logarithmic frequency axis
H_dB = interp1(log(f_Hz+eps), g_dBc1Hz, log(abs(fb_Hz)+eps), 'linear');
% dB => magnitude
H = 10 .^ (H_dB / 20);
% plot on linear f axis
figure(); hold on;
plot(fftshift(fb_Hz), fftshift(mag2dB(H)), 'k');
xlabel('f/Hz'); ylabel('dBc in 1 Hz');
title('phase noise spectrum (linear frequency axis)');
% plot on logarithmic f axis
figure(); hold on;
ix = find(fb_Hz > 0);
semilogx(fb_Hz(ix), mag2dB(H(ix)), 'k');
xlabel('f/Hz'); ylabel('dBc in 1 Hz');
title('phase noise spectrum (logarithmic frequency axis)');
% apply frequency response to signal
sig = sig .* H;
% add carrier
sig (1) = A;
% convert to time domain
td = ifft(sig);
% determine phase
% for an ideal carrier, it would be zero
% any deviation from zero is phase error
ph_deg = angle(td) * 180 / pi;
figure();
ix = 1:1000;
plot(ix / n / deltaF_Hz, ph_deg(ix));
xlabel('time / s');
ylabel('phase error / degrees');
title('phase of example signal (first 1000 samples)');
figure();
hist(ph_deg, 300);
title('histogram of example signal phase');
xlabel('phase error / degrees');
% RMS average
% note: exp(1i*x) ~ x
% as with magnitude, power/energy is proportional to phase error squared
r = RMS(ph_deg);
% knowing that the signal does not have an amplitude component, we can alternatively
% determine the RMS phase error from the power of the phase noise component
% exclude carrier:
sig(1) = 0;
% 3rd alternative to calculate RMS phase error
rAlt_deg = sqrt(sig * sig') * 180 / pi
end
% gets a vector of frequencies corresponding to n FFT bins, when the
% frequency spacing between two adjacent bins is deltaF_Hz
function fb_Hz = FFT_freqbase(n, deltaF_Hz)
fb_Hz = 0:(n - 1);
fb_Hz = fb_Hz + floor(n / 2);
fb_Hz = mod(fb_Hz, n);
fb_Hz = fb_Hz - floor(n / 2);
fb_Hz = fb_Hz * deltaF_Hz;
end
% Root-mean-square average
function r = RMS(vec)
r = sqrt(vec * vec' / numel(vec));
end
% convert a magnitude to dB
function r = mag2dB(vec)
r = 20*log10(abs(vec) + eps);
end
function save_fig(h, name, format)
% Matlab has a wierd way of saving figures and keeping the plot looking
% exactly the way it does on your screen. Thi function simplifies the
% entire process in a way that is barely documented.
%
% Usage: SAVE_FIG(h, name, format);
%
% H is the handle to the figure. This can be obtain in the
% following manner: H = figure(1);
% NAME is the filename without extension
% FORMAT is the graphic format. Options are:
%
% 'bmpmono' BMP monochrome BMP
% 'bmp16m' BMP 24-bit BMP
% 'bmp256' BMP 8-bit (256-color)
% 'bmp' BMP 24-bit
% 'meta' EMF
% 'eps' EPS black and white
% 'epsc' EPS color
% 'eps2' EPS Level 2 black and white
% 'epsc2' EPS Level 2 color
% 'fig' FIG Matlab figure file
% 'hdf' HDF 24-bit
% 'ill' ILL (Adobe Illustrator)
% 'jpeg' JPEG 24-bit
% 'pbm' PBM (plain format) 1-bit
% 'pbmraw' PBM (raw format) 1-bit
% 'pcxmono' PCX 1-bit
% 'pcx24b' PCX 24-bit color PCX file format
% 'pcx256' PCX 8-bit newer color (256-color)
% 'pcx16' PCX 4-bit older color (16-color)
% 'pdf' PDF Color PDF file format
% 'pgm' PGM Portable Graymap (plain format)
% 'pgmraw' PGM Portable Graymap (raw format)
% 'png' PNG 24-bit
% 'ppm' PPM Portable Pixmap (plain format)
% 'ppmraw' PPM Portable Pixmap (raw format)
% 'svg' SVG Scalable Vector Graphics
% 'tiff' TIFF 24-bit
%
% Author: sparafucile17
if(strcmp(format,'fig'))
saveas(h, name, 'fig');
else
options.Format = format;
hgexport(h, name, options);
end
function set_fig_position(h, top, left, height, width)
% Matlab has a wierd way of positioning figures so this function
% simplifies the poisitioning scheme in a more conventional way.
%
% Usage: SET_FIG_POISITION(h, top, left, height, width);
%
% H is the handle to the figure. This can be obtain in the
% following manner: H = figure(1);
% TOP is the "y" screen coordinate for the top of the figure
% LEFT is the "x" screen coordinate for the left side of the figure
% HEIGHT is how tall you want the figure
% WIDTH is how wide you want the figure
%
% Author: sparafucile17
% PC's active screen size
screen_size = get(0,'ScreenSize');
pc_width = screen_size(3);
pc_height = screen_size(4);
%Matlab also does not consider the height of the figure's toolbar...
%Or the width of the border... they only care about the contents!
toolbar_height = 77;
window_border = 5;
% The Format of Matlab is this:
%[left, bottom, width, height]
m_left = left + window_border;
m_bottom = pc_height - height - top - toolbar_height - 1;
m_height = height;
m_width = width - 1;
%Set the correct position of the figure
set(h, 'Position', [m_left, m_bottom, m_width, m_height]);
%If you want it to print to file correctly, this must also be set
% and you must use the "-r72" scaling to get the proper format
set(h, 'PaperUnits', 'points');
set(h, 'PaperSize', [width, height]);
set(h, 'PaperPosition', [0, 0, width, height]); %[ left, bottom, width, height]
function [m, b] = ls_linear(x_observed, y_observed)
%
% Perform Least Squares curve-fitting techniques to solve the coefficients
% of the linear equation: y = m*x + b. Input and Output equation
% observations must be fed into the algorithm and a best fit equation will
% be calculated.
%
% Usage: [M,B] = ls_linear(observed_input, observed_output);
%
% observed_input is the x-vector observed data
% observed_output is the y-vector observed data
%
% Author: sparafucile17 06/04/03
%
if(length(x_observed) ~= length(y_observed))
error('ERROR: Both X and Y vectors must be the same size!');
end
%Calculate vector length once
vector_length = length(x_observed);
%
% Theta = [ x1 1 ]
% [ x2 1 ]
% [ x3 1 ]
% [ x4 1 ]
% [ ... ... ]
%
Theta = ones(vector_length, 2);
Theta(1:end, 1) = x_observed;
%
% Theta_0 = [ y1 ]
% [ y2 ]
% [ y3 ]
% [ y4 ]
% [ ... ]
%
Theta_0 = y_observed;
%
% Theta_LS = [ M ]
% [ B ]
%
Theta_LS = ((Theta' * Theta)^-1 ) * Theta' * Theta_0;
%Return the answer
m = Theta_LS(1);
b = Theta_LS(2);