package rtty;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Binary_frame_handler {
protected ArrayList<StringRxEvent> _listeners = new ArrayList<StringRxEvent>() ;
private List<Extractor> extractors = new ArrayList<Extractor>();
int max_extractors = 10;
private int[] lengths = new int[]{40,40, 64, 88, 112, 136, 160, 184, 208, 232, 256, 280, 304, 328, 352, 376, 400, 424, 448, 472, 496, 528, 576, 624, 672, 720, 768, 816, 864, 912, 960, 1008, 1088, 1184, 1280, 1376, 1472, 1568, 1664, 1760, 1856, 1952, 2048, 2240, 2432, 2624, 2816, 3008, 3200, 3392, 3584, 3776, 3968, 4160, 4352, 4544, 4736, 4928, 5120, 5312, 5504, 5696, 5888, 6144};
private boolean _last_valid = false;
//private double[] _sync_search_buff = null;
//private int _sync_search_ptr = 0;
private boolean[] _sync_pattern = null;
private Extractor primary_extractor = null;
private int last_length = -1;
int _max_buff = 6000;
private double[] rx_buffer = new double[_max_buff];
int rx_buff_ptr = 0; //points to last inserted item
Map <String,boolean[]> data_sync_map = new HashMap<String, boolean[]>();
Map <String,boolean[]> mask_sync_map = new HashMap<String, boolean[]>();
Map <String,Integer> d_len_sync_map = new HashMap<String, Integer>();
Turbo_decoder tdec = new Turbo_decoder();
public Binary_frame_handler(boolean[] sync_pattern) {
setSync(sync_pattern);
}
public void addStringRecievedListener(StringRxEvent listener)
{
_listeners.add(listener);
}
protected void fireStringReceived(byte[] str, boolean checksum, int length, int flags, int fixed)
{
last_length = length;
for (int i = 0; i < _listeners.size(); i++)
{
_listeners.get(i).StringRx(str,checksum, length, flags, fixed);
}
}
public void provide_binary_sync_helper(byte[] data, byte[] mask, String id, int len)
{
//TODO: add start sync and len fields
boolean[] data_b = new boolean[len+4];
boolean[] mask_b = new boolean[len+4];
int m = 0x80;
int j = 0;
//place into boolean buffer
for (int i = 0; i < len; i++)
{
if ((data[j] & m) > 0)
data_b[i] = true;
if ((mask[j] & m) > 0)
mask_b[i] = true;
m >>= 1;
if (m == 0){
m = 0x80;
j++;
}
}
//do interleaving
int D = len + 4;
int rowTcSb = (int) Math.ceil((double)D/32);
int Nd = 32*rowTcSb - D;
int[] d0 = Turbo_encoder.subBlockInterleaver1(data_b,32,rowTcSb,Nd);
int[] m0 = Turbo_encoder.subBlockInterleaver1(mask_b,32,rowTcSb,Nd);
int i = 0;
int k = 0;
boolean[] out_data = new boolean[len + 4 + 16];
boolean[] out_mask = new boolean[len + 4 + 16];
k = 16;
while (k < (len + 4 + 16) && i < d0.length)
{
if (d0[i] >= 0)
{
if (d0[i] == 0)
out_data[k] = false;
else
out_data[k] = true;
if (m0[i] == 0)
out_mask[k] = false;
else
out_mask[k] = true;
k++;
}
i++;
}
//add on length field TODO: hamming stuff
int len_f = java.util.Arrays.binarySearch(lengths, len);
if (len_f > 0)
{
len_f = len_f << 2;
len_f |= (len_f<<4) & 0xF00;
len_f &= 0xF0F;
int len_m =0xF0C;
//interleave length field
int addr = 0;
for (i = 0; i < 16; i++)
{
if ((len_f & (1<<addr))>0)
out_data[i] = true;
if ((len_m & (1<<addr))>0)
out_mask[i] = true;
addr += 8;
if (addr > 15){
addr -= 16;
addr++;
if (addr >= 8)
addr -=8;
}
}
}
if (data_sync_map.containsKey(id))
data_sync_map.remove(id);
if (mask_sync_map.containsKey(id))
mask_sync_map.remove(id);
if (d_len_sync_map.containsKey(id))
d_len_sync_map.remove(id);
data_sync_map.put(id, out_data);
mask_sync_map.put(id, out_mask);
d_len_sync_map.put(id, new Integer(len+4+16));
}
public boolean get_last_valid() {
return _last_valid;
}
public void setSync(boolean[] pattern)
{
_sync_pattern = pattern;
}
public String bits2chars(double[] bits) {
String out_buff = "";
//TODO: check what happens if partial sync found and successful decode in same calling
_last_valid = false;
int old_ptr = rx_buff_ptr;
//put new data onto buffer
for (int i = 0; i < bits.length; i++)
{
rx_buff_ptr++;
if (rx_buff_ptr >= _max_buff)
rx_buff_ptr = 0;
rx_buffer[rx_buff_ptr] = -bits[i];
}
//go through and run each extractor
boolean remove_all = false; //set if one extractor indicates it has a valid string
for (int i = 0; i < extractors.size(); i++)
{
int r = extractors.get(i).process(old_ptr, rx_buff_ptr);
if (r > rx_buffer.length || r < 0)
extractors.remove(i);
if (r == -2){
remove_all = true;
_last_valid = true;
break;
}
}
if (remove_all)
{
while(extractors.size() > 0)
extractors.remove(0);
}
else
{
//look for sync based on partial packets
int search_start = old_ptr;
for (int i = 0; i < bits.length; i++)
{
search_start++;
if (search_start >= _max_buff)
search_start = 0;
for (Map.Entry<String, boolean[]> entry : data_sync_map.entrySet())
{
double cmp = compare_sync(search_start,entry.getValue(),mask_sync_map.get(entry.getKey()));
if (cmp > 0.75) //change
{
out_buff = out_buff + " <sync p>";
System.out.print("sync partial (" + cmp + ") ");
//calculate start address
int ptr_end_sync = search_start - d_len_sync_map.get(entry.getKey());
int ptr_post_sync = ptr_end_sync + 1;
if (ptr_end_sync < 0)
ptr_end_sync += _max_buff;
if (ptr_post_sync < 0)
ptr_post_sync += _max_buff;
//check there isnt already an extractor with this start address
boolean exist = false;
for (int j = 0; j < extractors.size(); j++)
{
if (extractors.get(j)._ptr_post_sync == ptr_post_sync){
exist = true;
extractors.get(j).flags_set_got_packet_sync();
}
}
//if new string detected, add to list
if (exist == false)
{
out_buff = out_buff + " <sync p>";
System.out.println("new");
Extractor e = null;
if (extractors.size() < max_extractors){
e = new Extractor(ptr_end_sync);
e.process(ptr_end_sync, rx_buff_ptr);
e.flags_set_got_packet_sync();
extractors.add(e);
primary_extractor = extractors.get(extractors.size()-1);
if (last_length > 0){
e = new Extractor(ptr_end_sync,last_length);
e.process(ptr_end_sync, rx_buff_ptr);
e.flags_set_got_packet_sync();
extractors.add(e);
}
}
else
System.out.println("Warning: Unable to create new extractor");
}
else
System.out.println("not new");
}
}
}
}
//look for sync sequence
int search_start = old_ptr;
search_start = old_ptr;
for (int i = 0; i < bits.length; i++)
{
search_start++;
if (search_start >= _max_buff)
search_start = 0;
int cmp = compare_sync(search_start);
if (cmp > 29) //change
{
out_buff = out_buff + " <sync>";
Extractor e = null;
if (extractors.size() < max_extractors){
e = new Extractor(search_start);
e.process(old_ptr, rx_buff_ptr);
e.flags_set_got_sync();
extractors.add(e);
primary_extractor = extractors.get(extractors.size()-1);
if (last_length > 0){
e = new Extractor(search_start,last_length);
e.process(old_ptr, rx_buff_ptr);
e.flags_set_got_sync();
extractors.add(e);
}
}
else
System.out.println("Warning: Unable to create new extractor");
}
}
if (primary_extractor != null)
{
out_buff = out_buff + primary_extractor.last_string;
primary_extractor.last_string = "";
if (remove_all)
out_buff = out_buff + "\n";
}
//out_buff = out_buff +" " +score;
//if (score == _sync_pattern.length)
// out_buff = out_buff + "!!";
return out_buff;
}
private int compare_sync(int last_added)
{
int count=0;
for (int i = _sync_pattern.length-1; i >= 0; i--)
{
if (_sync_pattern[i] == rx_buffer[last_added]>0)
count++;
last_added--;
if (last_added < 0)
last_added = rx_buffer.length-1;
}
return count;
}
private double compare_sync(int last_added, boolean[] pattern, boolean[] mask)
{
int count=0;
int total = 0;
for (int i = pattern.length-1; i >= 0; i--)
{
if (mask[i])
{
total++;
if (pattern[i] == rx_buffer[last_added]>0)
count++;
}
last_added--;
if (last_added < 0)
last_added = rx_buffer.length-1;
}
return (double)count/(double)total;
}
class Extractor
{
int _cycles_active = 0;
int _ptr_post_sync = 0;
int internal_interleaver_len = -1;
int systematic_start = -1;
int systematic_end = -1;
int parity_start = -1;
int parity_0_end = -1;
int parity_1_end = -1;
int parity_2_end = -1;
int parity_3_end = -1;
private int parity_counter = 0;
int flags = 0;
int partial_byte = 0;
int partial_byte_mask = 0x80;
String last_string = "";
public Extractor(int ptr_end_sync)
{
_ptr_post_sync = ptr_end_sync;
_ptr_post_sync++;
if (_ptr_post_sync > _max_buff)
_ptr_post_sync = 0;
}
public Extractor(int ptr_end_sync, int length)
{
_ptr_post_sync = ptr_end_sync;
_ptr_post_sync++;
if (_ptr_post_sync > _max_buff)
_ptr_post_sync = 0;
internal_interleaver_len = length;
flags_set_preset_len();
calcuate_addresses();
}
public int process(int last_ptr, int new_ptr)
{
int ptr = last_ptr;
last_string = "";
do
{
ptr++;
if (ptr >= _max_buff)
ptr = 0;
int since_sync = ptr - _ptr_post_sync;
if (since_sync < 0)
since_sync = since_sync + _max_buff;
//add this bit to the output buffer
if (since_sync >= 0 && since_sync < 5500)
{
if (rx_buffer[ptr] > 0)
partial_byte |= partial_byte_mask;
partial_byte_mask >>= 1;
if (partial_byte_mask == 0)
{
partial_byte_mask = 0x80;
last_string = last_string + " " + toHexString(partial_byte);
partial_byte = 0;
}
}
if (since_sync == 2*8-1) //get length
{
double[] size_f = buffer_copy(_ptr_post_sync,ptr);
boolean[] sf = new boolean[size_f.length];
int addr = 0;
for (int i = 0; i < size_f.length; i++) //deinterleave length
{
sf[addr] = size_f[i] > 0;
addr = addr + 8;
if (addr > 15){
addr = addr - 16;
addr += 1;
if (addr >= 8)
addr -= 8;
}
}
byte hamming1 = 0;
byte mask = 1;
for (int i = 0; i < 8; i++)
{
if (sf[i])
hamming1 |= mask;
mask <<= 1;
}
byte hamming2 = 0;
mask = 1;
for (int i = 8; i < 16; i++)
{
if (sf[i])
hamming2 |= mask;
mask <<= 1;
}
Turbo_decoder.hamming_decode84(hamming1); //TODO: this
Turbo_decoder.hamming_decode84(hamming2);
if (internal_interleaver_len < 0)
internal_interleaver_len = lengths[((hamming1&0xC) >> 2) + ((hamming2&0xF) << 2)];
System.out.println("length: " + internal_interleaver_len);
last_string = last_string + " <L:" + internal_interleaver_len + ">";
//internal_interleaver_len = 40; //////////////////////////////change
calcuate_addresses();
}
else if (since_sync == systematic_end) //get systematic bits
{
double[] systematic = Turbo_decoder.systematic_subblock_deinterleave(
buffer_copy(wrap(_ptr_post_sync+systematic_start),ptr));
boolean[] sys_bits = new boolean[internal_interleaver_len];
for (int i = 0; i < internal_interleaver_len; i++)
sys_bits[i] = systematic[i]>0 ? true:false;
last_string = last_string + " <P>";
if (Turbo_decoder.check_checksum(sys_bits))
{
flags_set_no_parity_needed();
System.out.println("checksum passed");
flags_set_parity_needed(0);
fireStringReceived(toByteArray(sys_bits), true,internal_interleaver_len,flags,0);
return -2;
}
else
{
System.out.println("checksum failed");
}
}
else if (since_sync == parity_0_end
|| since_sync == parity_1_end
|| since_sync == parity_2_end
|| since_sync == parity_3_end)
{
double[] bits = new double[since_sync - parity_start + 1 + internal_interleaver_len+4];
//copy the relevent bits into the buffer
int i = wrap(_ptr_post_sync + systematic_start);
int j = 0;
int end_ptr = wrap(_ptr_post_sync + systematic_end);
if (end_ptr >= _max_buff)
end_ptr = end_ptr - _max_buff;
while(i != end_ptr )
{
bits[j] = rx_buffer[i];
j++;
i++;
if (i >= _max_buff)
i = 0;
}
bits[j] = rx_buffer[i];
j++;
i = wrap(_ptr_post_sync + parity_start);
if (i >= _max_buff)
i = i - _max_buff;
while(i != ptr )
{
bits[j] = rx_buffer[i];
j++;
i++;
if (i >= _max_buff)
i = 0;
}
bits[j] = rx_buffer[i];
double[][] v = Turbo_decoder.output_rate_dematching(bits, internal_interleaver_len+4);
boolean[] out = tdec.decode(v[0], v[1], v[2], true, true);
if (tdec.last_success)
{
System.out.println("turbo: checksum passed");
last_string = last_string + " <fixed: " + tdec.last_fixed + ">";
flags_set_parity_needed(parity_counter);
fireStringReceived(toByteArray(out), true,internal_interleaver_len,flags,tdec.last_fixed);
return -2;
}
else
{
parity_counter++;
System.out.println("turbo: checksum failed");
}
if (since_sync == parity_3_end){
last_string = last_string + " < :( >";
return -1;
}
}
_cycles_active++;
} while(ptr != new_ptr);
return _cycles_active;
}
private String toHexString(int input)
{
String output = "";
output = Integer.toHexString(input);
if (output.length() == 1)
output = "0"+output;
return output;
}
private void calcuate_addresses()
{
parity_start = 2*8 + internal_interleaver_len + 4 + 4 + 8; //(int) (Math.ceil((double)(3*8 + internal_interleaver_len + 4)/8)*8);
systematic_end = 2*8-1 + internal_interleaver_len + 4;
systematic_start = 2*8;
parity_0_end = parity_start + (internal_interleaver_len + 4)/4 - 1;
parity_1_end = parity_start + (internal_interleaver_len + 4)*7/13 - 1;
parity_2_end = parity_start + (internal_interleaver_len + 4) - 1;
parity_3_end = parity_start + (internal_interleaver_len + 4)*2 - 1;
}
private int wrap(int in)
{
if (in < 0)
in += _max_buff;
if (in >= _max_buff)
in -= _max_buff;
return in;
}
byte[] toByteArray(boolean[] in)
{
byte[] out = new byte[(int)Math.ceil((double)in.length/8)];
int mask = 0x80;
int j = 0;
for (int i = 0; i < in.length; i++)
{
if (in[i])
out[j] |= mask;
mask >>= 1;
if (mask == 0){
mask = 0x80;
j++;
}
}
return out;
}
//inclusive of start and end
double[] buffer_copy(int start, int end)
{
int len = end - start+1;
if (len < 0)
len = len + _max_buff;
double[] out = new double[len];
if (start >=_max_buff )
start = start - _max_buff;
int i = start;
int j = 0;
while(i != end)
{
out[j] = rx_buffer[i];
j++;
i++;
if (i >= _max_buff)
i = 0;
}
out[j] = rx_buffer[i];
return out;
}
void flags_set_got_sync()
{
flags |= (1<<0);
}
void flags_set_got_packet_sync()
{
flags |= (1<<1);
}
void flags_set_no_parity_needed()
{
flags |= (1<<2);
}
void flags_set_parity_needed(int i)
{
flags |= ((i&0x3)<<3);
}
void flags_set_preset_len()
{
flags |= (1<<5);
}
}
}