Sunday, April 22, 2018

Java version of QIC-24 pulse decoding - Draft 01

According to Carlos G Mendioroz, who wrote these programs:

Ok, code is not clean at all, but with a little help it should be understandable...

It read the raw dump as 0/1 for sample and makes run length codes of periods (at rising edges). There is a class for doing the timing (finding the first long preamble and then resynching at preambles) and then some utility methods to follow the block structure, decode the gcr 4/5 and calculate CRC.

The main trick ended being variating the timing when an exception occurs and retry decoding a block. it tries -2, -1, +1 and +2 reference periods, which change the 0/1/2 run length boundaries.

Let me know if you need help understanding it. There is no output yet, but having a good CRC kind of proves the decoding of the block was ok (and the block id matching :)


========================  Decode.java =========================

import java.io.*;
import java.nio.file.*;

public class Decode {

public static void main(String[] args) {

Path inputFile = FileSystems.getDefault().getPath(args[0]);
int pulses = 0; // Pulse width events (24MHz)
rmean pll = new rmean(10); // PLL approximation ? running mean of pulse width

try (InputStream in = Files.newInputStream(inputFile);
BufferedReader reader =
new BufferedReader(new InputStreamReader(in))) {
int burst = 0; // same period pulses burst length
int blocks = 0; // may be blocks
int minburst = 15000; // long 
int retry = 0; // try +1 and -1 on pll 
long markpos = 0; // reset position
long pstart = 0; // preamble start
int bstart = 0; // block start in pulses
int bend = 0; // block end in pulses

while (!eot && (pulse = readPulse(reader)) >= 0 && blocks < 1000) {
// System.out.println("Pulse " + pulse + ": " + postime(pos));
        pulses++; 
        //if (blocks == 60) debug = true;
        int pw = pll.range(pulse);
        if (pw == 1) {
        if (burst == 0) { pstart = pos; bend = pulses; }
        burst++;
        } else {
        if (burst > minburst && pll.mean() < 30) {
        blocks++;
        System.out.println("Burst " + blocks + ": " + postime(pos) + " " + burst 
        + " " + pulse + "/" + pll.mean() + "(" + pw + ") [" + (pstart - markpos) 
        + "/" + (bend - bstart) + "]"); 
        minburst = 100; // normal
        while (pw == 2 && pulse > 55) {
        // Has to be 3!
        System.out.println("Rescaling!");
        pll.fix(-1);
        pw = pll.range(pulse);
        }
        if (pw == 3) {
        // Packet ??
        reader.mark(200000);
        markpos = pos;
        bstart = pulses;
        int symbols = 0;
        do {
        try {
        do {
        switch(pw) {
        case 1:
        symbol(1, blocks);
        symbols++;
        // pll.add(pulse);
        break;
        case 2:
        symbol(1, blocks);
        symbol(0, blocks);
        symbols+=2;
        // pll.add(pulse/2);
        break;
        case 3:
        symbol(1, blocks);
        symbol(0, blocks);
        symbol(0, blocks);
        symbols+=3;
        // pll.add(pulse/3);
        break;
        }
        pulse = readPulse(reader);
        pulses++;
            pw = pll.range(pulse);
        if (debug) System.out.println(" " + pw + " [" + pulse + "/" + pll.mean() + "]");
        } while (pw >= 1 && !eob);
        } catch(CodeException ce) {
        System.out.println("    Oops:" + ce.getMessage());
    state = preamble;
    sym = 0;
    blpos = 0;
    if (retry++ < 4) {
    reader.reset();
    pos = markpos;
    pulses = bstart;
    pw = 3;
    if (retry == 1) {
    pll.fix(-2);
    } else if (retry == 3) {
    pll.fix(2);
    } else {
    pll.fix(1);
    }
    System.out.println("Retry " + blocks + ": " + postime(pos) + " /" + pll.mean());
    } else if (debug) {
    while (pulses < (bstart + 5000)) {
    pulse = readPulse(reader);
        pulses++;
            pw = pll.range(pulse);
        System.out.println(" " + pw + " [" + pulse + "/" + pll.mean() + "]");
    }
    eot = true; // Exit
    }
        }
        } while (!eob && retry < 5);
        //System.out.println("");
        //if (!eot) System.out.println(" end " + symbols + ":" + pulse + "/" + pll.mean());
        }
        }
        burst = 0;
        eob = false;
        if (retry > 0) {
        // Back to original
        switch(retry) {
        case 1:
        pll.fix(2);
        break;
        case 2:
        pll.fix(1);
        break;
        case 3:
        pll.fix(-1);
        break;
        case 4:
        pll.fix(-2);
        }
        retry = 0;
        }
        }
        if (pw <= 1)
        pll.add(pulse); // Keep track of pulse width
}
} catch (IOException x) {
    System.err.println(x);
}
//System.out.println("Total: " + lines);
}

// Pulse decoding, return cycle length in samples (24MHz)

static long pos = -1; // position (time) in tape after last falling edge
static int pulse = 0; // (last) cycle/pulse width - global for debugging
static boolean debug = false;

static int readPulse(BufferedReader reader) throws IOException {
int s, len = 0;
boolean inzero = true;
while ((s = (int)reader.read()) >= 0) {
len++;
if (inzero) {
if (s != 0)
inzero = false;
} else {
if (s == 0) {
pos += len;
//System.out.println(len);
return len;
}
}
}
return -1;
}

// Block framing
static final int preamble = 0;
static final int inblock = 1;
static final int inaddress = 513;
static final int incrc = 517;
static final int post = 519;
static int state = preamble;
// inside section position
static int blpos = 0;
// symbol count
static int sym = 0;
// code being constructed
static int code = 0;
// EOF at block x
static int eof = 0; 
// EOT
static boolean eot = false;
// EOB
static boolean eob = false;


static char buffer[] = new char[512];
static char bl_add[] = new char[4];
static char bl_crc[] = new char[2];
static int bl_trk = 0;
static int bl_id = 0;
static int crc = 0;
static int crcok = 0; // blocks ok
static int polynomial = 0x1021;   // 0001 0000 0010 0001  (0, 5, 12)
static String codes[] = { "11001", "11011", "10010", "10011", "11101", "10101", "10110", "10111", 
   "11010", "01001", "01010", "01011", "11110", "01101", "01110", "01111" };
static String blockstart = "1111100111";
static String filemark = "00101";

static void symbol(int s, int block) {
if (false && state != preamble && state != post) {
System.out.print(s);
if (sym == 9) {
if ((blpos % 8) == 7) 
System.out.println();
else
System.out.print(" ");
}
}

switch(state) {
case preamble:
if (s==0) {
if (sym < 2)
sym++;
else 
throw new CodeException(pos, state,blpos,sym,s);
} else {
if (sym == 1)
throw new CodeException(pos, state,blpos,sym,s);
if (sym > 0 && ++sym == 5) {
state = inblock;
sym = 0;
code = 0;
blpos = 0;
crc = 0xffff;
}
}
break;
case inblock:
code *= 2;
code += s;
sym++;
if (sym == 10) {
int n1 = gcr45[code & 0x1f];
if (n1 == 100) {
//File mark
if (eof > 0 && eof < block) {
// EOT, i.e., second block with EOF
eot = true;
} else { 
eof = block;
}
blpos++;
} else {
if (n1 < 0 || n1 > 15 || eot || eof == block) {
if (eof == block) eof = 0;
throw new CodeException(pos, state,blpos,5,code & 0x1f);
}
int n2 = gcr45[code >> 5];
if (n2 < 0 || n2 > 15) {
if (eof == block) eof = 0;
throw new CodeException(pos, state,blpos,0,code >> 5);
}
char c = (char)(n1 | n2 << 4);
//System.out.println("Byte " + blpos + ":" + (int)c);
buffer[blpos++] = c;
// CRC
for (int i = 0; i < 8; i++) {
                boolean bit = ((c  >> (7-i) & 1) == 1);
                boolean c15 = ((crc >> 15    & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit) crc ^= polynomial;
            }
}
sym = 0;
code = 0;
if (blpos == 512) {
state = inaddress;
blpos = 0;
}
}
break;
case inaddress:
code *= 2;
code += s;
sym++;
if (sym == 10) {
int n1 = gcr45[code & 0x1f];
if (n1 < 0 || n1 > 15)
throw new CodeException(pos, state,blpos,sym,s);
int n2 = gcr45[code >> 5];
if (n2 < 0 || n2 > 15)
throw new CodeException(pos, state,blpos,sym-5,s);
char c = (char)(n1 | n2 << 4);
bl_add[blpos++] = c;
// CRC
for (int i = 0; i < 8; i++) {
                boolean bit = ((c  >> (7-i) & 1) == 1);
                boolean c15 = ((crc >> 15    & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit) crc ^= polynomial;
            }
sym = 0;
code = 0;
if (blpos == 4) {
bl_trk = bl_add[0];
bl_id = bl_add[3] | bl_add[2] << 8 | bl_add[1] << 16; 
// if (!eof) System.out.println("  Address: Track " + track + " bl_trk " + bl_id); 
state = incrc;
blpos = 0;
}
}
break;
case incrc:
code *= 2;
code += s;
sym++;
if (sym == 10) {
int n1 = gcr45[code & 0x1f];
if (n1 < 0 || n1 > 15)
throw new CodeException(pos, state,blpos,sym,s);
int n2 = gcr45[code >> 5];
if (n2 < 0 || n2 > 15)
throw new CodeException(pos, state,blpos,sym-5,s);
char c = (char)(n1 | n2 << 4);
bl_crc[blpos++] = c;
sym = 0;
code = 0;
if (blpos == 2) {
// x^16 + x^12 + x^5 + 1
int crc_check = bl_crc[1] | bl_crc[0] << 8; 
crc &= 0xffff;
if (eot) {
System.out.println("  EOT!");
} else if(eof == block) {
System.out.println("  EOF!");
} else
if (crc == crc_check) System.out.println("  Trk " + bl_trk + " Blk " + bl_id 
+ "/" + (++crcok) + " CRC " + crc);
state = post;
blpos = 0;
}
}
break;
case post:
if (s==0) 
throw new CodeException(pos, state,blpos,sym,s);
if (++sym > 5) {
//System.out.println("Post!");
state = preamble;
sym = 0;
blpos = 0;
eob = true;
}
break;
}
}
// Prety print position (24 MHz sampling)
static final String postime(long pos) {
int s = (int)(pos / 24000000);
pos -= s * 24000000;
int ms = (int)(pos / 24000);
pos -= ms * 24000;
int us = (int)(pos / 24);
return "" + s + "s " + ms + "ms " + us + "us";
}

//100 for FileMark
static final int gcr45[] = {-1,-1,-1,-1,-1, 100,-1,-1,-1,9,
10,11,-1,13,14,15,-1,-1,2,3,
-1,5,6,7,-1,0,8,1,-1,4,
12,-1};

static class CodeException extends RuntimeException {
CodeException(long aPos, int aState,int aBlockPosition, int aSymbolPosition, int aSymbol) {
super("Bad symbol " + aSymbol + " at " + postime(aPos) + ": " 
+ aSymbolPosition + "/" + aBlockPosition + " [" + aState + "]");
}
}

}



========================  rmean.java =========================



public class rmean {
int val[];
int size = 0;
int last = 0;
long sum = 0;
public rmean(int aSize) {
size = aSize;
val = new int[size];
last = 0;
for(int i = 0; i < size; i++) val[i] = 0;
}
public int mean() {
return (int) (sum / size + 0.5);
}
public void add(int n) {
sum -= val[last];
sum += n;
val[last++] = n;
if (last >= size) last = 0;
}
public void fix(int n) {
sum += n * size; // try different lock 
}
// Delta es el medio rango de error
// La medida de pulso es 9-10, ciclo ~25
static double error = 0.6; 
public int range(int n) {
int m = this.mean();
//int delta = (int)((double)m * error + 0.5);
int delta = 15;
if (n < 10)
return 0;
if (n < (m + delta))
return 1;
if (n >= (m + delta) &&
n < (m * 2 + delta))
return 2;
if (n >= (m * 2 + delta) &&
n < (m * 3 + delta))
return 3;
return -1;
}
}





No comments:

Post a Comment