Parallel Computing Toolbox 4.2
Distributed BER Performance of Several Equalizer Types
This demo uses the Parallel Computing Toolbox™ to compare the bit error rate (BER) performance of several types of equalizers in a static channel with a null in the passband. The demo constructs and implements a linear equalizer object and a decision feedback equalizer (DFE) object. It also initializes and invokes a maximum likelihood sequence estimation (MLSE) equalizer. The MLSE equalizer is first invoked with perfect channel knowledge, then with a straightforward but imperfect channel estimation technique.
As the simulation progresses, we update a BER plot for comparative analysis between the equalization methods.
For details about the computations, view the code for pctdemo_setup_eqber.
Prerequisites:
Related demos:
Contents
Analyze the Sequential Problem
First, we look at how the computations in the sequential demo fit into the model introduced in the Dividing MATLAB Computations into Tasks demo. The sequential demo calls two functions, pctdemo_task_eqber_mlse and pctdemo_task_eqber_adaptive, using different equalizer types and different Eb/No values (i.e. signal-to-noise ratios). The function values can be calculated quickly for low signal-to-noise ratios, but it takes a long time to calculate them for high signal-to-noise ratios. For simplicity, we ignore this difference and proceed as if the function values can all be calculated quickly. We therefore have each task compute several function values. Because the functions pctdemo_task_eqber_mlse and pctdemo_task_eqber_adaptive are vectorized, we can use them directly as our task functions.
Load the Demo Settings and the Data
The demo uses the default configuration when identifying the scheduler to use. The configurations documentation explains how to create new configurations and how to change the default configuration. See Customizing the Settings for the Demos in the Parallel Computing Toolbox for instructions on how to change the demo difficulty level or the number of tasks created.
Because this demo uses callbacks, we also verify that we have a job manager object to use, rather than one of the other scheduler types.
[difficulty, manager, numTasks] = pctdemo_helper_getDefaults(); if ~isa(manager, 'distcomp.jobmanager') error('distcomp:demo:NotJobmanager', ... ['This demo uses callbacks, which are only available with ' ... 'a job manager.']); end tasksPerEq = max(1, round(numTasks/4)); % We create tasks for 4 equalizers.
We obtain all the input parameters as well as the structure for storing the output data from pctdemo_setup_eqber. The difficulty parameter controls the number of Eb/No values (i.e. signal-to-noise ratios) for which we calculate the bit error rate. The function pctdemo_setup_eqber also displays a BER plot of ideal binary phase shift keying (BPSK) in a gaussian noise, unlimited bandwidth channel. We will add points to this graph as we obtain data about the BER of the other equalizers. You can view the code for pctdemo_setup_eqber for full details.
[figHandles, params, BERs] = pctdemo_setup_eqber(difficulty); EbNoIdx = length(params.EbNo):-1:1; startTime = clock;
The structure for storing the output data, BERs, is empty except for the field storing the performance of the ideal BPSK. Let's see what fields the structure has.
fieldnames(BERs)
ans =
'mlseideal'
'mlseimperfect'
'dfe'
'linear'
'ideal'
Divide the Work into Smaller Tasks
We choose to let one task consist of calculating the bit error rate for a single equalizer type and a range of signal-to-noise ratios. Because we have four equalizer types, we will create a total of 4*tasksPerEq tasks.
[splitEbNoIdx, tasksPerEq] = pctdemo_helper_split_vector(EbNoIdx, tasksPerEq ); fprintf('This demo will submit a job with %d task(s) to the job manager.\n', ... 4*tasksPerEq);
This demo will submit a job with 16 task(s) to the job manager.
Create and Submit the Job
We create the job and the tasks in the job. We set the task finished callback function to be pctdemo_taskfin_eqber, which will add the task results to our plot. We let the UserData property of the job point to the output figure and the structure storing the results of the computations. Additionally, we let the tasks' UserData store which field of the struct should store the task results.
job = createJob(manager);
job.UserData = {figHandles, BERs};
Let's create the tasks that compute the BER for the MLSE equalizer with and without perfect channel knowledge. You can view the code for pctdemo_task_eqber_mlse and pctdemo_taskfin_eqber for full details.
mlseTypes = {'ideal', 'imperfect'};
bFields = {'mlseideal', 'mlseimperfect'};
for ind = 1:numel(mlseTypes)
mlseType = mlseTypes{ind};
fieldname = bFields{ind};
for i = 1:tasksPerEq
task = createTask(job, @pctdemo_task_eqber_mlse, 1, ...
{splitEbNoIdx{i}, mlseType, params},
...
'UserData', fieldname);
set(task, 'FinishedFcn', @pctdemo_taskfin_eqber);
end
end
We also create the tasks that calculate the BER for the linear and DFE equalizers. You can view the code for pctdemo_task_eqber_adaptive and pctdemo_taskfin_eqber for full details.
eqTypes = {'dfe', 'linear'};
bFields = eqTypes;
for ind = 1:numel(eqTypes);
eqType = eqTypes{ind};
fieldname = bFields{ind};
for i = 1:tasksPerEq
task = createTask(job, @pctdemo_task_eqber_adaptive, 1, ...
{splitEbNoIdx{i}, eqType, params}, .
..
'UserData', fieldname);
set(task, 'FinishedFcn', @pctdemo_taskfin_eqber);
end
end
We can now submit the job and wait for it to finish.
submit(job);
waitForState(job, 'finished');
Retrieve the Results
As the tasks finish, the task finished callback function updates the BER plots and collects the task results into the UserData property of the job. Therefore, we do not need to perform any plotting here, and our data retrieval consists simply of getting calculated BERs from the UserData property of the job and verifying our results. We throw an error if we could not obtain any results, but display a warning if we got only some of the results.
jobdata = get(job, 'UserData'); if isempty(jobdata) taskErrorMsgs = pctdemo_helper_getUniqueErrors(job); destroy(job); error('distcomp:demo:EmptyJobUserData', ... ['Could not obtain any job results. ', ... 'The following error(s) occurred \nduring task execution:\n\n%s'] , ... taskErrorMsgs); end
We verify that we obtained all BER values by checking whether there are any NaN values remaining in the BERs output structure.
BERs = jobdata{2};
c = struct2cell(BERs);
if any(isnan([c{:}]))
taskErrorMsgs = pctdemo_helper_getUniqueErrors(job);
warning('distcomp:demo:IncompleteJobResults', ...
['Did not obtain all BER values. \nThe following error(s) ' ...
'occurred during task execution:\n\n%s'], taskErrorMsgs);
end
We have now finished all the verifications, so we can destroy the job.
destroy(job);
Measure the Elapsed Time
The time used for the distributed computations should be compared against the time it takes to perform the same set of calculations in the Sequential BER Performance of Several Equalizer Types demo. The elapsed time varies with the underlying hardware and network infrastructure.
elapsedTime = etime(clock, startTime);
fprintf('Elapsed time is %2.1f seconds\n', elapsedTime);
Elapsed time is 17.4 seconds
Store