/* # Copyright 2010 Nick Foster # Copyright 2013 Nicholas Corgan # # This file is part of gr-air-modes # # gr-air-modes 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. # # gr-air-modes 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 gr-air-modes; 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 #include "preamble_impl.h" #include #include #include #include namespace gr { air_modes::preamble::sptr air_modes::preamble::make(float channel_rate, float threshold_db) { return gnuradio::get_initial_sptr(new air_modes::preamble_impl(channel_rate, threshold_db)); } air_modes::preamble_impl::preamble_impl(float channel_rate, float threshold_db) : gr::block ("preamble", gr::io_signature::make2 (2, 2, sizeof(float), sizeof(float)), //stream 0 is received data, stream 1 is moving average for reference gr::io_signature::make (1, 1, sizeof(float))) //the output soft symbols { d_chip_rate = 2000000; //2Mchips per second set_rate(channel_rate); set_threshold(threshold_db); std::stringstream str; str << name() << unique_id(); d_me = pmt::string_to_symbol(str.str()); d_key = pmt::string_to_symbol("preamble_found"); } void air_modes::preamble_impl::set_rate(float channel_rate) { d_samples_per_chip = channel_rate / d_chip_rate; d_samples_per_symbol = d_samples_per_chip * 2; d_check_width = 120 * d_samples_per_symbol; d_sample_rate = channel_rate; set_output_multiple(1+d_check_width*2); set_history(d_samples_per_symbol); } void air_modes::preamble_impl::set_threshold(float threshold_db) { d_threshold_db = threshold_db; d_threshold = powf(10., threshold_db/20.); //the level that the sample must be above the moving average in order to qualify as a pulse } float air_modes::preamble_impl::get_threshold(void) { return d_threshold_db; } float air_modes::preamble_impl::get_rate(void) { return d_sample_rate; } static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) { for(int i=0; i 1.0f) { abs_frac -= 1.0f; abs_whole += 1; } tstime = pmt::make_tuple(pmt::from_uint64(abs_whole), pmt::from_double(abs_frac)); return tstime; } int air_modes::preamble_impl::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { const float *in = (const float *) input_items[0]; const float *inavg = (const float *) input_items[1]; int mininputs = std::min(ninput_items[0], ninput_items[1]); //they should be matched but let's be safe //round number of input samples down to nearest d_samples_per_chip //we also subtract off d_samples_per_chip to allow the bit center finder some leeway const int ninputs = std::max(mininputs - (mininputs % int(d_samples_per_chip)) - int(d_samples_per_chip), 0); if (ninputs <= 0) { consume_each(0); return 0; } float *out = (float *) output_items[0]; if(0) std::cout << "Preamble called with " << ninputs << " samples" << std::endl; //fixme move into .h const int pulse_offsets[4] = { 0, int(2 * d_samples_per_chip), int(7 * d_samples_per_chip), int(9 * d_samples_per_chip) }; uint64_t abs_sample_cnt = nitems_read(0); std::vector tstamp_tags; get_tags_in_range(tstamp_tags, 0, abs_sample_cnt, abs_sample_cnt + ninputs, pmt::string_to_symbol("rx_time")); //tags.back() is the most recent timestamp, then. if(tstamp_tags.size() > 0) { d_timestamp = tstamp_tags.back(); } for(int i=0; i < ninputs; i++) { float pulse_threshold = inavg[i] * d_threshold; if(in[i] > pulse_threshold) { //hey we got a candidate if(in[i+1] > in[i]) continue; //wait for the peak //check to see the rest of the pulses are there if( in[i+pulse_offsets[1]] < pulse_threshold ) continue; if( in[i+pulse_offsets[2]] < pulse_threshold ) continue; if( in[i+pulse_offsets[3]] < pulse_threshold ) continue; //get a more accurate bit center by finding the correlation peak across all four preamble bits bool late, early; int how_late = 0; do { double now_corr = correlate_preamble(in+i, d_samples_per_chip); double late_corr = correlate_preamble(in+i+1, d_samples_per_chip); double early_corr = correlate_preamble(in+i-1, d_samples_per_chip); late = (late_corr > now_corr); //early = (early_corr > now_corr); if(late) { i++; how_late++; } //if(early && i>0) { std::cout << "EARLY " << i << std::endl; i--; } } while(late and how_late < d_samples_per_chip);// xor early); if(0) std::cout << "We were " << how_late << " samples late" << std::endl; //now check to see that the non-peak symbols in the preamble //are below the peaks by threshold dB float avgpeak = ( in[i+pulse_offsets[0]] + in[i+pulse_offsets[1]] + in[i+pulse_offsets[2]] + in[i+pulse_offsets[3]]) / 4.0; float space_threshold = inavg[i] + (avgpeak - inavg[i])/d_threshold; bool valid_preamble = true; //f'in c++ for( int j=1.5*d_samples_per_symbol; j<=3*d_samples_per_symbol; j++) if(in[i+j] > space_threshold) valid_preamble = false; for( int j=5*d_samples_per_symbol; j<=7.5*d_samples_per_symbol; j++) if(in[i+j] > space_threshold) valid_preamble = false; if(!valid_preamble) continue; //be sure we've got enough room in the input buffer to copy out a whole packet if(ninputs-i < 240*d_samples_per_chip) { consume_each(std::max(i-1,0)); if(0) std::cout << "Preamble consumed " << std::max(i-1,0) << ", returned 0 (no room)" << std::endl; return 0; } //all right i'm prepared to call this a preamble for(int j=0; j<240; j++) { out[j] = in[i+int(j*d_samples_per_chip)] - inavg[i]; } //get the timestamp of the preamble pmt::pmt_t tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_sample_rate); //now tag the preamble add_item_tag(0, //stream ID nitems_written(0), //sample d_key, //frame_info tstamp, d_me //block src id ); //produce only one output per work call -- TODO this should probably change if(0) std::cout << "Preamble consumed " << i+240*d_samples_per_chip << "with i=" << i << ", returned 240" << std::endl; consume_each(i+240*d_samples_per_chip); return 240; } } //didn't get anything this time if(0) std::cout << "Preamble consumed " << ninputs << ", returned 0" << std::endl; consume_each(ninputs); return 0; } } //namespace gr