User Tools

Site Tools


milestone_3

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

milestone_3 [2015/01/29 08:47]
hutch
milestone_3 [2023/02/06 10:26] (current)
scott [Software Coding Standard]
Line 1: Line 1:
-===== Milestone 3 =====+====== Milestone 3 ======
  
 In this milestone you will implement most of the '​C'​ code that implements the laser-tag system. In this milestone you will implement most of the '​C'​ code that implements the laser-tag system.
  
-==== Goal ====+===== Goal =====
  
-Demonstrate the system working with a feedback ​cable (provided) connecting the output of the transmitter to the input of the ADC. You will demonstrate the ability of your system to:+Demonstrate the system working with a feedback ​connection ​(built into the ECEn Development Board) connecting the output of the transmitter to the input of the ADC. You will demonstrate the ability of your system to:
   * detect hits on each of the 10 frequencies,​   * detect hits on each of the 10 frequencies,​
   * no false detects,   * no false detects,
Line 11: Line 11:
   * display the computed power of each of the channels on the TFT with the provided histogram package.   * display the computed power of each of the channels on the TFT with the provided histogram package.
  
-Note: you don't need the feedback cable until the final task. 
  
-==== Organization ==== 
  
-This milestone will be divided into three tasks that will be due and passed off weekly. +===== Organization =====
-  - [[Milestone 3 Task 1 | Task 1]]. Implementation and verification of the FIR and IIR filters (filter.c). Demonstrate all sets of of IIR coefficients. +
-  - [[Milestone 3 Task 2 | Task 2]]. Implementation of the transmitter state-machine (transmitter.c),​ the trigger state-machine (trigger.c),​ the hitLedTimer state-machine (hitLedTimer.c) and the lockoutTimer state-machine (lockoutTimer.c). +
-  - [[Milestone 3 Task 3 | Task 3]]. Implementation of the detector (detector.c) and integration with all other parts of the project in order to perform the final pass-off for the overall milestone.+
  
-==== Task 1: Implement ​the FIR and IIR Filters ====+This milestone will be divided into three tasks that will be due and passed off as recorded in Learning Suite. 
 +  - [[Milestone 3 Task 1 | Task 1]]. Implementation and verification of the FIR and IIR filters (filter.c). Demonstrate all sets of IIR coefficients. 
 +  - [[Milestone 3 Task 2 | Task 2]]. Implementation of the transmitter state-machine (transmitter.c),​ the trigger state-machine (trigger.c),​ the hitLedTimer state-machine (hitLedTimer.c),​ the lockoutTimer state-machine (lockoutTimer.c),​ and the code to compute the power in the output from each IIR band-pass filter. 
 +  - [[Milestone 3 Task 3 | Task 3]]. Implementation of the detector (detector.c) and integration with all other parts of the project in order to perform the final pass-off for the overall milestone using the feedback provided by the development board.
  
-In this task you will implement the FIR and IIR filters using '​C'​ code. Let's start with the FIR filter. Remember that the FIR filter is implemented as a weighted sum of some past number of inputs. Here's an example from Wikipedia:+===== Software Coding Standard =====
  
