From 8b1c1791c7cc3e0825d4399ea7f0696ea68a1214 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Sat, 11 Jun 2011 21:32:28 -0700 Subject: [PATCH] Everything changed. * Rewrote preamble detector. Uses integrate-and-dump along with correlator to perform optimal receive. * No longer using framer. Determining packet length via header. Missing some anti-FRUIT stuff. * Pulled out the slicer logic into its own function. Lots of todos and fixmes. --- src/lib/air_modes_framer.cc | 20 ++-- src/lib/air_modes_preamble.cc | 175 +++++++++++++++++----------------- src/lib/air_modes_preamble.h | 7 +- src/lib/air_modes_slicer.cc | 136 ++++++++++++++------------ src/python/uhd_modes.py | 31 ++---- 5 files changed, 185 insertions(+), 184 deletions(-) diff --git a/src/lib/air_modes_framer.cc b/src/lib/air_modes_framer.cc index bbf87a9..29da25a 100644 --- a/src/lib/air_modes_framer.cc +++ b/src/lib/air_modes_framer.cc @@ -60,8 +60,8 @@ int air_modes_framer::work(int noutput_items, gr_vector_void_star &output_items) { //do things! - const float *inraw = (const float *) input_items[0]; - float *outraw = (float *) output_items[0]; + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; //unsigned char *outattrib = (unsigned char *) output_items[0]; int size = noutput_items - d_check_width*2; if(size < 0) return 0; @@ -73,7 +73,7 @@ int air_modes_framer::work(int noutput_items, get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("preamble_found")); std::vector::iterator tag_iter; - memcpy(outraw, inraw, size * sizeof(float)); + memcpy(out, in, size * sizeof(float)); for(tag_iter = tags.begin(); tag_iter != tags.end(); tag_iter++) { uint64_t i = gr_tags::get_nitems(*tag_iter) - abs_sample_cnt; @@ -81,18 +81,18 @@ int air_modes_framer::work(int noutput_items, packet_attrib = Long_Packet; //let's use the preamble marker to get a reference level for the packet - reference_level = (inraw[i] - + inraw[i+int(1.0*d_samples_per_symbol)] - + inraw[i+int(3.5*d_samples_per_symbol)] - + inraw[i+int(4.5*d_samples_per_symbol)]) / 4; + reference_level = (in[i] + + in[i+2] + + in[i+7] + + in[i+9]) / 4.0; //armed with our reference level, let's look for marks within 3dB of the reference level in bits 57-62 (65-70, see above) //if bits 57-62 have marks in either chip, we've got a long packet //otherwise we have a short packet //NOTE: you can change the default here to be short packet, and then check for a long packet. don't know which way is better. for(int j = (65 * d_samples_per_symbol); j < (70 * d_samples_per_symbol); j += d_samples_per_symbol) { - float t_max = std::max(inraw[i+j], - inraw[i+j+d_samples_per_chip] + float t_max = std::max(in[i+j], + in[i+j+d_samples_per_chip] ); if(t_max < (reference_level / 2.0)) packet_attrib = Short_Packet; } @@ -114,7 +114,7 @@ int air_modes_framer::work(int noutput_items, //insert tag here add_item_tag(0, //stream ID nitems_written(0)+i, //sample - d_key, //preamble_found + d_key, //frame_info pmt::pmt_from_long((long)packet_attrib), d_me //block src id ); diff --git a/src/lib/air_modes_preamble.cc b/src/lib/air_modes_preamble.cc index f36338b..e9e95fa 100644 --- a/src/lib/air_modes_preamble.cc +++ b/src/lib/air_modes_preamble.cc @@ -35,17 +35,19 @@ air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshol } air_modes_preamble::air_modes_preamble(int channel_rate, float threshold_db) : - gr_sync_block ("modes_preamble", + gr_block ("modes_preamble", gr_make_io_signature2 (2, 2, sizeof(float), sizeof(float)), //stream 0 is received data, stream 1 is moving average for reference - gr_make_io_signature (1, 1, sizeof(float))) //the original data. we pass it out in order to use tags. + gr_make_io_signature (1, 1, sizeof(float))) //the output packets { d_chip_rate = 2000000; //2Mchips per second d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work d_samples_per_symbol = d_samples_per_chip * 2; - d_check_width = 7.5 * d_samples_per_symbol; //only search to this far from the end of the stream buffer + d_check_width = 120 * d_samples_per_symbol; //only search to this far from the end of the stream buffer d_threshold_db = threshold_db; d_threshold = powf(10., threshold_db/10.); //the level that the sample must be above the moving average in order to qualify as a pulse + set_output_multiple(1+d_check_width*2); + std::stringstream str; str << name() << unique_id(); d_me = pmt::pmt_string_to_symbol(str.str()); @@ -59,111 +61,110 @@ static int early_late(const float *data) { else return 0; } -int air_modes_preamble::work(int noutput_items, +static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) { + for(int i=0; i pulse_threshold) { //if the sample is greater than the reference level by the specified amount - int gate_sum = early_late(&in[i]); - if(gate_sum != 0) continue; //if either the early gate or the late gate had greater energy, keep moving. - //the packets are so short we choose not to do any sort of closed-loop synchronization after this simple gating. - //if we get a good center sample, the drift should be negligible. - preamble_pulses[0] = in[i+pulse_offsets[0]]; - preamble_pulses[1] = in[i+pulse_offsets[1]]; - preamble_pulses[2] = in[i+pulse_offsets[2]]; - preamble_pulses[3] = in[i+pulse_offsets[3]]; - - //search for the rest of the pulses at their expected positions - if( preamble_pulses[1] < pulse_threshold ) continue; - if( preamble_pulses[2] < pulse_threshold ) continue; - if( preamble_pulses[3] < pulse_threshold ) continue; - - valid_preamble = true; //this gets falsified by the following statements to disqualify a preamble - - float avgpeak = (preamble_pulses[0] + preamble_pulses[1] + preamble_pulses[2] + preamble_pulses[3]) / 4; - - //set the threshold requirement for spaces (0 chips) to - //threshold dB below the current peak - float space_threshold = preamble_pulses[0] / d_threshold; - //search between pulses and all the way out to 8.0us to make - //sure there are no pulses inside the "0" chips. make sure - //all the samples are <= (in[peak] * d_threshold). - //so 0.5us has to be < space_threshold, as does (1.5-3), 4, (5-7.5) in order to qualify. - for(int j = 1.5 * d_samples_per_symbol; j <= 3 * d_samples_per_symbol; j+=d_samples_per_chip) - 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+=d_samples_per_chip) - if(in[i+j] > space_threshold) valid_preamble = false; - - //make sure all four peaks are within 3dB of each other - float minpeak = avgpeak * 0.5;//-3db, was 0.631; //-2db - float maxpeak = avgpeak * 2.0;//3db, was 1.585; //2db - - if(preamble_pulses[0] < minpeak || preamble_pulses[0] > maxpeak) continue; - if(preamble_pulses[1] < minpeak || preamble_pulses[1] > maxpeak) continue; - if(preamble_pulses[2] < minpeak || preamble_pulses[2] > maxpeak) continue; - if(preamble_pulses[3] < minpeak || preamble_pulses[3] > maxpeak) continue; - } - - if(valid_preamble) { - //get a more accurate chip center by finding the energy peak across all four preamble peaks - //there's some weirdness in the early part, so i ripped it out. - bool early, late; + 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 = false; do { - early = late = false; - //gate_sum_early= in[i+pulse_offsets[0]-1] - // + in[i+pulse_offsets[1]-1] - // + in[i+pulse_offsets[2]-1] - // + in[i+pulse_offsets[3]-1]; - - gate_sum_now = in[i+pulse_offsets[0]] - + in[i+pulse_offsets[1]] - + in[i+pulse_offsets[2]] - + in[i+pulse_offsets[3]]; - - gate_sum_late = in[i+pulse_offsets[0]+1] - + in[i+pulse_offsets[1]+1] - + in[i+pulse_offsets[2]+1] - + in[i+pulse_offsets[3]+1]; - - early = (gate_sum_early > gate_sum_now); - late = (gate_sum_late > gate_sum_now); + double now_corr = correlate_preamble(in+i, d_samples_per_chip); + double late_corr = correlate_preamble(in+i+1, d_samples_per_chip); + late = (late_corr > now_corr); if(late) i++; - //else if(early) i--; - //if(early && late) early = late = false; } while(late); - //finally after all this, let's post the preamble! + //now check to see that the rest of the chips 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(i); + return 0; + } + + //all right i'm prepared to call this a preamble + //let's integrate and dump the output + i -= d_samples_per_chip-1; + integrate_and_dump(out, &in[i], 240, d_samples_per_chip); +// out[0] = 1.0; //for debug +// out[1] = out[2] = out[3] = out[4] = avgpeak; +// out[5] = out[6] = out[7] = out[8] = space_threshold; +// out[9] = 0.0; + + //now tag the preamble add_item_tag(0, //stream ID - nitems_written(0)+i, //sample - d_key, //preamble_found - pmt::PMT_T, //meaningless for us - d_me //block src id - ); + nitems_written(0), //sample + d_key, //frame_info + pmt::pmt_from_double((double) space_threshold), + d_me //block src id + ); + + //produce only one output per work call + consume_each(i+240*d_samples_per_chip); + return 240; //fixme debug should be 240 } } - return size; + + //didn't get anything this time + consume_each(ninputs); + return 0; } diff --git a/src/lib/air_modes_preamble.h b/src/lib/air_modes_preamble.h index a537cc2..31780d6 100644 --- a/src/lib/air_modes_preamble.h +++ b/src/lib/air_modes_preamble.h @@ -23,7 +23,7 @@ #ifndef INCLUDED_AIR_MODES_PREAMBLE_H #define INCLUDED_AIR_MODES_PREAMBLE_H -#include +#include class air_modes_preamble; typedef boost::shared_ptr air_modes_preamble_sptr; @@ -34,7 +34,7 @@ air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshol * \brief mode select preamble detection * \ingroup block */ -class air_modes_preamble : public gr_sync_block +class air_modes_preamble : public gr_block { private: friend air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db); @@ -50,7 +50,8 @@ private: pmt::pmt_t d_me, d_key; public: - int work (int noutput_items, + int general_work (int noutput_items, + gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); }; diff --git a/src/lib/air_modes_slicer.cc b/src/lib/air_modes_slicer.cc index acdee8f..da9d95c 100644 --- a/src/lib/air_modes_slicer.cc +++ b/src/lib/air_modes_slicer.cc @@ -31,6 +31,7 @@ #include #include #include +#include extern "C" { @@ -50,7 +51,7 @@ air_modes_slicer::air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue) : { //initialize private data here d_chip_rate = 2000000; //2Mchips per second - d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work + d_samples_per_chip = 2;//FIXME this is constant now channel_rate / d_chip_rate; d_samples_per_symbol = d_samples_per_chip * 2; d_check_width = 120 * d_samples_per_symbol; //how far you will have to look ahead d_queue = queue; @@ -66,6 +67,44 @@ static bool pmtcompare(pmt::pmt_t x, pmt::pmt_t y) t_y = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(y, 0)); return t_x < t_y; } + +//this slicer is courtesy of Lincoln Labs. supposedly it is more resistant to mode A/C FRUIT. +//see http://adsb.tc.faa.gov/WG3_Meetings/Meeting8/Squitter-Lon.pdf +static bool slicer(const float bit0, const float bit1, const float ref) { + bool slice, confidence; + + //3dB limits for bit slicing and confidence measurement + float highlimit=ref*2; + float lowlimit=ref*0.5; + + bool firstchip_inref = ((bit0 > lowlimit) && (bit0 < highlimit)); + bool secondchip_inref = ((bit1 > lowlimit) && (bit1 < highlimit)); + + if(firstchip_inref && !secondchip_inref) { + slice = 1; + confidence = 1; + } + else if(secondchip_inref && !firstchip_inref) { + slice = 0; + confidence = 1; + } + else if(firstchip_inref && secondchip_inref) { + slice = bit0 > bit1; + confidence = 0; + } + else if(!firstchip_inref && !secondchip_inref) { //in this case, we determine the bit by whichever is larger, and we determine high confidence if the low chip is 6dB below reference. + slice = bit0 > bit1; + if(slice) { + if(bit1 < lowlimit * 0.5) confidence = 1; + else confidence = 0; + } else { + if(bit0 < lowlimit * 0.5) confidence = 1; + else confidence = 0; + } + } + return slice; +} + /* static double pmt_to_timestamp(pmt::pmt_t tstamp, uint64_t sample_cnt, double secs_per_sample) { double frac; @@ -85,92 +124,63 @@ int air_modes_slicer::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - //do things! - const float *inraw = (const float *) input_items[0]; + const float *in = (const float *) input_items[0]; int size = noutput_items - d_check_width; //since it's a sync block, i assume that it runs with ninput_items = noutput_items int i; std::vector tags; uint64_t abs_sample_cnt = nitems_read(0); - get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("frame_info")); + get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("preamble_found")); std::vector::iterator tag_iter; for(tag_iter = tags.begin(); tag_iter != tags.end(); tag_iter++) { uint64_t i = gr_tags::get_nitems(*tag_iter) - abs_sample_cnt; modes_packet rx_packet; - framer_packet_type packet_type = framer_packet_type(pmt::pmt_to_long(gr_tags::get_value(*tag_iter))); - int packet_length; - packet_length = packet_type == framer_packet_type(Short_Packet) ? 56 : 112; - - rx_packet.type = packet_type; memset(&rx_packet.data, 0x00, 14 * sizeof(unsigned char)); memset(&rx_packet.lowconfbits, 0x00, 24 * sizeof(unsigned char)); rx_packet.numlowconf = 0; - //let's use the preamble marker to get a reference level for the packet - rx_packet.reference_level = (inraw[i] - + inraw[i+int(1.0*d_samples_per_symbol)] - + inraw[i+int(3.5*d_samples_per_symbol)] - + inraw[i+int(4.5*d_samples_per_symbol)]) / 4; + //let's use the preamble to get a reference level for the packet + //fixme: a better thing to do is create a bi-level avg 1 and avg 0 + //through simple statistics, then take the median for your slice level + //this won't improve decoding but will improve confidence + rx_packet.reference_level = (in[i] + + in[i+2] + + in[i+7] + + in[i+9]) / 4.0; + + i += 16; //move on up to the first bit of the packet data + //now let's slice the header so we can determine if it's a short pkt or a long pkt + unsigned char pkt_hdr = 0; + for(int j=0; j < 5; j++) { + bool slice = slicer(in[i+j*2], in[i+j*2+1], rx_packet.reference_level); + if(slice) pkt_hdr += 1 << (4-j); + } + //std::cout << "SLICER: TYPE " << int(pkt_hdr) << std::endl; - i += 8 * d_samples_per_symbol; //move to the center of the first bit of the data + if(pkt_hdr == 17) rx_packet.type = Long_Packet; + else rx_packet.type = Short_Packet; + + int packet_length; + packet_length = (rx_packet.type == framer_packet_type(Short_Packet)) ? 56 : 112; - //here we calculate the total energy contained in each chip of the symbol + //it's slice time! + //TODO: don't repeat your work here, you already have the first 5 bits for(int j = 0; j < packet_length; j++) { - int firstchip = i+j*d_samples_per_symbol; - int secondchip = firstchip + d_samples_per_chip; - bool slice, confidence; - float firstchip_energy=0, secondchip_energy=0; - - firstchip_energy = inraw[firstchip]; - secondchip_energy = inraw[secondchip]; - - //3dB limits for bit slicing and confidence measurement - float highlimit=rx_packet.reference_level*2; - float lowlimit=rx_packet.reference_level*0.5; - bool firstchip_inref = ((firstchip_energy > lowlimit) && (firstchip_energy < highlimit)); - bool secondchip_inref = ((secondchip_energy > lowlimit) && (secondchip_energy < highlimit)); - - //these two lines for a super simple naive slicer. -// slice = firstchip_energy > secondchip_energy; -// confidence = bool(int(firstchip_inref) + int(secondchip_inref)); //one and only one chip in the reference zone - - //below is the Lincoln Labs slicer. it may produce greater bit errors. supposedly it is more resistant to mode A/C FRUIT. - //see http://adsb.tc.faa.gov/WG3_Meetings/Meeting8/Squitter-Lon.pdf - if(firstchip_inref && !secondchip_inref) { - slice = 1; - confidence = 1; - } - else if(secondchip_inref && !firstchip_inref) { - slice = 0; - confidence = 1; - } - else if(firstchip_inref && secondchip_inref) { - slice = firstchip_energy > secondchip_energy; - confidence = 0; - } - else if(!firstchip_inref && !secondchip_inref) { //in this case, we determine the bit by whichever is larger, and we determine high confidence if the low chip is 6dB below reference. - slice = firstchip_energy > secondchip_energy; - if(slice) { - if(secondchip_energy < lowlimit * 0.5) confidence = 1; - else confidence = 0; - } else { - if(firstchip_energy < lowlimit * 0.5) confidence = 1; - else confidence = 0; - } - } - //put the data into the packet + bool slice = slicer(in[i+j*2], in[i+j*2+1], rx_packet.reference_level); + + //put the data into the packet if(slice) { rx_packet.data[j/8] += 1 << (7-(j%8)); } //put the confidence decision into the packet - if(confidence) { +// if(confidence) { //rx_packet.confidence[j/8] += 1 << (7-(j%8)); - } else { - if(rx_packet.numlowconf < 24) rx_packet.lowconfbits[rx_packet.numlowconf++] = j; - } +// } else { +// if(rx_packet.numlowconf < 24) rx_packet.lowconfbits[rx_packet.numlowconf++] = j; +// } } /******************** BEGIN TIMESTAMP BS ******************/ diff --git a/src/python/uhd_modes.py b/src/python/uhd_modes.py index 4bbe2ad..c8458b4 100755 --- a/src/python/uhd_modes.py +++ b/src/python/uhd_modes.py @@ -19,9 +19,9 @@ # Boston, MA 02110-1301, USA. # -#my_position = [37.76225, -122.44254] +my_position = [37.76225, -122.44254] #my_position = [37.409066,-122.077836] -my_position = None +#my_position = None from gnuradio import gr, gru, optfir, eng_notation, blks2, air from gnuradio import uhd @@ -92,32 +92,21 @@ class adsb_rx_block (gr.top_block): #the DBSRX especially tends to be spur-prone; the LPF keeps out the #spur multiple that shows up at 2MHz -# self.filtcoeffs = gr.firdes.low_pass(1, rate, 1.8e6, 200e3) -# self.filter = gr.fir_filter_fff(1, self.filtcoeffs) - - #this is an integrate-and-dump filter to act as a matched filter - #for the essentially rectangular Mode S pulses. - #if a particular Mode S transponder is using a pulse shaping filter, - #this will not be optimal. - self.filtcoeffs = list() - for i in range(int(rate/4e6)): - self.filtcoeffs.append(1.0 / (rate/4e6)) - - #i think downstream blocks can therefore process at 2Msps -- try this - #self.filter = gr.fir_filter_fff(int(rate/2e6), self.filtcoeffs) + #self.lpfiltcoeffs = gr.firdes.low_pass(1, rate, 1.8e6, 200e3) + #self.lpfilter = gr.fir_filter_fff(1, self.lpfiltcoeffs) + self.filter = gr.fir_filter_fff(1, self.filtcoeffs) #rate = int(2e6) self.preamble = air.modes_preamble(rate, options.threshold) - self.framer = air.modes_framer(rate) + #self.framer = air.modes_framer(rate) self.slicer = air.modes_slicer(rate, queue) - self.connect(self.u, self.demod, self.filter) - self.connect(self.filter, self.avg) - self.connect(self.filter, (self.preamble, 0)) + self.connect(self.u, self.demod) + self.connect(self.demod, self.avg) + self.connect(self.demod, (self.preamble, 0)) self.connect(self.avg, (self.preamble, 1)) - self.connect((self.preamble, 0), (self.framer, 0)) - self.connect(self.framer, self.slicer) + self.connect((self.preamble, 0), (self.slicer, 0)) def tune(self, freq): result = self.u.set_center_freq(freq, 0)