Skip to content

Commit

Permalink
The viterbi is still not optimized - but it's in all the right places…
Browse files Browse the repository at this point in the history
… now, and it's better commented, so someone who actually knows *how* to optimize it will have an easier time, when such a person comes along.
  • Loading branch information
kzhws committed Nov 19, 2024
1 parent a5b936d commit 21f66c0
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 43 deletions.
File renamed without changes.
2 changes: 2 additions & 0 deletions src/ecc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod viterbi35;
pub mod hamming84;
56 changes: 26 additions & 30 deletions src/radios/viterbi38.rs → src/ecc/viterbi35.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//use std::{print, println};

//The reason it's called "35" is that it should really be "Viterbi,3,5" - three-bit-words encoded with a five-bit lookahead for error correction.
pub fn viterbi_encode<const NUM_BYTES: usize, const NUM_BYTES_WITH_PADDING: usize, const RETURNED_BYTES: usize>(data_original: [u8; NUM_BYTES]) -> [u8; RETURNED_BYTES]{
let mut final_array = [0 as u8; RETURNED_BYTES];
let mut data = [0 as u8; NUM_BYTES_WITH_PADDING];
Expand All @@ -20,11 +21,12 @@ pub fn viterbi_encode<const NUM_BYTES: usize, const NUM_BYTES_WITH_PADDING: usiz
((((data[index as usize/8])>>(index%8))&0x01),
(((data[(index as usize+1)/8])>>(index+1)%8)&0x01),
(((data[(index as usize+2)/8])>>(index+2)%8)&0x01)); //It's true! This goes through the byte backwards. It's faster that way.
//So long as we decode it backwards as well, it'll work out.
//let one = current_tuple.0; let two = current_tuple.1; let three = current_tuple.2; println!("Current tuple is {one}, {two}, {three} at id {index}");
index += 1;
let processed_tuple =
(((current_tuple.0^current_tuple.1^current_tuple.2) << outdex%8), //Why doesn't the just use a look up table? Is he Stupid?
((current_tuple.0^current_tuple.1) << ((outdex+1)%8)),
((current_tuple.0^current_tuple.1) << ((outdex+1)%8)), //(it's because we need to XOR the actual bits in individually.)
((current_tuple.1^current_tuple.2) << ((outdex+2)%8)));
//let one = processed_tuple.0; let two = processed_tuple.1; let three = processed_tuple.2; println!("XOR'd tuple is {one}, {two}, {three}");
final_array[(outdex as usize)/8] |= processed_tuple.0; outdex += 1;
Expand All @@ -50,49 +52,43 @@ pub fn viterbi_decode<const NUM_BYTES: usize, const RETURNED_BYTES: usize>(data:
let mut one = valids[last_value as usize][1] == true_value;
if !(zero | one){ //If neither the 0 nor the 1 account for this read word - we have an error!
//println!("That's an error, the last was {last_value}!");
/*let zero_value = valids[last_value as usize][0];
let one_value = valids[last_value as usize][1];
let zero_tuple = (zero_value>>2,(zero_value>>1)%2,(zero_value>>2)%2);
let one_tuple = (one_value>>2,(one_value>>1)%2,(one_value>>2)%2);
let zero_score = (zero_tuple.0==current_tuple.0) as u8 + (zero_tuple.1==current_tuple.1) as u8 + (zero_tuple.2==current_tuple.2) as u8;
let one_score = (one_tuple.0==current_tuple.0) as u8 + (one_tuple.1==current_tuple.1) as u8 + (one_tuple.2==current_tuple.2) as u8;
println!("Zero score is {zero_score}");
println!("One score is {one_score}");
one = zero_score < one_score;
println!("Using one? {one}");*/
let preserved_last_value = last_value;
let mut paths_array: [u8; 256] = [0; 256];
let mut record_lowest: u8 = 6;
let preserved_last_value = last_value; //Store the "last value" - we'll be changing it in this loop.
let mut paths_array: [u8; 32] = [0; 32];
let mut record_lowest: u8 = 8; //This value should be higher than the number of bits which are looked through.
let mut record_lowest_entry: u8 = 0;
for entry in 0..32{
let mut truncated_entry_no = entry;
//The way this implementation of the algorithm works: Essentially, based on the last word in the encoded datastream
//(which we know to be valid) before this erroring one, we'll reconstruct what the encoded message would be had no
//errors occurred. Then, we'll compare each sequence with what next five words we actually find in the data stream,
//and then use the lowest significant bit (i.e. the first bit) of the pattern which correctly predicted the most.
for entry in 0..32{ //For every possible pattern of next five bits...
let mut truncated_entry_no = entry; //Make a copy which we can mutilate.
let mut index_offset: u32 = 0;
while (index_offset < 5) && index+index_offset<(NUM_BYTES*8-30) as u32{
let index = index + index_offset*3;
let scanning_index = index + index_offset*3;
let current_tuple =
((((data[index as usize/8])>>(index%8))&0x01),
(((data[(index as usize+1)/8])>>(index+1)%8)&0x01),
(((data[(index as usize+2)/8])>>(index+2)%8)&0x01));
let true_value: u8 = (current_tuple.0<<2) | (current_tuple.1<<1) | (current_tuple.2);
let current_bit = truncated_entry_no%2;
let expected_value = valids[last_value as usize][current_bit];
((((data[scanning_index as usize/8])>>(scanning_index%8))&0x01),
(((data[(scanning_index as usize+1)/8])>>(scanning_index+1)%8)&0x01),
(((data[(scanning_index as usize+2)/8])>>(scanning_index+2)%8)&0x01)); //Find the 3 next real bits...
let true_value: u8 = (current_tuple.0<<2) | (current_tuple.1<<1) | (current_tuple.2); //...and then conglomerate them into a value.
let current_bit = truncated_entry_no%2; //Then take the lowest bit of the mutilated copy...
let expected_value = valids[last_value as usize][current_bit]; //...and then find out what it predicts from the viterbi algorithm.
//print!("E{expected_value}-R{true_value}-");
let is_error = expected_value != true_value;
let is_error = expected_value != true_value; //If they differ, the bit failed to predict...
//println!("Pattern {entry}. The last value was {last_value}, and the current bit is {current_bit}. I expected {expected_value} and found {true_value}.");
if is_error {
paths_array[entry] += 1;
if is_error {
paths_array[entry] += 1; //And the set of 5 bits' score is increased by 1.
}
last_value = expected_value;
last_value = expected_value;
index_offset += 1;
truncated_entry_no >>= 1;
truncated_entry_no >>= 1; //Reset, bit shift the mutilatable copy over by one and repeat.
} //println!();
let result = paths_array[entry];
if result<record_lowest {
record_lowest = result;
record_lowest = result;
record_lowest_entry = entry as u8;
}
//print!("Pattern no. {entry} has {result} errors. ");
last_value = preserved_last_value;
last_value = preserved_last_value; //Reset the last value.
} //println!();
let decided_bit = (record_lowest_entry)%2;
one = decided_bit != 0;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod filters_and_windows;
#[cfg(feature = "gui")]
pub mod gui;
pub mod radios;
pub mod ecc;

pub mod modulation;
mod vhdl;
Expand Down
4 changes: 1 addition & 3 deletions src/radios/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#[cfg(feature = "bladerf")]
pub mod bladerf;
pub mod viterbi38;
pub mod hamming84;
pub mod bladerf;
2 changes: 1 addition & 1 deletion tests/hammingcode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Test Triangle Window
use std::time::SystemTime;
use superdsp::radios::hamming84::{self, hamming_decode};
use superdsp::ecc::hamming84::{self, hamming_decode};


#[test]
Expand Down
32 changes: 23 additions & 9 deletions tests/viterbi.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::time::SystemTime;
use superdsp::radios::viterbi38::{self, viterbi_decode, viterbi_encode};
use superdsp::ecc::viterbi35::{self, viterbi_decode, viterbi_encode};


#[test]
pub fn encode_decode() {
let viterbi = viterbi38::viterbi_encode::<16, 18, 54>([75, 0x01, 0x21, 0x23, 0x43, 0x45, 0x65, 0x67, 0x87, 0x89, 0xA9, 0xAB, 0xCB, 0xCD, 0xED, 0xEF]);
let viterbi = viterbi35::viterbi_encode::<16, 18, 54>([75, 0x01, 0x21, 0x23, 0x43, 0x45, 0x65, 0x67, 0x87, 0x89, 0xA9, 0xAB, 0xCB, 0xCD, 0xED, 0xEF]);
print!("Encoded bytes: ");
for byte in viterbi{
/*for i in 0..=7{
Expand Down Expand Up @@ -46,7 +46,7 @@ pub fn encode_decode() {
#[test]
pub fn small_bytes(){
let small_byte_array = [0x0, 0b01010101, 0xFF];
let encoded_array = viterbi38::viterbi_encode::<3, 5, 15>(small_byte_array);
let encoded_array = viterbi35::viterbi_encode::<3, 5, 15>(small_byte_array);
let mut corrupted_encoded = encoded_array;
for i in 0..=14{
if i%2 == 0{corrupted_encoded[i] = encoded_array[i] ^ (0x01 << (i%8));} //Flip "random" (not really) bits.
Expand Down Expand Up @@ -87,10 +87,10 @@ pub fn every_byte() {
for i in 0..=255{
bytes_array[i] = i as u8;
}
let encoded_array = viterbi38::viterbi_encode::<256, 258, 774>(bytes_array);
let encoded_array = viterbi35::viterbi_encode::<256, 258, 774>(bytes_array);
let mut corrupted_encoded = encoded_array;
for i in 0..=773{
corrupted_encoded[i] = encoded_array[i] ^ (0x01 << (i%8)); //Flip "random" (not really) bits.
corrupted_encoded[i] = encoded_array[i] ^ (0x40 << (i%8)); //Flip "random" (not really) bits.
let that_num = corrupted_encoded[i];
let this_num = encoded_array[i];
println!("{this_num}, {that_num}");
Expand Down Expand Up @@ -128,17 +128,31 @@ pub fn speed_test() {
big_i += 1;
}
big_i = 0;
let start = SystemTime::now();
let mut start = SystemTime::now();
while big_i < (great/10_i32.pow(3)){
let encoded = viterbi_encode::<1000, 1002, 3006>(array);
let _decoded = viterbi_decode::<3006, 1000>(encoded);
big_i += 1;
}
big_i = 0;
let mut total_time = 0;
match SystemTime::now().duration_since(start) {
Ok(n) => {total_time = n.as_nanos()/great as u128;},
Err(_) => panic!("System traveled back in time!"),
}
start = SystemTime::now();
while big_i < (great/10_i32.pow(3)){
let _encoded = viterbi_encode::<1000, 1002, 3006>(array);
big_i += 1;
}
let mut encoding_time = 0;
match SystemTime::now().duration_since(start) {
Ok(n) => {println!("Process began {} milliseconds ago!", n.as_millis());
let time = n.as_nanos()/great as u128;
println!("Time per instruction is {time} nanoseconds.")},
Ok(n) => {encoding_time = n.as_nanos()/great as u128;},
Err(_) => panic!("System traveled back in time!"),
}
let decoding_time = total_time-encoding_time;
println!("encoding time per byte ... {encoding_time} nanoseconds");
println!("decoding time per byte ... {decoding_time} nanoseconds");
println!("total time per byte ... {total_time} nanoseconds");
}

0 comments on commit 21f66c0

Please sign in to comment.