-{{ ::​firexamplefromwikipedia.jpg?​600 ​|}}+All of the tasks comprising Milestone 3 are software tasks and must adhere to the [[https://byu-cpe.github.io/​ecen330/​other/​coding-standard/​|coding standard]]. **Exception:​ clang-format is not required. Ignore any clang-format rules.**
  
-It can be confusing to transition from the finite array-based approach used in MatLab to the "​infinite"​ approach ​that is required in the implementation of a signal-processing system. To wit, the inputs ​and outputs of a real-time signal-processing system are essentially infinite. As such, the array-based notation in the equation above fails us because the output is an indexed array ''​y[n]''​. You want to eliminate the ''​[n]''​ part so that the output is simply ''​y''​+See learning suite for a breakdown of the credit ​that you receive for pass-off and source-code submission.
  
-When we saw the English-based description from Wikipedia (see above), there were no indexes. Remember that the FIR-filter is implemented as a weighted-sum of some past number of inputs. All those indexes, the ''​i'', ​ the ''​k'',​ etc., are just a way to keep the coefficients aligned with the data. As long as we can keep the incoming inputs properly aligned with the coefficients,​ we are good to go. 
- 
-The idea is pretty simple and is based upon these ideas: 
-  - Create a data structure that will keep an ordered history of past values. The size of the data structure must match the order of the filter, e.g., a 50-tap FIR filter needs a history of 50 values. 
-  - At start-up time, fill the data structure with zeros. 
-  - As each new value arrives, throw away the oldest value. 
-  - Retrieve values from the data structure so that they can be multiplied with the correct coefficients. 
- 
-As you have probably guessed at this point, the queues that you implemented as a part of Milestone 1 are the perfect data structure for this purpose. 
- 
----- 
- 
-=== Filters and Queues === 
- 
-You can implement a FIR filter using the queues that you have already coded. Consider an example where the FIR filter uses 4 past values to compute its output. In the example code below, I have "​pushed"​ four values onto the queue. Assume that these are 4 values that are based on values from the ZYBO's ADC.  
- 
-<code C> 
-#include "​queue.h"​ 
-#include <​stdio.h>​ 
-#define FIR_COEF_COUNT 4 
- 
-int main() { 
-  // Initialization stuff. 
-  queue_t xQ;                           // x is the queue with the input history for the queue. 
-  queue_init(&​xQ,​ FIR_COEF_COUNT); ​     // Size of history queue must equal coefficient count. 
-  for (int i=0; i<​FIR_COEF_COUNT;​ i++)  // Start out with an empty queue (all zeroes). 
-    queue_overwritePush(&​xQ,​ 0.0); 
- 
-  double b[FIR_COEF_COUNT] = {0.25, 0.5, 0.75, 1.0};  // These coefficients are used for this example. 
- 
-  // Add some example inputs to the queue. 
-  queue_overwritePush(&​xQ,​ -0.1); ​ // Add a new input to the queue (oldest in input history). 
-  queue_overwritePush(&​xQ,​ -0.4); ​ // Add a new input to the queue. 
-  queue_overwritePush(&​xQ,​ 0.24); ​ // Add a new input to the queue. 
-  queue_overwritePush(&​xQ, ​ 0.54); // Add a new input to the queue (newest in input history). 
- 
-  // Compute output of FIR-filter (y) 
-  // using a single lone statement (broken into 4 lines to keep it readable). 
-  // This is just for example. You will use a for-loop as shown below. 
-  double y; 
-  y = queue_readElementAt(&​xQ,​ 0) * b[3] + 
-      queue_readElementAt(&​xQ,​ 1) * b[2] + 
-      queue_readElementAt(&​xQ,​ 2) * b[1] + 
-      queue_readElementAt(&​xQ,​ 3) * b[0]; 
-  printf("​%lf\n\r",​ y); 
- 
-  // Add new input. 
-  queue_overwritePush(&​xQ,​ 0.33); 
- 
-  // Compute the next y, exactly the same computation as above but with a concise for-loop instead. 
-  // += accumulates the result during the for-loop. Must start out with y = 0. 
-  y = 0.0; 
-  // This for-loop performs the identical computation to that shown above. for-loop is correct way to do it. 
-  for (int i=0; i<​FIR_COEF_COUNT;​ i++) { 
-    y += queue_readElementAt(&​xQ,​ i) * b[FIR_COEF_COUNT-1-i]; ​ // iteratively adds the (b * input) products. 
-  } 
-  printf("​%lf\n\r",​ y); 
- 
-} 
-</​code>​ 
- 
-Pictorially,​ implementing the FIR filter would appear as shown below. As shown, you can see the 4 values that were pushed onto the queue as well as the total computations. ​ 
- 
-{{ ::​firqueueexample1.jpg?​800 |}} 
- 
-The next computation of y is shown below. You can see that by adding a new value to the queue, all of the other values shifted over, relative to the ''​b''​ coefficients. Thus you can use the same code to compute ''​y''​ over and over again. 
- 
-{{ ::​firqueueexample2.jpg?​800 |}} 
- 
-You can see that purpose of the queue is to store past values in the order that they were received and make all of the queue-contained values accessible during the computation. 
- 
----- 
- 
-=== What About Decimation? === 
- 
-Decimation is really easy. In our laser-tag system we will be decimating by 10. All we do is invoke our FIR-filter each 10 samples. As you add incoming samples to the FIR-filter input-queue,​ only invoke the FIR-filter each time you have received 10 new inputs. You will invoke the IIR filters right after you invoke the FIR-filter. 
- 
----- 
- 
-=== IIR Filters and Queues === 
- 
-The IIR filter equations can be implemented similarly. However, the IIR filter relies on two signal histories: y and z, as shown in the equation below. 
- 
-{{ ::​iirequation.jpg?​300 |}} 
- 
-As you can see from the equation, you would need two queues of different sizes (5 and 4) to keep the necessary signal histories. The only other difference is that the computed value (''​z''​) is also pushed onto the queue that keeps a history of z values. This is essentially what puts the "​IIR"​ in the filter, e.g, feedback. The size of the queues for your IIR filter will depend upon your filter design. My IIR filter uses 8 "​a"​ coefficients and 9 "​b"​ coefficients,​ for example. 
- 
----- 
- 
-=== Filter Structure === 
- 
-Viewed as a data-flow image, the filter structure would look as depicted below. In '​C'​ code, the data-flow is implemented sequentially,​ as in, 1) get a new sample from the ADC, 2) process the new sample with the FIR-filter to produce a single output (y), and 3) process the single output of the FIR-filter with the 10 IIR filters to produce 10 outputs (z[0] - z[9]). You do this process forever. 
- 
-{{ ::​filterstructure.jpg?​600 |}} 
- 
----- 
- 
-=== Steps to Implement the Filters With Queues === 
-  - Declare and initialize 12 queues: an x-queue to store incoming inputs from the ADC, a y-queue that holds the history of outputs from the FIR filter, and an array of 10 z-queues that hold the history of outputs from the IIR filter. Declare these as static ''​queue_t''​ variables in ''​filter.c''​. 
-  - At start up, fill each of the queues with zeroes. This provides a stable starting condition. **DO NOT USE AN IF-STATEMENT TO HANDLE NEGATIVE TIME.** It is much cleaner just to "​zero-out"​ the values in the queues that hold the histories. Extra if-statements for this kind of thing just add bugs and make life painful. 
- 
-At run-time, you "​run"​ the filters by doing the following: 
-  - As they become available, add new inputs to the x_queue. This is what the function ''​filter_addNewInput()''​ is supposed to do. 
-  - Each time you receive 10 new inputs, run the filters as follows: 
-    - invoke ''​filter_firFilter()''​. This will use the appropriate FIR coefficients and the x_queue that contains the necessary signal history to compute the output of the FIR filter. Add this output to the y_queue. Also return this computed value. 
-    - invoke the filter_iirFilter() function 10 times, once for each frequency. Also return the computed z-value for each frequency.  ​ 
- 
-The code below provides a little more detail. Note that this code will **not** compile. I provide it as an example of how you will use the ''​filter_addNewInput()'',​ ''​filter_fir()''​ and ''​filter_iir()''​ functions. You would run the code below each time there is at least one new available value. For this task, you are only verifying that your filters work correctly so you don't need to the decimation in this task. If you want to use this code for verification,​ simply set the decimation factor to 1. 
- 
-<code C> 
-#define FILTER_IIR_FILTER_COUNT 10 
-#define FILTER_FIR_DECIMATION_FACTOR 10 
- 
-int sampleCount = 0; 
-filter_addNewInput(adcValue); ​                      // add a new input to the FIR history queue.  
-sampleCount++; ​                                     // Keep track of how many samples you have acquired. 
-if (sampleCount == FILTER_FIR_DECIMATION_FACTOR) {  // Only invoke the filters after every DECIMATION_FACTOR times. 
-  sampleCount = 0;                                  // Reset the sample count when you run the filters. 
-  filter_firFilter() ​                               // Runs the FIR filter on the accumulated input and places the output in the y-queue. 
-  for (int filterNumber=0;​ filterNumber<​FILTER_IIR_FILTER_COUNT;​ filterNumber++) { 
-    filter_iirFilter(filterNumber); ​                // Run each of the IIR filters. 
-  } 
-} 
- 
-</​code>​ 
- 
-=== Verifying the Filters === 
- 
-It is important to carefully verify your filters at this stage. Here is the process that you will be required to follow. 
-  - Verify each filter separately. We will verify the FIR filter without decimation as that dramatically reduces the amount of verification data that you will need. You will implement decimation later during Task 3. 
-  - As the FIR filter is fairly simple, you can verify it with data your produce from the ''​filter()''​ command in MatLab (it is allowed for this purpose). 
-  - The IIR filter is more complex. I recommend that you use a spreadsheet to verify it. Using a spreadsheet makes it easy to generate the intermediate values to make verification much easier. In my implementation,​ I computed the two summations separately, referring to the one that uses a-coefficients as the "​a-summation"​ and referring to the other summation as the "​b-summation."​ Here is the {{::​iirspreadsheet1.xlsx|spreadsheet}} that I used to verify my IIR filter. You may need to modify it depending upon your filter design. ​ 
-  - For each filter (FIR and IIR), using the same generated test data-sets, take the test-data and outputs from the spreadsheet (or MatLab) and paste them into your program as array initializations. Write a filter_runTest() function that: 
-    - invokes your filter_fir() function and compare its output against the spreadsheet- (or MatLab)-initialized output to verify its correctness. If the results are correct, print "FIR filter tests OK." 
-    - invokes your filter_iir() function and compare its output against the spreadsheet- (or Matlab)-initialized output to verify its correctness. If the results are correct, print "IIR filter tests OK." 
-  - If either filter fails the test, print out the contents of the queues for the failing and the failing index. 
- 
-Here's an simple example of what a portion of your filter_runTest() function could look like: 
- 
-<code C> 
-#include "​filter.h"​ 
-#include <​stdio.h>​ 
-#include <​stdbool.h>​ 
- 
-#define TEST_DATA_COUNT 5 
- 
-const double firInputTestData[TEST_DATA_COUNT] = 
-  {0.3, 
-   0.25, 
-   0.41, 
-   ​-0.11,​ 
-   -0.5 
-  }; 
- 
-const double firOutputTestData[TEST_DATA_COUNT] = 
-  {0.6, 
-   0.51, 
-   0.25, 
-   ​-0.78,​ 
-   -0.12 
-  }; 
- 
- 
-int main() { 
-  filter_init(); ​       // Standard init function. 
-  bool success = true; // Be optimistic 
-  uint16_t failedIndex = 0; // Keep track of the index where things failed. 
-  for (uint16_t i=0; i<​TEST_DATA_COUNT;​ i++) { 
-    // No decimation for this test - just invoke the FIR filter each time you add new data. 
-    filter_addNewInput(firInputTestData[i]);​ 
-    if (filter_firFilter() != firOutputTestData[i]) { // Is the output what you expected? 
-      success = false; ​                                // Nope, note failure and break out of the loop. 
-      failedIndex = i;                                 // Note the failed index. 
-      break; 
-    } 
-  } 
-  if (success) { 
-    printf("​Success!\n\r"​);​ 
-  } 
-  else { 
-    printf("​Failure!\n\r"​);​ 
-    printf("​First failure detected at index: %d\n\r",​ failedIndex);​ 
-  } 
-} 
-</​code>​ 
- 
-I show a screen-capture of my spreadsheet below. ​ 
-  * b refers to the b-coefficients,​ 
-  * y refers to the outputs from the FIR filter, 
-  * b-summation refers to the summation of the b-coefficients multiplied with the y-history, 
-  * z refers to the z-history 
-  * a refers to the a-coefficients,​ 
-  * a-summation refers to the summation of the a-coefficients multiplied with the z-history. 
- 
-Here's the basic IIR equation for reference (it's the same one as I pasted above). Note that the order is incorrect as the one in the spreadsheet is higher order. The computational structure is the same. 
- 
-{{ ::​iirequation.jpg?​300 |}} 
- 
-{{ ::​iirspreadsheetexample.jpg?​600 |}} 
- 
----- 
- 
-=== Requirements === 
-  - Implement all of the functions shown in filter.h, except for those with "​power"​ in the name (we will implement those later). 
-  - Use queues as described in this task. You must have an xQueue, a yQueue, and an array containing 10 zQueues. You must declare these queues as ''​static''​ in ''​filter.c''​. 
-  - You must implement your filters as follows. ''​filter_addNewInput(value)''​ must push value onto xQueue. ''​filter_firFilter()''​ reads values from xQueue using ''​filter_readElementAt()''​ and pushes its output onto yQueue. ''​filter_iirFilter()''​ reads values from yQueue using ''​filter_readElementAt()''​ and pushes it output onto zQueue[filterNumber]. 
-  - You must implement the ''​filter_runTest()''​ function as described. 
- 
-Here's an example of how to declare and initialize an array of queues. Note this code will not compile "as is." 
- 
-<code C> 
-#define Z_QUEUE_SIZE IIR_A_COEFFICIENT_COUNT 
-static queue_t zQueue[FILTER_IIR_FILTER_COUNT];​  
- 
-void initZQueues() { 
-  for (int i=0; i<​FILTER_IIR_FILTER_COUNT;​ i++) { 
-    queue_init(&​(zQueue[i]),​ Z_QUEUE_SIZE);​ 
-    for (int j=0; j<​Z_QUEUE_SIZE;​ j++) 
-     ​queue_overwritePush(&​(zQueue[i]),​ 0.0); 
-  } 
-} 
- 
-</​code>​ 
- 
----- 
- 
-=== What You Need to Get Started === 
-  - A working, bug-free queue. Make sure to check the correctness of your queue code with the test code on the [[queues|queue]] page. Note that you are responsible to thoroughly test your queue code to ensure that it is bug-free. The test-code on the queue page is provided to help with this task. Even if your queue code runs correctly with the provided test code, it does not guarantee that it is bug-free. On the other hand, if you code does not run correctly with the provided test-code, you queue code has bugs in it. 
-  - All necessary coefficients for the FIR and IIR filters. You will need one set of coefficients for the FIR filter and 10 sets of coefficients for the IIR filter code, e.g., for the 10 band-pass filters. 
- 
----- 
- 
-=== Pass-Off === 
-To pass off this task, you must: 
-  - show the TA that your queue code successfully passes the test code on the queue page, 
-  - show the TA how your generated input/​output verification data for the FIR and IIR filters, 
-  - show the TA that your filter_runTest() function uses the verification data to verify the correctness of your code, 
-  - run filter_runTest() to demonstrate that your code is correct. 
-  - You must test all IIR filters. Thus, you will run the filter_iirFilter() in a loop within filter_runTest(),​ passing in 0:9 for the filter-number argument. 
- 
----- 
-=== filter.h === 
-Here is the filter.h file. You do not need to implement any of the functions that relate to power for this milestone. 
-<code C> 
-#ifndef FILTER_H_ 
-#define FILTER_H_ 
- 
-#include <​stdint.h>​ 
-#include "​queue.h"​ 
- 
-#define FILTER_IIR_FILTER_COUNT 10      // You need this many IIR filters. 
-#define FILTER_FIR_DECIMATION_FACTOR 10 // Filter needs this many new inputs to compute a new output. 
-#define FILTER_INPUT_PULSE_WIDTH 2000 // This is the width of the pulse you are looking for, in terms of decimated sample count. 
- 
-// Filtering routines for the laser-tag project. 
-// Filtering is performed by a two-stage filter, as described below. 
- 
-// 1. First filter is a decimating FIR filter with a configurable number of taps and decimation factor. 
-// 2. The output from the decimating FIR filter is passed through a bank of 10 IIR filters. The 
-// characteristics of the IIR filter are fixed. 
- 
-// The decimation factor determines how many new samples must be read for each new filter output. 
-//uint16_t filter_getFirDecimationFactor();​ 
- 
-// Must call this prior to using any filter functions. 
-// Will initialize queues and any other stuff you need to init before using your filters. 
-// Make sure to fill your queues with zeros after you initialize them. 
-void filter_init();​ 
- 
-// Print out the contents of the xQueue for debugging purposes. 
-void filter_printXQueue();​ 
- 
-// Print out the contents of yQueue for debugging purposes. 
-void filter_printYQueue();​ 
- 
-// Print out the contents of the the specified zQueue for debugging purposes. 
-void filter_printZQueue(uint16_t filterNumber);​ 
- 
-// Use this to copy an input into the input queue (x_queue). 
-void filter_addNewInput(double x); 
- 
-// Invokes the FIR filter. Returns the output from the FIR filter. Also adds the output to the y_queue for use by the IIR filter. 
-double filter_firFilter();​ 
- 
-// Use this to invoke a single iir filter. Uses the y_queue and z_queues as input. Returns the IIR-filter output. 
-double filter_iirFilter(uint16_t filterNumber);​ 
- 
-// Use this to compute the power for values contained in a queue. 
-// If force == true, then recompute everything from scratch. 
-double filter_computePower(uint16_t filterNumber,​ bool forceComputeFromScratch,​ bool debugPrint);​ 
- 
-double filter_getCurrentPowerValue(uint16_t filterNumber);​ 
- 
-// Uses the last computed-power values, scales them to the provided lowerBound and upperBound args, returns the index of element containing the max value. 
-// The caller provides the normalizedArray that will contain the normalized values. indexOfMaxValue indicates the channel with max. power. 
-void filter_getNormalizedPowerValues(double normalizedArray[],​ uint16_t* indexOfMaxValue);​ 
- 
-void filter_runTest();​ 
- 
-#endif /* FILTER_H_ */ 
-</​code>​ 
milestone_3.1422546440.txt.gz · Last modified: 2015/01/29 08:47 by hutch