diff --git a/src/lib/air_modes_framer.cc b/src/lib/air_modes_framer.cc index 2b59058..565ceea 100644 --- a/src/lib/air_modes_framer.cc +++ b/src/lib/air_modes_framer.cc @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include air_modes_framer_sptr air_make_modes_framer(int channel_rate) { @@ -36,16 +39,16 @@ air_modes_framer_sptr air_make_modes_framer(int channel_rate) air_modes_framer::air_modes_framer(int channel_rate) : gr_sync_block ("modes_framer", - gr_make_io_signature2 (2, 2, sizeof(float), sizeof(unsigned char)), //stream 0 is received data, stream 1 is binary preamble detector output + gr_make_io_signature (1, 1, sizeof(float)), //stream 0 is received data gr_make_io_signature (1, 1, sizeof(unsigned char))) //output is [0, 1, 2]: [no frame, short frame, long frame] { //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_symbol = d_samples_per_chip * 2; - d_check_width = 120 * d_samples_per_symbol; //gotta be able to look at two long frame lengths at a time in the event that FRUIT occurs near the end of the first frame - - set_output_multiple(1+d_check_width*2); + d_check_width = 120 * d_samples_per_symbol; //gotta be able to look at two long frame lengths at a time + //in the event that FRUIT occurs near the end of the first frame + //set_history(d_check_width*2); } int air_modes_framer::work(int noutput_items, @@ -54,60 +57,56 @@ int air_modes_framer::work(int noutput_items, { //do things! const float *inraw = (const float *) input_items[0]; - const unsigned char *inattrib = (const unsigned char *) input_items[1]; - //float *outraw = (float *) output_items[0]; unsigned char *outattrib = (unsigned char *) output_items[0]; - - int size = noutput_items - d_check_width; //need to be able to look ahead a full frame - - float reference_level = 0; + int size = noutput_items - d_check_width*2; + float reference_level; framer_packet_type packet_attrib; - - for(int i = 0; i < size; i++) { - - packet_attrib = No_Packet; - - if(!inattrib[i]) { - outattrib[i] = packet_attrib; - continue; //if there's no preamble marker, forget it, move on - } - + 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("preamble_found")); + std::vector::iterator tag_iter; + + memset(outattrib, 0x00, size * sizeof(unsigned char)); + + for(tag_iter = tags.begin(); tag_iter != tags.end(); tag_iter++) { + uint64_t i = gr_tags::get_nitems(*tag_iter) - abs_sample_cnt; //first, assume we have a long packet packet_attrib = Long_Packet; //let's use the preamble marker to get a reference level for the packet reference_level = (bit_energy(&inraw[i], d_samples_per_chip) - + bit_energy(&inraw[i+int(1.0*d_samples_per_symbol)], d_samples_per_chip) - + bit_energy(&inraw[i+int(3.5*d_samples_per_symbol)], d_samples_per_chip) - + bit_energy(&inraw[i+int(4.5*d_samples_per_symbol)], d_samples_per_chip)) / 4; - + + bit_energy(&inraw[i+int(1.0*d_samples_per_symbol)], d_samples_per_chip) + + bit_energy(&inraw[i+int(3.5*d_samples_per_symbol)], d_samples_per_chip) + + bit_energy(&inraw[i+int(4.5*d_samples_per_symbol)], d_samples_per_chip)) / 4; + //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 = (bit_energy(&inraw[i+j], d_samples_per_chip) > bit_energy(&inraw[i+j+d_samples_per_chip], d_samples_per_chip)) ? bit_energy(&inraw[i+j], d_samples_per_chip) : bit_energy(&inraw[i+j+d_samples_per_chip], d_samples_per_chip); - if(t_max < (reference_level / 2)) packet_attrib = Short_Packet; + float t_max = std::max(bit_energy(&inraw[i+j], d_samples_per_chip), + bit_energy(&inraw[i+j+d_samples_per_chip], d_samples_per_chip) + ); + if(t_max < (reference_level / 2.0)) packet_attrib = Short_Packet; } - - //BUT: we must also loop through the entire packet to make sure it is clear of additional preamble markers! if it has another preamble marker, it's been FRUITed, and we must only + + //BUT: we must also loop through the entire packet to make sure it is clear of additional preamble markers! + //if it has another preamble marker, it's been FRUITed, and we must only //mark the new packet (i.e., just continue). int lookahead; - if(packet_attrib == Long_Packet) lookahead = 112; - else lookahead = 56; - - for(int j = i+1; j < i+(lookahead * d_samples_per_symbol); j++) { - if(inattrib[j]) packet_attrib = Fruited_Packet; //FRUITed by mode S! in this case, we drop this first packet - //if(inraw[j] > (reference_level * 2)) packet_attrib = Fruited_Packet; //catches strong Mode A/C fruit inside the packet - //but good error correction should cope with that - } + if(packet_attrib == Long_Packet) lookahead = 112 * d_samples_per_symbol; + else lookahead = 56 * d_samples_per_symbol; + //ok we have to re-do lookahead for this tagged version. + //we can do this by looking for tags that fall within that window + std::vector fruit_tags; + get_tags_in_range(fruit_tags, 0, abs_sample_cnt+i+1, abs_sample_cnt+i+lookahead, pmt::pmt_string_to_symbol("preamble_found")); + if(fruit_tags.size() > 0) packet_attrib = Fruited_Packet; + outattrib[i] = packet_attrib; - } return size; diff --git a/src/lib/air_modes_preamble.cc b/src/lib/air_modes_preamble.cc index 4ef3c8c..97d65ba 100644 --- a/src/lib/air_modes_preamble.cc +++ b/src/lib/air_modes_preamble.cc @@ -27,6 +27,8 @@ #include #include #include +#include +#include air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db) { @@ -36,9 +38,8 @@ 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_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(unsigned char))) + gr_make_io_signature (1, 1, sizeof(float))) //the original data. we pass it out in order to use tags. { - //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_symbol = d_samples_per_chip * 2; @@ -46,6 +47,11 @@ air_modes_preamble::air_modes_preamble(int channel_rate, float threshold_db) : 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()); + d_key = pmt::pmt_string_to_symbol("preamble_found"); + //set_history(d_check_width); } int air_modes_preamble::work(int noutput_items, @@ -56,12 +62,15 @@ int air_modes_preamble::work(int noutput_items, const float *inraw = (const float *) input_items[0]; const float *inavg = (const float *) input_items[1]; - //float *outraw = (float *) output_items[0]; - unsigned char *outattrib = (unsigned char *) output_items[0]; + float *outraw = (float *) output_items[0]; int size = noutput_items - d_check_width; int pulse_offsets[4]; float bit_energies[4]; + + memcpy(outraw, inraw, size * sizeof(float)); + + uint64_t abs_out_sample_cnt = nitems_written(0); for(int i = d_samples_per_chip; i < size; i++) { float pulse_threshold = bit_energy(&inavg[i], d_samples_per_chip) * d_threshold; @@ -69,22 +78,10 @@ int air_modes_preamble::work(int noutput_items, float gate_sum_now = 0, gate_sum_early = 0, gate_sum_late = 0; if(bit_energy(&inraw[i], d_samples_per_chip) > pulse_threshold) { //if the sample is greater than the reference level by the specified amount - //while(inraw[i+1] > inraw[i]) i++; - - //if(inraw[i+1] > inraw[i]) continue; //we're still coming up on the pulse peak, so let's just fall out and look at it next time around - //if(inraw[i-1] > inraw[i]) continue; //we're past the peak, so it's no longer a valid pulse - - //a note on the above. this simple early/late gate system works for decim = 16, but doesn't work so great for decim = 8. the extra samples, subject to noise, - //mean the peak is not necessarily the center of the bit, and you get fooled into sampling at strange bit edges. the solution is an area integral, a real early/late gate - //system, computing the area of a bit and maximizing so you sample at the center of the bit shape for any decimation. for decim = 16 it won't really matter since you only have - //one possible bit center. - int gate_sum = early_late(&inraw[i], d_samples_per_chip); //see modes_energy.cc if(gate_sum != 0) continue; //if either the early gate or the late gate had greater energy, keep moving. -// if(gate_sum_late > gate_sum_now) continue; - - //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. - + //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. pulse_offsets[0] = 0; pulse_offsets[1] = int(1.0 * d_samples_per_symbol); pulse_offsets[2] = int(3.5 * d_samples_per_symbol); @@ -112,43 +109,52 @@ int air_modes_preamble::work(int noutput_items, for(int j = 5 * d_samples_per_symbol; j <= 7.5 * d_samples_per_symbol; j+=d_samples_per_chip) if(bit_energy(&inraw[i+j], d_samples_per_chip) > space_threshold) valid_preamble = false; - //make sure all four peaks are within 2dB of each other - - - float minpeak = avgpeak * 0.631; //-2db - float maxpeak = avgpeak * 1.585; //2db + //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(bit_energies[0] < minpeak || bit_energies[0] > maxpeak) continue; if(bit_energies[1] < minpeak || bit_energies[1] > maxpeak) continue; if(bit_energies[2] < minpeak || bit_energies[2] > maxpeak) continue; if(bit_energies[3] < minpeak || bit_energies[3] > maxpeak) continue; - } - //just for kicks, after validating a preamble, you might want to use all four peaks to form a more accurate "average" center sample time, so that if noise corrupts the first leading edge - //sample, you don't mis-sample the entire packet. - - //this could also be done in a separate block, although it probably saves CPU to do it here - //for the 2 samples per chip case, you can just add up the peaks at the expected peak times, then do the same for +1. if(valid_preamble) { - i--; + //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; do { - i++; + early = late = false; + gate_sum_early= bit_energy(&inraw[i+pulse_offsets[0]-1], d_samples_per_chip) + + bit_energy(&inraw[i+pulse_offsets[1]-1], d_samples_per_chip) + + bit_energy(&inraw[i+pulse_offsets[2]-1], d_samples_per_chip) + + bit_energy(&inraw[i+pulse_offsets[3]-1], d_samples_per_chip); + gate_sum_now = bit_energy(&inraw[i+pulse_offsets[0]], d_samples_per_chip) + bit_energy(&inraw[i+pulse_offsets[1]], d_samples_per_chip) + bit_energy(&inraw[i+pulse_offsets[2]], d_samples_per_chip) + bit_energy(&inraw[i+pulse_offsets[3]], d_samples_per_chip); - - gate_sum_late = bit_energy(&inraw[i+pulse_offsets[0]+1], d_samples_per_chip) - + bit_energy(&inraw[i+pulse_offsets[1]+1], d_samples_per_chip) - + bit_energy(&inraw[i+pulse_offsets[2]+1], d_samples_per_chip) - + bit_energy(&inraw[i+pulse_offsets[3]+1], d_samples_per_chip); - } while(gate_sum_late > gate_sum_now); - - outattrib[i] = 1; + gate_sum_late = bit_energy(&inraw[i+pulse_offsets[0]+1], d_samples_per_chip) + + bit_energy(&inraw[i+pulse_offsets[1]+1], d_samples_per_chip) + + bit_energy(&inraw[i+pulse_offsets[2]+1], d_samples_per_chip) + + bit_energy(&inraw[i+pulse_offsets[3]+1], d_samples_per_chip); - } else outattrib[i] = 0; + early = (gate_sum_early > gate_sum_now); + late = (gate_sum_late > gate_sum_now); + if(late) i++; + //else if(early) i--; + //if(early && late) early = late = false; + } while(late); + + //finally after all this, let's post 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 + ); + } } return size; } diff --git a/src/lib/air_modes_preamble.h b/src/lib/air_modes_preamble.h index 0334410..a537cc2 100644 --- a/src/lib/air_modes_preamble.h +++ b/src/lib/air_modes_preamble.h @@ -47,6 +47,7 @@ private: int d_samples_per_symbol; float d_threshold_db; float d_threshold; + pmt::pmt_t d_me, d_key; public: int work (int noutput_items, diff --git a/src/lib/air_modes_slicer.cc b/src/lib/air_modes_slicer.cc index 3402ce1..6f1a8bb 100644 --- a/src/lib/air_modes_slicer.cc +++ b/src/lib/air_modes_slicer.cc @@ -86,8 +86,6 @@ int air_modes_slicer::work(int noutput_items, int packet_length = 112; if(inattrib[i] == framer_packet_type(Short_Packet)) packet_length = 56; - //printf("Packet received from framer w/length %i\n", packet_length); - rx_packet.type = framer_packet_type(inattrib[i]); memset(&rx_packet.data, 0x00, 14 * sizeof(unsigned char)); memset(&rx_packet.lowconfbits, 0x00, 24 * sizeof(unsigned char)); diff --git a/src/python/uhd_modes.py b/src/python/uhd_modes.py index d59f520..cfb5d40 100755 --- a/src/python/uhd_modes.py +++ b/src/python/uhd_modes.py @@ -73,12 +73,14 @@ class adsb_rx_block (gr.top_block): print "Setting gain to %i" % (options.gain,) self.u.set_gain(options.gain) + print "Gain is %i" % (self.u.get_gain(),) else: rate = options.rate self.u = gr.file_source(gr.sizeof_gr_complex, options.filename) print "Rate is %i" % (rate,) + pass_all = 0 if options.output_all : pass_all = 1 @@ -113,8 +115,7 @@ class adsb_rx_block (gr.top_block): self.connect(self.demod, self.avg) self.connect(self.demod, (self.preamble, 0)) self.connect(self.avg, (self.preamble, 1)) - self.connect(self.demod, (self.framer, 0)) - self.connect(self.preamble, (self.framer, 1)) + self.connect((self.preamble, 0), (self.framer, 0)) self.connect(self.demod, (self.slicer, 0)) self.connect(self.framer, (self.slicer, 1))