Advertisement
Guest User

Untitled

a guest
Oct 25th, 2010
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.11 KB | None | 0 0
  1. //
  2. // ASCII Art DFT Plotter - Josh Blum
  3. //
  4.  
  5. #ifndef ASCII_ART_DFT_HPP
  6. #define ASCII_ART_DFT_HPP
  7.  
  8. #include <string>
  9. #include <cstddef>
  10. #include <vector>
  11. #include <complex>
  12. #include <stdexcept>
  13.  
  14. namespace acsii_art_dft{
  15.  
  16.     //! Type produced by the log power DFT function
  17.     typedef std::vector<float> log_pwr_dft_type;
  18.  
  19.     /*!
  20.      * Get a logarithmic power DFT of the input samples.
  21.      * Samples are expected to be in the range [-1.0, 1.0].
  22.      * \param samps a pointer to an array of complex samples
  23.      * \param nsamps the number of samples in the array
  24.      * \return a real range of DFT bins in units of dB
  25.      */
  26.     template <typename T> log_pwr_dft_type log_pwr_dft(
  27.         const std::complex<T> *samps, size_t nsamps
  28.     );
  29.  
  30.     /*!
  31.      * Convert a DFT to a printable ascii plot.
  32.      * \param dft the log power dft bins
  33.      * \param width the frame width in characters
  34.      * \param height the frame height in characters
  35.      * \param samp_rate the sample rate in Sps
  36.      * \param dc_freq the DC frequency in Hz
  37.      * \param dyn_rng the dynamic range in dB
  38.      * \param ref_lvl the reference level in dB
  39.      * \return the plot as an ascii string
  40.      */
  41.     std::string dft_to_plot(
  42.         const log_pwr_dft_type &dft,
  43.         size_t width,
  44.         size_t height,
  45.         double samp_rate,
  46.         double dc_freq,
  47.         float dyn_rng,
  48.         float ref_lvl
  49.     );
  50.  
  51. } //namespace ascii_dft
  52.  
  53. /***********************************************************************
  54.  * Implementation includes
  55.  **********************************************************************/
  56. #include <cmath>
  57. #include <sstream>
  58. #include <algorithm>
  59.  
  60. /***********************************************************************
  61.  * Helper functions
  62.  **********************************************************************/
  63. namespace {/*anon*/
  64.  
  65.     static const double pi = double(std::acos(-1.0));
  66.  
  67.     //! Round a floating-point value to the nearest integer
  68.     template <typename T> int iround(T val){
  69.         return (val > 0)? int(val + 0.5) : int(val - 0.5);
  70.     }
  71.  
  72.     //! Pick the closest number that is nice to display
  73.     template <typename T> T to_clean_num(const T num){
  74.         if (num == 0) return 0;
  75.         const T pow10 = std::pow(T(10), int(std::floor(std::log10(std::abs(num)))));
  76.         const T norm = std::abs(num)/pow10;
  77.         static const int cleans[] = {1, 2, 5, 10};
  78.         int clean = cleans[0];
  79.         for (size_t i = 1; i < sizeof(cleans)/sizeof(cleans[0]); i++){
  80.             if (std::abs(norm - cleans[i]) < std::abs(norm - clean))
  81.                 clean = cleans[i];
  82.         }
  83.         return ((num < 0)? -1 : 1)*clean*pow10;
  84.     }
  85.  
  86.     //! Compute an FFT with pre-computed factors using Cooley-Tukey
  87.     template <typename T> std::complex<T> ct_fft_f(
  88.         const std::complex<T> *samps, size_t nsamps,
  89.         const std::complex<T> *factors,
  90.         size_t start = 0, size_t step = 1
  91.     ){
  92.         if (nsamps == 1) return samps[start];
  93.         std::complex<T> E_k = ct_fft_f(samps, nsamps/2, factors+1, start,      step*2);
  94.         std::complex<T> O_k = ct_fft_f(samps, nsamps/2, factors+1, start+step, step*2);
  95.         return E_k + factors[0]*O_k;
  96.     }
  97.  
  98.     //! Compute an FFT for a particular bin k using Cooley-Tukey
  99.     template <typename T> std::complex<T> ct_fft_k(
  100.         const std::complex<T> *samps, size_t nsamps, size_t k
  101.     ){
  102.         //pre-compute the factors to use in Cooley-Tukey
  103.         std::vector<std::complex<T> > factors;
  104.         for (size_t N = nsamps; N != 0; N /= 2){
  105.             factors.push_back(std::exp(std::complex<T>(0, T(-2*pi*k/N))));
  106.         }
  107.         return ct_fft_f(samps, nsamps, &factors.front());
  108.     }
  109.  
  110.     //! Helper class to build a DFT plot frame
  111.     class frame_type{
  112.     public:
  113.         frame_type(size_t width, size_t height):
  114.             _frame(width-1, std::vector<char>(height, ' '))
  115.         {
  116.             /* NOP */
  117.         }
  118.  
  119.         //accessors to parts of the frame
  120.         char &get_plot(size_t b, size_t z){return _frame.at(b+albl_w).at(z+flbl_h);}
  121.         char &get_albl(size_t b, size_t z){return _frame.at(b)       .at(z+flbl_h);}
  122.         char &get_ulbl(size_t b)          {return _frame.at(b)       .at(flbl_h-1);}
  123.         char &get_flbl(size_t b)          {return _frame.at(b+albl_w).at(flbl_h-1);}
  124.  
  125.         //dimension accessors
  126.         size_t get_plot_h(void) const{return _frame.front().size() - flbl_h;}
  127.         size_t get_plot_w(void) const{return _frame.size() - albl_w;}
  128.         size_t get_albl_w(void) const{return albl_w;}
  129.  
  130.         std::string to_string(void){
  131.             std::stringstream frame_ss;
  132.             for (size_t z = 0; z < _frame.front().size(); z++){
  133.                 for (size_t b = 0; b < _frame.size(); b++){
  134.                     frame_ss << _frame[b][_frame[b].size()-z-1];
  135.                 }
  136.                 frame_ss << std::endl;
  137.             }
  138.             return frame_ss.str();
  139.         }
  140.  
  141.     private:
  142.         static const size_t albl_w = 6, flbl_h = 1;
  143.         std::vector<std::vector<char> > _frame;
  144.     };
  145.  
  146. } //namespace /*anon*/
  147.  
  148. /***********************************************************************
  149.  * Implementation code
  150.  **********************************************************************/
  151. namespace acsii_art_dft{
  152.  
  153.     //! skip constants for amplitude and frequency labels
  154.     static const size_t albl_skip = 5, flbl_skip = 20;
  155.  
  156.     template <typename T> log_pwr_dft_type log_pwr_dft(
  157.         const std::complex<T> *samps, size_t nsamps
  158.     ){
  159.         if (nsamps & (nsamps - 1))
  160.             throw std::runtime_error("num samps is not a power of 2");
  161.  
  162.         //compute the window
  163.         double win_pwr = 0;
  164.         std::vector<std::complex<T> > win_samps;
  165.         for(size_t n = 0; n < nsamps; n++){
  166.             //double w_n = 1;
  167.             //double w_n = 0.54 //hamming window
  168.             //    -0.46*std::cos(2*pi*n/(nsamps-1))
  169.             //;
  170.             double w_n = 0.35875 //blackman-harris window
  171.                 -0.48829*std::cos(2*pi*n/(nsamps-1))
  172.                 +0.14128*std::cos(4*pi*n/(nsamps-1))
  173.                 -0.01168*std::cos(6*pi*n/(nsamps-1))
  174.             ;
  175.             //double w_n = 1 // flat top window
  176.             //    -1.930*std::cos(2*pi*n/(nsamps-1))
  177.             //    +1.290*std::cos(4*pi*n/(nsamps-1))
  178.             //    -0.388*std::cos(6*pi*n/(nsamps-1))
  179.             //    +0.032*std::cos(8*pi*n/(nsamps-1))
  180.             //;
  181.             win_samps.push_back(T(w_n)*samps[n]);
  182.             win_pwr += w_n*w_n;
  183.         }
  184.  
  185.         //compute the log-power dft
  186.         log_pwr_dft_type log_pwr_dft;
  187.         for(size_t k = 0; k < nsamps; k++){
  188.             std::complex<T> dft_k = ct_fft_k(&win_samps.front(), nsamps, k);
  189.             log_pwr_dft.push_back(float(
  190.                 + 20*std::log10(std::abs(dft_k))
  191.                 - 20*std::log10(T(nsamps))
  192.                 - 10*std::log10(win_pwr/nsamps)
  193.                 + 3
  194.             ));
  195.         }
  196.  
  197.         return log_pwr_dft;
  198.     }
  199.  
  200.     std::string dft_to_plot(
  201.         const log_pwr_dft_type &dft_,
  202.         size_t width,
  203.         size_t height,
  204.         double samp_rate,
  205.         double dc_freq,
  206.         float dyn_rng,
  207.         float ref_lvl
  208.     ){
  209.         frame_type frame(width, height); //fill this frame
  210.  
  211.         //re-order the dft so dc in in the center
  212.         const size_t num_bins = dft_.size() - 1 + dft_.size()%2; //make it odd
  213.         log_pwr_dft_type dft(num_bins);
  214.         for (size_t n = 0; n < num_bins; n++){
  215.             dft[n] = dft_[(n + num_bins/2)%num_bins];
  216.         }
  217.  
  218.         //fill the plot with dft bins
  219.         for (size_t b = 0; b < frame.get_plot_w(); b++){
  220.             //indexes from the dft to grab for the plot
  221.             const size_t n_start = std::max(iround(double(b-0.5)*(num_bins-1)/(frame.get_plot_w()-1)), 0);
  222.             const size_t n_stop  = std::min(iround(double(b+0.5)*(num_bins-1)/(frame.get_plot_w()-1)), int(num_bins));
  223.  
  224.             //calculate val as the max across points
  225.             float val = dft.at(n_start);
  226.             for (size_t n = n_start; n < n_stop; n++) val = std::max(val, dft.at(n));
  227.  
  228.             const float scaled = (val - (ref_lvl - dyn_rng))*(frame.get_plot_h()-1)/dyn_rng;
  229.             for (size_t z = 0; z < frame.get_plot_h(); z++){
  230.                 static const std::string syms(".:!|");
  231.                 if      (scaled-z > 1) frame.get_plot(b, z) = syms.at(syms.size()-1);
  232.                 else if (scaled-z > 0) frame.get_plot(b, z) = syms.at(size_t((scaled-z)*syms.size()));
  233.             }
  234.         }
  235.  
  236.         //create vertical amplitude labels
  237.         const float db_step = to_clean_num(dyn_rng/(frame.get_plot_h()-1)*albl_skip);
  238.         for (
  239.             float db = db_step*(int((ref_lvl - dyn_rng)/db_step));
  240.             db      <=  db_step*(int(ref_lvl/db_step));
  241.             db      +=  db_step
  242.         ){
  243.             const int z = iround((db - (ref_lvl - dyn_rng))*(frame.get_plot_h()-1)/dyn_rng);
  244.             if (z < 0 or size_t(z) >= frame.get_plot_h()) continue;
  245.             std::stringstream ss; ss << db; std::string lbl = ss.str();
  246.             for (size_t i = 0; i < lbl.size() and i < frame.get_albl_w(); i++){
  247.                 frame.get_albl(i, z) = lbl[i];
  248.             }
  249.         }
  250.  
  251.         //create vertical units label
  252.         std::string ulbl = "dBfs";
  253.         for (size_t i = 0; i < ulbl.size(); i++){
  254.             frame.get_ulbl(i+1) = ulbl[i];
  255.         }
  256.  
  257.         //create horizontal frequency labels
  258.         const double f_step = to_clean_num(samp_rate/frame.get_plot_w()*flbl_skip);
  259.         for (
  260.             double freq = f_step*int((-samp_rate/2/f_step));
  261.             freq       <= f_step*int((+samp_rate/2/f_step));
  262.             freq       += f_step
  263.         ){
  264.             const int b = iround((freq + samp_rate/2)*(frame.get_plot_w()-1)/samp_rate);
  265.             std::stringstream ss; ss << (freq+dc_freq)/1e6 << "MHz"; std::string lbl = ss.str();
  266.             if (b < int(lbl.size()/2) or b + lbl.size() - lbl.size()/2 >= frame.get_plot_w()) continue;
  267.             for (size_t i = 0; i < lbl.size(); i++){
  268.                 frame.get_flbl(b + i - lbl.size()/2) = lbl[i];
  269.             }
  270.         }
  271.  
  272.         return frame.to_string();
  273.     }
  274. } //namespace ascii_dft
  275.  
  276. #endif /*ASCII_ART_DFT_HPP*/
  277.  
  278. /*
  279.  
  280. //example main function to test the dft
  281.  
  282. #include <iostream>
  283. #include <cstdlib>
  284. #include <curses.h>
  285.  
  286. int main(void){
  287.     initscr();
  288.  
  289.     while (true){
  290.         clear();
  291.  
  292.         std::vector<std::complex<float> > samples;
  293.         for(size_t i = 0; i < 512; i++){
  294.             samples.push_back(std::complex<float>(
  295.                 float(std::rand() - RAND_MAX/2)/(RAND_MAX)/4,
  296.                 float(std::rand() - RAND_MAX/2)/(RAND_MAX)/4
  297.             ));
  298.             samples[i] += 0.5*std::sin(i*3.14/2) + 0.7;
  299.         }
  300.  
  301.         acsii_art_dft::log_pwr_dft_type dft;
  302.         dft = acsii_art_dft::log_pwr_dft(&samples.front(), samples.size());
  303.  
  304.         printw("%s", acsii_art_dft::dft_to_plot(
  305.             dft, COLS, LINES,
  306.             12.5e4, 2.45e9,
  307.             60, 0
  308.         ).c_str());
  309.  
  310.         sleep(1);
  311.     }
  312.  
  313.  
  314.     endwin();
  315.     std::cout << "here\n";
  316.     return 0;
  317. }
  318.  
  319. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement