diff options
Diffstat (limited to 'gr-digital/lib')
-rw-r--r-- | gr-digital/lib/CMakeLists.txt | 6 | ||||
-rw-r--r-- | gr-digital/lib/Makefile.am | 6 | ||||
-rw-r--r-- | gr-digital/lib/digital_ofdm_cyclic_prefixer.cc | 70 | ||||
-rw-r--r-- | gr-digital/lib/digital_ofdm_frame_acquisition.cc | 210 | ||||
-rw-r--r-- | gr-digital/lib/digital_ofdm_frame_sink.cc | 405 | ||||
-rw-r--r-- | gr-digital/lib/digital_ofdm_insert_preamble.cc | 187 | ||||
-rw-r--r-- | gr-digital/lib/digital_ofdm_mapper_bcv.cc | 241 | ||||
-rw-r--r-- | gr-digital/lib/digital_ofdm_sampler.cc | 133 |
8 files changed, 1258 insertions, 0 deletions
diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt index c100453c2..9417dc355 100644 --- a/gr-digital/lib/CMakeLists.txt +++ b/gr-digital/lib/CMakeLists.txt @@ -46,6 +46,12 @@ list(APPEND gr_digital_sources digital_lms_dd_equalizer_cc.cc digital_kurtotic_equalizer_cc.cc digital_mpsk_receiver_cc.cc + digital_ofdm_cyclic_prefixer.cc + digital_ofdm_frame_acquisition.cc + digital_ofdm_frame_sink.cc + digital_ofdm_insert_preamble.cc + digital_ofdm_mapper_bcv.cc + digital_ofdm_sampler.cc digital_gmskmod_bc.cc digital_cpmmod_bc.cc ) diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am index 17baf2101..2860974ca 100644 --- a/gr-digital/lib/Makefile.am +++ b/gr-digital/lib/Makefile.am @@ -41,6 +41,12 @@ libgnuradio_digital_la_SOURCES = \ digital_lms_dd_equalizer_cc.cc \ digital_kurtotic_equalizer_cc.cc \ digital_mpsk_receiver_cc.cc \ + digital_ofdm_cyclic_prefixer.cc \ + digital_ofdm_frame_acquisition.cc \ + digital_ofdm_frame_sink.cc \ + digital_ofdm_insert_preamble.cc \ + digital_ofdm_mapper_bcv.cc \ + digital_ofdm_sampler.cc \ digital_gmskmod_bc.cc \ digital_cpmmod_bc.cc diff --git a/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc b/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc new file mode 100644 index 000000000..192af2591 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006,2010,2011 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_cyclic_prefixer.h> +#include <gr_io_signature.h> + +digital_ofdm_cyclic_prefixer_sptr +digital_make_ofdm_cyclic_prefixer (size_t input_size, size_t output_size) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_cyclic_prefixer (input_size, + output_size)); +} + +digital_ofdm_cyclic_prefixer::digital_ofdm_cyclic_prefixer (size_t input_size, + size_t output_size) + : gr_sync_interpolator ("ofdm_cyclic_prefixer", + gr_make_io_signature (1, 1, input_size*sizeof(gr_complex)), + gr_make_io_signature (1, 1, sizeof(gr_complex)), + output_size), + d_input_size(input_size), + d_output_size(output_size) + +{ +} + +int +digital_ofdm_cyclic_prefixer::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex *in = (gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + size_t cp_size = d_output_size - d_input_size; + unsigned int i=0, j=0; + + j = cp_size; + for(i=0; i < d_input_size; i++,j++) { + out[j] = in[i]; + } + + j = d_input_size - cp_size; + for(i=0; i < cp_size; i++, j++) { + out[i] = in[j]; + } + + return d_output_size; +} diff --git a/gr-digital/lib/digital_ofdm_frame_acquisition.cc b/gr-digital/lib/digital_ofdm_frame_acquisition.cc new file mode 100644 index 000000000..93b58aeca --- /dev/null +++ b/gr-digital/lib/digital_ofdm_frame_acquisition.cc @@ -0,0 +1,210 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2008,2010,2011 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_frame_acquisition.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_math.h> +#include <cstdio> + +#define VERBOSE 0 +#define M_TWOPI (2*M_PI) +#define MAX_NUM_SYMBOLS 1000 + +digital_ofdm_frame_acquisition_sptr +digital_make_ofdm_frame_acquisition (unsigned int occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_frame_acquisition (occupied_carriers, fft_length, cplen, + known_symbol, max_fft_shift_len)); +} + +digital_ofdm_frame_acquisition::digital_ofdm_frame_acquisition (unsigned occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len) + : gr_block ("ofdm_frame_acquisition", + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*fft_length, sizeof(char)*fft_length), + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char))), + d_occupied_carriers(occupied_carriers), + d_fft_length(fft_length), + d_cplen(cplen), + d_freq_shift_len(max_fft_shift_len), + d_known_symbol(known_symbol), + d_coarse_freq(0), + d_phase_count(0) +{ + d_symbol_phase_diff.resize(d_fft_length); + d_known_phase_diff.resize(d_occupied_carriers); + d_hestimate.resize(d_occupied_carriers); + + unsigned int i = 0, j = 0; + + std::fill(d_known_phase_diff.begin(), d_known_phase_diff.end(), 0); + for(i = 0; i < d_known_symbol.size()-2; i+=2) { + d_known_phase_diff[i] = norm(d_known_symbol[i] - d_known_symbol[i+2]); + } + + d_phase_lut = new gr_complex[(2*d_freq_shift_len+1) * MAX_NUM_SYMBOLS]; + for(i = 0; i <= 2*d_freq_shift_len; i++) { + for(j = 0; j < MAX_NUM_SYMBOLS; j++) { + d_phase_lut[j + i*MAX_NUM_SYMBOLS] = gr_expj(-M_TWOPI*d_cplen/d_fft_length*(i-d_freq_shift_len)*j); + } + } +} + +digital_ofdm_frame_acquisition::~digital_ofdm_frame_acquisition(void) +{ + delete [] d_phase_lut; +} + +void +digital_ofdm_frame_acquisition::forecast (int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size (); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = 1; +} + +gr_complex +digital_ofdm_frame_acquisition::coarse_freq_comp(int freq_delta, int symbol_count) +{ + // return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count), + // sin(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count)); + + return gr_expj(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count); + + //return d_phase_lut[MAX_NUM_SYMBOLS * (d_freq_shift_len + freq_delta) + symbol_count]; +} + +void +digital_ofdm_frame_acquisition::correlate(const gr_complex *symbol, int zeros_on_left) +{ + unsigned int i,j; + + std::fill(d_symbol_phase_diff.begin(), d_symbol_phase_diff.end(), 0); + for(i = 0; i < d_fft_length-2; i++) { + d_symbol_phase_diff[i] = norm(symbol[i] - symbol[i+2]); + } + + // sweep through all possible/allowed frequency offsets and select the best + int index = 0; + float max = 0, sum=0; + for(i = zeros_on_left - d_freq_shift_len; i < zeros_on_left + d_freq_shift_len; i++) { + sum = 0; + for(j = 0; j < d_occupied_carriers; j++) { + sum += (d_known_phase_diff[j] * d_symbol_phase_diff[i+j]); + } + if(sum > max) { + max = sum; + index = i; + } + } + + // set the coarse frequency offset relative to the edge of the occupied tones + d_coarse_freq = index - zeros_on_left; +} + +void +digital_ofdm_frame_acquisition::calculate_equalizer(const gr_complex *symbol, int zeros_on_left) +{ + unsigned int i=0; + + // Set first tap of equalizer + d_hestimate[0] = d_known_symbol[0] / + (coarse_freq_comp(d_coarse_freq,1)*symbol[zeros_on_left+d_coarse_freq]); + + // set every even tap based on known symbol + // linearly interpolate between set carriers to set zero-filled carriers + // FIXME: is this the best way to set this? + for(i = 2; i < d_occupied_carriers; i+=2) { + d_hestimate[i] = d_known_symbol[i] / + (coarse_freq_comp(d_coarse_freq,1)*(symbol[i+zeros_on_left+d_coarse_freq])); + d_hestimate[i-1] = (d_hestimate[i] + d_hestimate[i-2]) / gr_complex(2.0, 0.0); + } + + // with even number of carriers; last equalizer tap is wrong + if(!(d_occupied_carriers & 1)) { + d_hestimate[d_occupied_carriers-1] = d_hestimate[d_occupied_carriers-2]; + } + + if(VERBOSE) { + fprintf(stderr, "Equalizer setting:\n"); + for(i = 0; i < d_occupied_carriers; i++) { + gr_complex sym = coarse_freq_comp(d_coarse_freq,1)*symbol[i+zeros_on_left+d_coarse_freq]; + gr_complex output = sym * d_hestimate[i]; + fprintf(stderr, "sym: %+.4f + j%+.4f ks: %+.4f + j%+.4f eq: %+.4f + j%+.4f ==> %+.4f + j%+.4f\n", + sym .real(), sym.imag(), + d_known_symbol[i].real(), d_known_symbol[i].imag(), + d_hestimate[i].real(), d_hestimate[i].imag(), + output.real(), output.imag()); + } + fprintf(stderr, "\n"); + } +} + +int +digital_ofdm_frame_acquisition::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *symbol = (const gr_complex *)input_items[0]; + const char *signal_in = (const char *)input_items[1]; + + gr_complex *out = (gr_complex *) output_items[0]; + char *signal_out = (char *) output_items[1]; + + int unoccupied_carriers = d_fft_length - d_occupied_carriers; + int zeros_on_left = (int)ceil(unoccupied_carriers/2.0); + + if(signal_in[0]) { + d_phase_count = 1; + correlate(symbol, zeros_on_left); + calculate_equalizer(symbol, zeros_on_left); + signal_out[0] = 1; + } + else { + signal_out[0] = 0; + } + + for(unsigned int i = 0; i < d_occupied_carriers; i++) { + out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count) + *symbol[i+zeros_on_left+d_coarse_freq]; + } + + d_phase_count++; + if(d_phase_count == MAX_NUM_SYMBOLS) { + d_phase_count = 1; + } + + consume_each(1); + return 1; +} diff --git a/gr-digital/lib/digital_ofdm_frame_sink.cc b/gr-digital/lib/digital_ofdm_frame_sink.cc new file mode 100644 index 000000000..f8fb1bbb1 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_frame_sink.cc @@ -0,0 +1,405 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2010,2011 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_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 + +inline void +digital_ofdm_frame_sink::enter_search() +{ + if (VERBOSE) + fprintf(stderr, "@ enter_search\n"); + + d_state = STATE_SYNC_SEARCH; + +} + +inline void +digital_ofdm_frame_sink::enter_have_sync() +{ + if (VERBOSE) + fprintf(stderr, "@ enter_have_sync\n"); + + d_state = STATE_HAVE_SYNC; + + // clear state of demapper + d_byte_offset = 0; + d_partial_byte = 0; + + 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 +digital_ofdm_frame_sink::enter_have_header() +{ + d_state = STATE_HAVE_HEADER; + + // header consists of two 16-bit shorts in network byte order + // payload length is lower 12 bits + // whitener offset is upper 4 bits + d_packetlen = (d_header >> 16) & 0x0fff; + d_packet_whitener_offset = (d_header >> 28) & 0x000f; + d_packetlen_cnt = 0; + + if (VERBOSE) + fprintf(stderr, "@ enter_have_header (payload_len = %d) (offset = %d)\n", + d_packetlen, d_packet_whitener_offset); +} + + +unsigned char digital_ofdm_frame_sink::slicer(const gr_complex x) +{ + 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 digital_ofdm_frame_sink::demapper(const gr_complex *in, + unsigned char *out) +{ + unsigned int i=0, bytes_produced=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); + + gr_complex closest_sym = d_sym_position[bits]; + + accum_error += sigrot * conj(closest_sym); + + // FIX THE FOLLOWING STATEMENT + if (norm(sigrot)> 0.001) d_dfe[i] += d_eq_gain*(closest_sym/sigrot-d_dfe[i]); + + i++; + + 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; +} + + +digital_ofdm_frame_sink_sptr +digital_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 gnuradio::get_initial_sptr(new digital_ofdm_frame_sink(sym_position, sym_value_out, + target_queue, occupied_carriers, + phase_gain, freq_gain)); +} + + +digital_ofdm_frame_sink::digital_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 (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_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) +{ + 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); + } + + // 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); + } + } + } + + // 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("digital_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(); +} + +digital_ofdm_frame_sink::~digital_ofdm_frame_sink () +{ + delete [] d_bytes_out; +} + +bool +digital_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(float(d_sym_value_out.size())) / log10(2.0)); + + return true; +} + + +int +digital_ofdm_frame_sink::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + 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"); + + switch(d_state) { + + case STATE_SYNC_SEARCH: // Look for flag indicating beginning of pkt + if (VERBOSE) + fprintf(stderr,"SYNC Search, noutput=%d\n", noutput_items); + + if (sig[0]) { // Found it, set up for header decode + enter_have_sync(); + } + 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"); + fprintf(stderr,"Header Search bitcnt=%d, header=0x%08x\n", + d_headerbytelen_cnt, d_header); + } + + j = 0; + while(j < bytes) { + d_header = (d_header << 8) | (d_bytes_out[j] & 0xFF); + j++; + + if (++d_headerbytelen_cnt == HEADERBYTELEN) { + + if (VERBOSE) + fprintf(stderr, "got header: 0x%08x\n", d_header); + + // we have a full header, check to see if it has been received properly + if (header_ok()){ + enter_have_header(); + + if (VERBOSE) + printf("\nPacket Length: %d\n", d_packetlen); + + while((j < bytes) && (d_packetlen_cnt < d_packetlen)) { + d_packet[d_packetlen_cnt++] = d_bytes_out[j++]; + } + + if(d_packetlen_cnt == d_packetlen) { + gr_message_sptr msg = + gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen); + memcpy(msg->msg(), d_packet, d_packetlen_cnt); + d_target_queue->insert_tail(msg); // send it + msg.reset(); // free it up + + enter_search(); + } + } + else { + enter_search(); // bad header + } + } + } + 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); + fprintf(stderr,"Packet Build\n"); + } + + j = 0; + while(j < bytes) { + d_packet[d_packetlen_cnt++] = d_bytes_out[j++]; + + if (d_packetlen_cnt == d_packetlen){ // packet is filled + // build a message + // NOTE: passing header field as arg1 is not scalable + gr_message_sptr msg = + gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen_cnt); + memcpy(msg->msg(), d_packet, d_packetlen_cnt); + + d_target_queue->insert_tail(msg); // send it + msg.reset(); // free it up + + enter_search(); + break; + } + } + break; + + default: + assert(0); + + } // switch + + return 1; +} diff --git a/gr-digital/lib/digital_ofdm_insert_preamble.cc b/gr-digital/lib/digital_ofdm_insert_preamble.cc new file mode 100644 index 000000000..a46133643 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_insert_preamble.cc @@ -0,0 +1,187 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2010,2011 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <digital_ofdm_insert_preamble.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <iostream> +#include <string.h> + +digital_ofdm_insert_preamble_sptr +digital_make_ofdm_insert_preamble(int fft_length, + const std::vector<std::vector<gr_complex> > &preamble) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_insert_preamble(fft_length, + preamble)); +} + +digital_ofdm_insert_preamble::digital_ofdm_insert_preamble + (int fft_length, + const std::vector<std::vector<gr_complex> > &preamble) + : gr_block("ofdm_insert_preamble", + gr_make_io_signature2(2, 2, + sizeof(gr_complex)*fft_length, + sizeof(char)), + gr_make_io_signature2(1, 2, + sizeof(gr_complex)*fft_length, + sizeof(char))), + d_fft_length(fft_length), + d_preamble(preamble), + d_state(ST_IDLE), + d_nsymbols_output(0), + d_pending_flag(0) +{ + // sanity check preamble symbols + for (size_t i = 0; i < d_preamble.size(); i++){ + if (d_preamble[i].size() != (size_t) d_fft_length) + throw std::invalid_argument("digital_ofdm_insert_preamble: invalid length for preamble symbol"); + } + + enter_idle(); +} + + +digital_ofdm_insert_preamble::~digital_ofdm_insert_preamble() +{ +} + +int +digital_ofdm_insert_preamble::general_work (int noutput_items, + gr_vector_int &ninput_items_v, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + int ninput_items = std::min(ninput_items_v[0], ninput_items_v[1]); + const gr_complex *in_sym = (const gr_complex *) input_items[0]; + const unsigned char *in_flag = (const unsigned char *) input_items[1]; + + gr_complex *out_sym = (gr_complex *) output_items[0]; + unsigned char *out_flag = 0; + if (output_items.size() == 2) + out_flag = (unsigned char *) output_items[1]; + + + int no = 0; // number items output + int ni = 0; // number items read from input + + +#define write_out_flag() \ + do { if (out_flag) \ + out_flag[no] = d_pending_flag; \ + d_pending_flag = 0; \ + } while(0) + + + while (no < noutput_items && ni < ninput_items){ + switch(d_state){ + case ST_IDLE: + if (in_flag[ni] & 0x1) // this is first symbol of new payload + enter_preamble(); + else + ni++; // eat one input symbol + break; + + case ST_PREAMBLE: + assert(in_flag[ni] & 0x1); + if (d_nsymbols_output >= (int) d_preamble.size()){ + // we've output all the preamble + enter_first_payload(); + } + else { + memcpy(&out_sym[no * d_fft_length], + &d_preamble[d_nsymbols_output][0], + d_fft_length*sizeof(gr_complex)); + + write_out_flag(); + no++; + d_nsymbols_output++; + } + break; + + case ST_FIRST_PAYLOAD: + // copy first payload symbol from input to output + memcpy(&out_sym[no * d_fft_length], + &in_sym[ni * d_fft_length], + d_fft_length * sizeof(gr_complex)); + + write_out_flag(); + no++; + ni++; + enter_payload(); + break; + + case ST_PAYLOAD: + if (in_flag[ni] & 0x1){ // this is first symbol of a new payload + enter_preamble(); + break; + } + + // copy a symbol from input to output + memcpy(&out_sym[no * d_fft_length], + &in_sym[ni * d_fft_length], + d_fft_length * sizeof(gr_complex)); + + write_out_flag(); + no++; + ni++; + break; + + default: + std::cerr << "digital_ofdm_insert_preamble: (can't happen) invalid state, resetting\n"; + enter_idle(); + } + } + + consume_each(ni); + return no; +} + +void +digital_ofdm_insert_preamble::enter_idle() +{ + d_state = ST_IDLE; + d_nsymbols_output = 0; + d_pending_flag = 0; +} + +void +digital_ofdm_insert_preamble::enter_preamble() +{ + d_state = ST_PREAMBLE; + d_nsymbols_output = 0; + d_pending_flag = 1; +} + +void +digital_ofdm_insert_preamble::enter_first_payload() +{ + d_state = ST_FIRST_PAYLOAD; +} + +void +digital_ofdm_insert_preamble::enter_payload() +{ + d_state = ST_PAYLOAD; +} diff --git a/gr-digital/lib/digital_ofdm_mapper_bcv.cc b/gr-digital/lib/digital_ofdm_mapper_bcv.cc new file mode 100644 index 000000000..cf3d08703 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_mapper_bcv.cc @@ -0,0 +1,241 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2008,2010,2011 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_mapper_bcv.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <string.h> + +digital_ofdm_mapper_bcv_sptr +digital_make_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned int msgq_limit, + unsigned int occupied_carriers, unsigned int fft_length) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_mapper_bcv (constellation, msgq_limit, + occupied_carriers, fft_length)); +} + +// Consumes 1 packet and produces as many OFDM symbols of fft_length to hold the full packet +digital_ofdm_mapper_bcv::digital_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned int msgq_limit, + unsigned int occupied_carriers, unsigned int fft_length) + : gr_sync_block ("ofdm_mapper_bcv", + gr_make_io_signature (0, 0, 0), + gr_make_io_signature2 (1, 2, sizeof(gr_complex)*fft_length, sizeof(char))), + d_constellation(constellation), + d_msgq(gr_make_msg_queue(msgq_limit)), d_msg_offset(0), d_eof(false), + d_occupied_carriers(occupied_carriers), + d_fft_length(fft_length), + d_bit_offset(0), + d_pending_flag(0), + d_resid(0), + d_nresid(0) +{ + if (!(d_occupied_carriers <= d_fft_length)) + throw std::invalid_argument("digital_ofdm_mapper_bcv: occupied carriers must be <= fft_length"); + + // this is not the final form of this solution since we still use the occupied_tones concept, + // which would get us into trouble if the number of carriers we seek is greater than the occupied carriers. + // Eventually, we will get rid of the occupied_carriers concept. + 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); + } + + // find out how many zeros to pad on the sides; the difference between the fft length and the subcarrier + // mapping size in chunks of four. This is the number to pack on the left and this number plus any + // residual nulls (if odd) will be packed on the right. + diff = (d_fft_length/4 - carriers.length())/2; + + unsigned int i,j,k; + for(i = 0; i < carriers.length(); i++) { + char c = carriers[i]; // get the current hex character from the string + for(j = 0; j < 4; j++) { // walk through all four bits + k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1; // convert to int and extract next bit + if(k) { // if bit is a 1, + d_subcarrier_map.push_back(4*(i+diff) + j); // use this subcarrier + } + } + } + + // 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("digital_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers"); + } + + d_nbits = (unsigned long)ceil(log10(float(d_constellation.size())) / log10(2.0)); +} + +digital_ofdm_mapper_bcv::~digital_ofdm_mapper_bcv(void) +{ +} + +int digital_ofdm_mapper_bcv::randsym() +{ + return (rand() % d_constellation.size()); +} + +int +digital_ofdm_mapper_bcv::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex *out = (gr_complex *)output_items[0]; + + unsigned int i=0; + + //printf("OFDM BPSK Mapper: ninput_items: %d noutput_items: %d\n", ninput_items[0], noutput_items); + + if(d_eof) { + return -1; + } + + if(!d_msg) { + d_msg = d_msgq->delete_head(); // block, waiting for a message + d_msg_offset = 0; + d_bit_offset = 0; + d_pending_flag = 1; // new packet, write start of packet flag + + if((d_msg->length() == 0) && (d_msg->type() == 1)) { + d_msg.reset(); + return -1; // We're done; no more messages coming. + } + } + + char *out_flag = 0; + if(output_items.size() == 2) + out_flag = (char *) output_items[1]; + + + // Build a single symbol: + // Initialize all bins to 0 to set unused carriers + memset(out, 0, d_fft_length*sizeof(gr_complex)); + + i = 0; + unsigned char bits = 0; + //while((d_msg_offset < d_msg->length()) && (i < d_occupied_carriers)) { + while((d_msg_offset < d_msg->length()) && (i < d_subcarrier_map.size())) { + + // need new data to process + if(d_bit_offset == 0) { + d_msgbytes = d_msg->msg()[d_msg_offset]; + //printf("mod message byte: %x\n", d_msgbytes); + } + + if(d_nresid > 0) { + // take the residual bits, fill out nbits with info from the new byte, and put them in the symbol + d_resid |= (((1 << d_nresid)-1) & d_msgbytes) << (d_nbits - d_nresid); + bits = d_resid; + + out[d_subcarrier_map[i]] = d_constellation[bits]; + i++; + + d_bit_offset += d_nresid; + d_nresid = 0; + d_resid = 0; + //printf("mod bit(r): %x resid: %x nresid: %d bit_offset: %d\n", + // bits, d_resid, d_nresid, d_bit_offset); + } + else { + if((8 - d_bit_offset) >= d_nbits) { // test to make sure we can fit nbits + // take the nbits number of bits at a time from the byte to add to the symbol + bits = ((1 << d_nbits)-1) & (d_msgbytes >> d_bit_offset); + d_bit_offset += d_nbits; + + out[d_subcarrier_map[i]] = d_constellation[bits]; + i++; + } + else { // if we can't fit nbits, store them for the next + // saves d_nresid bits of this message where d_nresid < d_nbits + unsigned int extra = 8-d_bit_offset; + d_resid = ((1 << extra)-1) & (d_msgbytes >> d_bit_offset); + d_bit_offset += extra; + d_nresid = d_nbits - extra; + } + + } + + if(d_bit_offset == 8) { + d_bit_offset = 0; + d_msg_offset++; + } + } + + // Ran out of data to put in symbol + if (d_msg_offset == d_msg->length()) { + if(d_nresid > 0) { + d_resid |= 0x00; + bits = d_resid; + d_nresid = 0; + d_resid = 0; + } + + //while(i < d_occupied_carriers) { // finish filling out the symbol + while(i < d_subcarrier_map.size()) { // finish filling out the symbol + out[d_subcarrier_map[i]] = d_constellation[randsym()]; + + i++; + } + + if (d_msg->type() == 1) // type == 1 sets EOF + d_eof = true; + d_msg.reset(); // finished packet, free message + assert(d_bit_offset == 0); + } + + if (out_flag) + out_flag[0] = d_pending_flag; + d_pending_flag = 0; + + return 1; // produced symbol +} diff --git a/gr-digital/lib/digital_ofdm_sampler.cc b/gr-digital/lib/digital_ofdm_sampler.cc new file mode 100644 index 000000000..cab8c2ba9 --- /dev/null +++ b/gr-digital/lib/digital_ofdm_sampler.cc @@ -0,0 +1,133 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2010,2011 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 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_ofdm_sampler.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <cstdio> + +digital_ofdm_sampler_sptr +digital_make_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout) +{ + return gnuradio::get_initial_sptr(new digital_ofdm_sampler (fft_length, symbol_length, timeout)); +} + +digital_ofdm_sampler::digital_ofdm_sampler (unsigned int fft_length, + unsigned int symbol_length, + unsigned int timeout) + : gr_block ("ofdm_sampler", + gr_make_io_signature2 (2, 2, sizeof (gr_complex), sizeof(char)), + gr_make_io_signature2 (2, 2, sizeof (gr_complex)*fft_length, sizeof(char)*fft_length)), + d_state(STATE_NO_SIG), d_timeout_max(timeout), d_fft_length(fft_length), d_symbol_length(symbol_length) +{ + set_relative_rate(1.0/(double) fft_length); // buffer allocator hint +} + +void +digital_ofdm_sampler::forecast (int noutput_items, gr_vector_int &ninput_items_required) +{ + // FIXME do we need more + //int nreqd = (noutput_items-1) * d_symbol_length + d_fft_length; + int nreqd = d_symbol_length + d_fft_length; + unsigned ninputs = ninput_items_required.size (); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = nreqd; +} + + +int +digital_ofdm_sampler::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *iptr = (const gr_complex *) input_items[0]; + const char *trigger = (const char *) input_items[1]; + + gr_complex *optr = (gr_complex *) output_items[0]; + char *outsig = (char *) output_items[1]; + + //FIXME: we only process a single OFDM symbol at a time; after the preamble, we can + // process a few at a time as long as we always look out for the next preamble. + + unsigned int index=d_fft_length; // start one fft length into the input so we can always look back this far + + outsig[0] = 0; // set output to no signal by default + + // Search for a preamble trigger signal during the next symbol length + while((d_state != STATE_PREAMBLE) && (index <= (d_symbol_length+d_fft_length))) { + if(trigger[index]) { + outsig[0] = 1; // tell the next block there is a preamble coming + d_state = STATE_PREAMBLE; + } + else + index++; + } + + unsigned int i, pos, ret; + switch(d_state) { + case(STATE_PREAMBLE): + // When we found a preamble trigger, get it and set the symbol boundary here + for(i = (index - d_fft_length + 1); i <= index; i++) { + *optr++ = iptr[i]; + } + + d_timeout = d_timeout_max; // tell the system to expect at least this many symbols for a frame + d_state = STATE_FRAME; + consume_each(index - d_fft_length + 1); // consume up to one fft_length away to keep the history + ret = 1; + break; + + case(STATE_FRAME): + // use this state when we have processed a preamble and are getting the rest of the frames + //FIXME: we could also have a power squelch system here to enter STATE_NO_SIG if no power is received + + // skip over fft length history and cyclic prefix + pos = d_symbol_length; // keeps track of where we are in the input buffer + while(pos < d_symbol_length + d_fft_length) { + *optr++ = iptr[pos++]; + } + + if(d_timeout-- == 0) { + printf("TIMEOUT\n"); + d_state = STATE_NO_SIG; + } + + consume_each(d_symbol_length); // jump up by 1 fft length and the cyclic prefix length + ret = 1; + break; + + case(STATE_NO_SIG): + default: + consume_each(index-d_fft_length); // consume everything we've gone through so far leaving the fft length history + ret = 0; + break; + } + + return ret; +} |