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