gnuradio-core: allows configuring FFTW to use ESTIMATE vs. MEASURE
[debian/gnuradio] / gnuradio-core / src / lib / general / gr_ofdm_frame_sink.cc
index c04aca3d7a5c75c0c359f319cd5a497c741b981a..26a5cf0fe16ad921c83613652f44bc73722cd4ef 100644 (file)
@@ -1,12 +1,12 @@
 /* -*- c++ -*- */
 /*
- * Copyright 2007 Free Software Foundation, Inc.
+ * Copyright 2007,2008 Free Software Foundation, Inc.
  * 
  * This file is part of GNU Radio
  * 
  * GNU Radio is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
+ * the Free Software Foundation; either version 3, or (at your option)
  * any later version.
  * 
  * GNU Radio is distributed in the hope that it will be useful,
 
 #include <gr_ofdm_frame_sink.h>
 #include <gr_io_signature.h>
+#include <gr_expj.h>
+#include <gr_math.h>
+#include <math.h>
 #include <cstdio>
 #include <stdexcept>
+#include <iostream>
+#include <string.h>
 
 #define VERBOSE 0
 
@@ -55,6 +60,11 @@ gr_ofdm_frame_sink::enter_have_sync()
 
   d_header = 0;
   d_headerbytelen_cnt = 0;
+
+  // Resetting PLL
+  d_freq = 0.0;
+  d_phase = 0.0;
+  fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0));
 }
 
 inline void
@@ -74,92 +84,183 @@ gr_ofdm_frame_sink::enter_have_header()
            d_packetlen, d_packet_whitener_offset);
 }
 
-unsigned char gr_ofdm_frame_sink::bpsk_slicer(gr_complex x)
-{
-  return (unsigned char)(x.real() > 0 ? 1 : 0);
-}
 
-unsigned char gr_ofdm_frame_sink::qpsk_slicer(gr_complex x)
+unsigned char gr_ofdm_frame_sink::slicer(const gr_complex x)
 {
-  unsigned char i = (x.real() > 0 ? 1 : 0);
-  unsigned char q = (x.imag() > 0 ? 1 : 0);
-
-  return (q << 1) | i;
+  unsigned int table_size = d_sym_value_out.size();
+  unsigned int min_index = 0;
+  float min_euclid_dist = norm(x - d_sym_position[0]);
+  float euclid_dist = 0;
+  
+  for (unsigned int j = 1; j < table_size; j++){
+    euclid_dist = norm(x - d_sym_position[j]);
+    if (euclid_dist < min_euclid_dist){
+      min_euclid_dist = euclid_dist;
+      min_index = j;
+    }
+  }
+  return d_sym_value_out[min_index];
 }
 
-unsigned int gr_ofdm_frame_sink::bpsk_demapper(const gr_complex *in,
-                                              unsigned char *out)
+unsigned int gr_ofdm_frame_sink::demapper(const gr_complex *in,
+                                         unsigned char *out)
 {
   unsigned int i=0, bytes_produced=0;
-
-  while(i < d_occupied_carriers) {
-
-    while((d_byte_offset < 8) && (i < d_occupied_carriers)) {
-      //fprintf(stderr, "%f+j%f\n", in[i].real(), in[i].imag()); 
-      d_partial_byte |= bpsk_slicer(in[i++]) << (d_byte_offset++);
-    }
-
-    if(d_byte_offset == 8) {
-      out[bytes_produced++] = d_partial_byte;
-      d_byte_offset = 0;
-      d_partial_byte = 0;
+  gr_complex carrier;
+
+  carrier=gr_expj(d_phase);
+
+  gr_complex accum_error = 0.0;
+  //while(i < d_occupied_carriers) {
+  while(i < d_subcarrier_map.size()) {
+    if(d_nresid > 0) {
+      d_partial_byte |= d_resid;
+      d_byte_offset += d_nresid;
+      d_nresid = 0;
+      d_resid = 0;
     }
-  }
+    
+    //while((d_byte_offset < 8) && (i < d_occupied_carriers)) {
+    while((d_byte_offset < 8) && (i < d_subcarrier_map.size())) {
+      //gr_complex sigrot = in[i]*carrier*d_dfe[i];
+      gr_complex sigrot = in[d_subcarrier_map[i]]*carrier*d_dfe[i];
+      
+      if(d_derotated_output != NULL){
+       d_derotated_output[i] = sigrot;
+      }
+      
+      unsigned char bits = slicer(sigrot);
 
-  return bytes_produced;
-}
+      gr_complex closest_sym = d_sym_position[bits];
+      
+      accum_error += sigrot * conj(closest_sym);
 
-unsigned int gr_ofdm_frame_sink::qpsk_demapper(const gr_complex *in,
-                                              unsigned char *out)
-{
-  unsigned int i=0, bytes_produced=0;
+      // FIX THE FOLLOWING STATEMENT
+      if (norm(sigrot)> 0.001) d_dfe[i] +=  d_eq_gain*(closest_sym/sigrot-d_dfe[i]);
+      
+      i++;
 
-  while(i < d_occupied_carriers) {
-    
-    while((d_byte_offset < 8) && (i < d_occupied_carriers)) {
-      //fprintf(stderr, "%f+j%f\n", in[i].real(), in[i].imag()); 
-      d_partial_byte |= qpsk_slicer(in[i++]) << (d_byte_offset);
-      d_byte_offset += 2;
+      if((8 - d_byte_offset) >= d_nbits) {
+       d_partial_byte |= bits << (d_byte_offset);
+       d_byte_offset += d_nbits;
+      }
+      else {
+       d_nresid = d_nbits-(8-d_byte_offset);
+       int mask = ((1<<(8-d_byte_offset))-1);
+       d_partial_byte |= (bits & mask) << d_byte_offset;
+       d_resid = bits >> (8-d_byte_offset);
+       d_byte_offset += (d_nbits - d_nresid);
+      }
+      //printf("demod symbol: %.4f + j%.4f   bits: %x   partial_byte: %x   byte_offset: %d   resid: %x   nresid: %d\n", 
+      //     in[i-1].real(), in[i-1].imag(), bits, d_partial_byte, d_byte_offset, d_resid, d_nresid);
     }
 
     if(d_byte_offset == 8) {
+      //printf("demod byte: %x \n\n", d_partial_byte);
       out[bytes_produced++] = d_partial_byte;
       d_byte_offset = 0;
       d_partial_byte = 0;
     }
   }
+  //std::cerr << "accum_error " << accum_error << std::endl;
+
+  float angle = arg(accum_error);
 
+  d_freq = d_freq - d_freq_gain*angle;
+  d_phase = d_phase + d_freq - d_phase_gain*angle;
+  if (d_phase >= 2*M_PI) d_phase -= 2*M_PI;
+  if (d_phase <0) d_phase += 2*M_PI;
+    
+  //if(VERBOSE)
+  //  std::cerr << angle << "\t" << d_freq << "\t" << d_phase << "\t" << std::endl;
+  
   return bytes_produced;
 }
 
+
 gr_ofdm_frame_sink_sptr
-gr_make_ofdm_frame_sink(gr_msg_queue_sptr target_queue, unsigned int occupied_carriers,
-                       const std::string &mod)
+gr_make_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, 
+                       const std::vector<unsigned char> &sym_value_out,
+                       gr_msg_queue_sptr target_queue, unsigned int occupied_carriers,
+                       float phase_gain, float freq_gain)
 {
-  return gr_ofdm_frame_sink_sptr(new gr_ofdm_frame_sink(target_queue, occupied_carriers, mod));
+  return gr_ofdm_frame_sink_sptr(new gr_ofdm_frame_sink(sym_position, sym_value_out,
+                                                       target_queue, occupied_carriers,
+                                                       phase_gain, freq_gain));
 }
 
 
-gr_ofdm_frame_sink::gr_ofdm_frame_sink(gr_msg_queue_sptr target_queue, unsigned int occupied_carriers,
-                                      const std::string &mod)
+gr_ofdm_frame_sink::gr_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, 
+                                      const std::vector<unsigned char> &sym_value_out,
+                                      gr_msg_queue_sptr target_queue, unsigned int occupied_carriers,
+                                      float phase_gain, float freq_gain)
   : gr_sync_block ("ofdm_frame_sink",
                   gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char)),
-                  gr_make_io_signature (0, 0, 0)),
+                  gr_make_io_signature (1, 1, sizeof(gr_complex)*occupied_carriers)),
     d_target_queue(target_queue), d_occupied_carriers(occupied_carriers), 
-    d_byte_offset(0), d_partial_byte(0)
+    d_byte_offset(0), d_partial_byte(0),
+    d_resid(0), d_nresid(0),d_phase(0),d_freq(0),d_phase_gain(phase_gain),d_freq_gain(freq_gain),
+    d_eq_gain(0.05)
 {
-  d_bytes_out = new unsigned char[(int)ceil(d_occupied_carriers/4.0)];
-
-  if(mod == "qpsk") {
-    d_demapper = &gr_ofdm_frame_sink::qpsk_demapper;
+  std::string carriers = "FE7F";
+
+  // A bit hacky to fill out carriers to occupied_carriers length
+  int diff = (d_occupied_carriers - 4*carriers.length()); 
+  while(diff > 7) {
+    carriers.insert(0, "f");
+    carriers.insert(carriers.length(), "f");
+    diff -= 8;
+  }
+  
+  // if there's extras left to be processed
+  // divide remaining to put on either side of current map
+  // all of this is done to stick with the concept of a carrier map string that
+  // can be later passed by the user, even though it'd be cleaner to just do this
+  // on the carrier map itself
+  int diff_left=0;
+  int diff_right=0;
+
+  // dictionary to convert from integers to ascii hex representation
+  char abc[16] = {'0', '1', '2', '3', '4', '5', '6', '7', 
+                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+  if(diff > 0) {
+    char c[2] = {0,0};
+
+    diff_left = (int)ceil((float)diff/2.0f);  // number of carriers to put on the left side
+    c[0] = abc[(1 << diff_left) - 1];         // convert to bits and move to ASCI integer
+    carriers.insert(0, c);
+    
+    diff_right = diff - diff_left;           // number of carriers to put on the right side
+    c[0] = abc[0xF^((1 << diff_right) - 1)];  // convert to bits and move to ASCI integer
+    carriers.insert(carriers.length(), c);
   }
-  else if(mod == "bpsk") {
-    d_demapper = &gr_ofdm_frame_sink::bpsk_demapper;
+
+  // It seemed like such a good idea at the time...
+  // because we are only dealing with the occupied_carriers
+  // at this point, the diff_left in the following compensates
+  // for any offset from the 0th carrier introduced
+  unsigned int i,j,k;
+  for(i = 0; i < (d_occupied_carriers/4)+diff_left; i++) {
+    char c = carriers[i];
+    for(j = 0; j < 4; j++) {
+      k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1;
+      if(k) {
+       d_subcarrier_map.push_back(4*i + j - diff_left);
+      }
+    }
   }
-  else {
-    throw std::invalid_argument("Modulation type must be BPSK or QPSK.");  
+  
+  // make sure we stay in the limit currently imposed by the occupied_carriers
+  if(d_subcarrier_map.size() > d_occupied_carriers) {
+    throw std::invalid_argument("gr_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers");
   }
 
+  d_bytes_out = new unsigned char[d_occupied_carriers];
+  d_dfe.resize(occupied_carriers);
+  fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0));
+
+  set_sym_value_out(sym_position, sym_value_out);
+  
   enter_search();
 }
 
@@ -168,6 +269,24 @@ gr_ofdm_frame_sink::~gr_ofdm_frame_sink ()
   delete [] d_bytes_out;
 }
 
+bool
+gr_ofdm_frame_sink::set_sym_value_out(const std::vector<gr_complex> &sym_position, 
+                                     const std::vector<unsigned char> &sym_value_out)
+{
+  if (sym_position.size() != sym_value_out.size())
+    return false;
+
+  if (sym_position.size()<1)
+    return false;
+
+  d_sym_position  = sym_position;
+  d_sym_value_out = sym_value_out;
+  d_nbits = (unsigned long)ceil(log10(d_sym_value_out.size()) / log10(2.0));
+
+  return true;
+}
+
+
 int
 gr_ofdm_frame_sink::work (int noutput_items,
                          gr_vector_const_void_star &input_items,
@@ -177,12 +296,15 @@ gr_ofdm_frame_sink::work (int noutput_items,
   const char *sig = (const char *) input_items[1];
   unsigned int j = 0;
   unsigned int bytes=0;
+
+  // If the output is connected, send it the derotated symbols
+  if(output_items.size() >= 1)
+    d_derotated_output = (gr_complex *)output_items[0];
+  else
+    d_derotated_output = NULL;
   
   if (VERBOSE)
     fprintf(stderr,">>> Entering state machine\n");
-  
-  //bytes = bpsk_demapper(&in[0], d_bytes_out);
-  bytes = (this->*d_demapper)(&in[0], d_bytes_out);  
 
   switch(d_state) {
       
@@ -196,6 +318,10 @@ gr_ofdm_frame_sink::work (int noutput_items,
     break;
 
   case STATE_HAVE_SYNC:
+    // only demod after getting the preamble signal; otherwise, the 
+    // equalizer taps will screw with the PLL performance
+    bytes = demapper(&in[0], d_bytes_out);
+    
     if (VERBOSE) {
       if(sig[0])
        printf("ERROR -- Found SYNC in HAVE_SYNC\n");
@@ -242,6 +368,8 @@ gr_ofdm_frame_sink::work (int noutput_items,
     break;
       
   case STATE_HAVE_HEADER:
+    bytes = demapper(&in[0], d_bytes_out);
+
     if (VERBOSE) {
       if(sig[0])
        printf("ERROR -- Found SYNC in HAVE_HEADER at %d, length of %d\n", d_packetlen_cnt, d_packetlen);
@@ -272,6 +400,6 @@ gr_ofdm_frame_sink::work (int noutput_items,
     assert(0);
     
   } // switch
-  
+
   return 1;
 }