diff --git a/Makefile b/Makefile index 498e9b69..b6310974 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ SRCS = \ cmd_read.c \ cmd_write.c \ cmd_mfmdecode.c \ - cmd_fmdecode.c \ cmd_testpattern.c \ cmd_fluxdump.c \ cmd_calibrate.c \ + cmd_getclock.c \ OBJS = $(patsubst %.c, .objs/%.o, $(SRCS)) diff --git a/cmd_fmdecode.c b/cmd_fmdecode.c deleted file mode 100644 index 3c8f7f7e..00000000 --- a/cmd_fmdecode.c +++ /dev/null @@ -1,256 +0,0 @@ -#include "globals.h" -#include "sql.h" -#include -#include - -#define DEBOUNCE_TICKS 0x28 - -#define CLOCK_LOCK_BOOST 6 /* arbitrary */ -#define CLOCK_LOCK_DECAY 1 /* arbitrary */ -#define CLOCK_DETECTOR_AMPLITUDE_THRESHOLD 60 /* arbi4rary */ -#define CLOCK_ERROR_BOUNDS 0.50 - -static const char* inputfilename = NULL; -static const char* outputfilename = NULL; -static bool verbose = false; -static sqlite3* indb; -static sqlite3* outdb; - -static const uint8_t* inputbuffer; -static int inputlen; -static int cursor; -static int elapsed_ticks; - -static int clock_period; /* mfm cell width */ -static int period0; /* lower limit for a short transition */ -static int period1; /* between short and long transitions */ -static int period2; /* upper limit for a long transition */ - -static uint8_t outputbuffer[5*1024]; -static int outputbufferpos; -static uint8_t fifo = 0; -static int bitcount = 0; -static bool phaselocked = false; - -static int thislength = 0; -static int nextlength = 0; - -static void syntax_error(void) -{ - fprintf(stderr, - "syntax: fluxclient fmdecode :\n" - " -i input filename (.flux)\n" - " -o output filename (.rec)\n" - " -v verbose decoding\n" - ); - exit(1); -} - -static char* const* parse_options(char* const* argv) -{ - for (;;) - { - switch (getopt(countargs(argv), argv, "+i:o:v")) - { - case -1: - return argv + optind - 1; - - case 'i': - inputfilename = optarg; - break; - - case 'o': - outputfilename = optarg; - break; - - case 'v': - verbose = true; - break; - - default: - syntax_error(); - } - } -} - -static void close_files(void) -{ - if (indb) - sqlite3_close(indb); - if (outdb) - sqlite3_close(outdb); -} - -static void open_files(void) -{ - indb = sql_open(inputfilename, SQLITE_OPEN_READONLY); - outdb = sql_open(outputfilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); - sql_prepare_record(outdb); - atexit(close_files); -} - -static void queue_bit(bool bit) -{ - fifo <<= 1; - fifo |= bit; - bitcount++; -} - -static bool read_bit(void) -{ - /* - * FM is incredibly stupid. Every cell has a pulse at the beginning; - * cells which contain a 1 have a pulse in the middle; cells which don't - * have a 0. Therefore we have these two states: - * - * Data: 0 1 - * Signal: 10 11 - * - * Therefore, a *long* interval represents a 0, and two *short* intervals - * represent a 1. - */ - - if (cursor >= inputlen) - return false; - uint8_t t = inputbuffer[cursor++]; - elapsed_ticks += t; - - if ((t < period0) || (t > period2)) - { - /* Garbage data: our clock's wrong. */ - phaselocked = false; - return false; - } - else if (t < period1) - { - /* That was the first short interval of a 1: now consume any additional - * until we reach the clock pulse again. */ - if (cursor >= inputlen) - return false; - while (t < period1) - { - if (cursor >= inputlen) - return false; - uint8_t tt = inputbuffer[cursor++]; - elapsed_ticks += tt; - t += tt; - } - - /* If the clock pulse didn't show up, give up. */ - if (t > period2) - { - phaselocked = false; - return false; - } - return true; - } - else - { - /* A long transition. */ - return false; - } -} - -static uint8_t read_byte(void) -{ - while (phaselocked && (bitcount < 8)) - queue_bit(read_bit()); - bitcount = 0; - return fifo; -} - -static void log_record(char type) -{ - if (verbose) - printf("\n % 8.3fms [0x%05x]: ", - (double)elapsed_ticks / (TICKS_PER_US*1000.0), cursor); - putchar(type); -} - -static bool process_byte(uint8_t b) -{ - outputbuffer[outputbufferpos++] = b; - if (outputbufferpos == sizeof(outputbuffer)) - goto abandon_record; - - error("unimplemented --- can't handle FM yet"); - -abandon_record: - if (verbose && (outputbufferpos > 4)) - printf(" misread"); - phaselocked = false; - return false; -} - -static void decode_track_cb(int track, int side, const struct fluxmap* fluxmap) -{ - printf("Track %02d side %d: ", track, side); - - inputbuffer = fluxmap->intervals; - inputlen = fluxmap->bytes; - cursor = 0; - elapsed_ticks = 0; - int record = 0; - - while (cursor < inputlen) - { - if (!phaselocked) - { - while (cursor < inputlen) - { - clock_period = fluxmap_seek_clock(fluxmap, &cursor, 16); - - /* - * Okay, this looks good. We'll assume this is clock/2 --- 250kHz - * for HD floppies; this is one long transition. - */ - - double short_time = clock_period / 2.0; - double long_time = short_time * 2.0; - - period0 = short_time - short_time * CLOCK_ERROR_BOUNDS; - period1 = (short_time + long_time) / 2.0; - period2 = long_time + long_time * CLOCK_ERROR_BOUNDS; - phaselocked = true; - - while (phaselocked && (cursor < inputlen)) - { - if (read_bit()) - goto found_byte; - } - } - found_byte:; - fifo = 1; - bitcount = 1; - outputbufferpos = 0; - } - - if (process_byte(read_byte())) - { - sql_write_record(outdb, track, side, record, outputbuffer, outputbufferpos); - record++; - } - } - - if (verbose) - printf("\n "); - printf(" = %d records\n", record); -} - -void cmd_fmdecode(char* const* argv) -{ - argv = parse_options(argv); - if (countargs(argv) != 1) - syntax_error(); - if (!inputfilename) - error("you must supply a filename to read from"); - if (!outputfilename) - error("you must supply a filename to write to"); - - open_files(); - sql_stmt(outdb, "BEGIN"); - sql_stmt(outdb, "DELETE FROM records"); - sql_for_all_flux_data(indb, decode_track_cb); - sql_stmt(outdb, "COMMIT"); -} - diff --git a/cmd_getclock.c b/cmd_getclock.c new file mode 100644 index 00000000..36b059e6 --- /dev/null +++ b/cmd_getclock.c @@ -0,0 +1,109 @@ +#include "globals.h" +#include "sql.h" +#include + +static const char* input_filename = NULL; +static int track = 0; +static int side = 0; +static int threshold = 100; +static sqlite3* db; + +static void syntax_error(void) +{ + fprintf(stderr, + "syntax: fluxclient getclock :\n" + " -i input filename (.flux)\n" + " -t track to analyse\n" + " -0 analyse side 0\n" + " -1 analyse side 1\n" + " -T noise threshold (default: 100)\n" + ); + exit(1); +} + +static char* const* parse_options(char* const* argv) +{ + for (;;) + { + switch (getopt(countargs(argv), argv, "+i:t:01T:")) + { + case -1: + return argv + optind - 1; + + case 'i': + input_filename = optarg; + break; + + case 't': + track = atoi(optarg); + break; + + case '0': + side = 0; + break; + + case '1': + side = 1; + break; + + case 'T': + threshold = atoi(optarg); + break; + + default: + syntax_error(); + } + } +} + +static void close_file(void) +{ + sqlite3_close(db); +} + +static void open_file(void) +{ + db = sql_open(input_filename, SQLITE_OPEN_READONLY); + atexit(close_file); +} + +void cmd_getclock(char* const* argv) +{ + argv = parse_options(argv); + if (countargs(argv) != 1) + syntax_error(); + if (!input_filename) + error("you must supply a filename to read from"); + + open_file(); + struct fluxmap* fluxmap = sql_read_flux(db, track, side); + if (!fluxmap) + error("no data for that track in the file"); + + uint32_t buckets[256] = {}; + for (int i=0; ibytes; i++) + buckets[fluxmap->intervals[i]]++; + + bool skipping = false; + for (int i=0; i<256; i++) + { + uint32_t v = buckets[i]; + if (v < threshold) + { + if (!skipping) + { + skipping = true; + printf("...\n"); + } + } + else + { + skipping = false; + printf("(0x%02x) % 6.2fus: %d\n", i, (double)i / (double)TICKS_PER_US, v); + } + } + + nanoseconds_t estimated_clock = fluxmap_guess_clock(fluxmap); + printf("Estimated clock: %6.2fus\n", estimated_clock/1000.0); +} + diff --git a/cmd_mfmdecode.c b/cmd_mfmdecode.c index e4597c08..3d50343d 100644 --- a/cmd_mfmdecode.c +++ b/cmd_mfmdecode.c @@ -23,28 +23,13 @@ static bool verbose = false; static sqlite3* indb; static sqlite3* outdb; -static const uint8_t* inputbuffer; -static int inputlen; static int cursor; -static int elapsed_ticks; - -static int clock_period; /* mfm cell width */ -static int period0; /* lower limit for a short transition */ -static int period1; /* between short and medium transitions */ -static int period2; /* between medium and long transitions */ -static int period3; /* upper limit for a long transition */ - -static uint8_t outputbuffer[5*1024]; +static nanoseconds_t elapsed_time; +static uint8_t outputbuffer[20*1024]; static int outputbufferpos; -static uint8_t fifo = 0; +static uint8_t outputfifo = 0; static int bitcount = 0; -static bool phaselocked = false; static bool phase = false; -static bool queued = false; -static bool has_queued = false; - -static int thislength = 0; -static int nextlength = 0; static void syntax_error(void) { @@ -100,268 +85,107 @@ static void open_files(void) atexit(close_files); } -static void queue_bit(bool bit) +static void write_bit(bool bit) { - fifo <<= 1; - fifo |= bit; + outputfifo = (outputfifo << 1) | bit; bitcount++; -} - -static bool read_bit(void) -{ - if (has_queued) + if (bitcount == 8) { - has_queued = false; - return queued; - } - - /* - * MFM divides the signal into two-bit cells, which can be either x0 - * (representing 0) and 01 (representing 1). x can be any value; the rules - * set it to 0 of the previous cell contained 01, and 1 otherwise. - * - * However, all we have are the intervals and so we don't know where a cell - * begins. Consider: - * - * Data: 1 1 0 0 0 1 0 0 1 1 1 0 1 0 1 0 1 0 - * Signal: 01 01 00 10 10 01 00 10 01 01 01 00 01 00 01 00 01 00 - * - * If our signal is offset half a cell, we get this: - * - * Data: 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 - * Signal: 0 10 10 01 01 00 10 01 00 10 10 10 00 10 00 10 00 10 0 - * - * However! This violates the rules. We have 10 00. Both of these - * encode zeros, which means the second 00 should have been a 10. - * So, after a long transition, we know that the cell sequence - * must have been (assuming correct encoding) 00 01, encoding 0 - * and 1, and that the next interval will be at the start of a - * cell. - * - * Data: 0 0 1 1 0 0 1 0 0 0 0 0 | 1 0 1 0 1 0 - * Signal: 0 10 10 01 01 00 10 01 00 10 10 10 00 | x1 00 01 00 01 00 - * \ resync - */ - - if (cursor >= inputlen) - return false; - uint8_t t = inputbuffer[cursor++]; - elapsed_ticks += t; - - if ((t < period0) || (t > period3)) - { - /* Garbage data: our clock's wrong. */ - phaselocked = false; - return false; - } - else if (t < period1) - { - /* Short transition: either (1),01, or ,(1)0,1. */ - return !phase; - } - else if (t > period2) - { - /* Long transition: either (1),00,01 or ,(1)0,00,1. - * The latter is illegal but occurs inside marker bytes. */ - - if (!phase) - { - queued = true; - has_queued = true; - return false; - } - else - { - queued = false; - has_queued = true; - return false; - } - } - else - { - /* Medium transition: either (1),00,1 or ,(1)0,01. */ - if (!phase) - { - phase = true; - return false; - } - else - { - queued = true; - has_queued = true; - phase = false; - return false; - } + outputbuffer[outputbufferpos++] = outputfifo; + bitcount = 0; } } -static uint8_t read_byte(void) -{ - while (phaselocked && (bitcount < 8)) - queue_bit(read_bit()); - bitcount = 0; - return fifo; -} - -static void log_record(char type) +static void log_record(void) { if (verbose) - printf("\n % 8.3fms [0x%05x]: ", - (double)elapsed_ticks / (TICKS_PER_US*1000.0), cursor); - putchar(type); -} - -static bool process_byte(uint8_t b) -{ - outputbuffer[outputbufferpos++] = b; - if (outputbufferpos == sizeof(outputbuffer)) - goto abandon_record; - -#if 0 - if (outputbufferpos < 4) - { - if ((b != 0xA1) && (b != 0xC2)) - goto abandon_record; - } - #endif - - if (outputbufferpos == 4) - { - switch (outputbuffer[3]) - { - case IAM: - thislength = IAM_LEN; - log_record('T'); - break; - - case IDAM: - thislength = IDAM_LEN; - log_record('H'); - break; - - case DAM1: - case DAM2: - thislength = nextlength; - nextlength = 0; - /* Sector with a header? */ - if (thislength == 0) - goto abandon_record; - log_record('D'); - break; - - default: - goto abandon_record; - } - } - - if (outputbufferpos == thislength) - { - /* We've read a complete record. */ - - if (verbose) - printf(" %d bytes ", thislength); - - switch (outputbuffer[3]) - { - case IDAM: - if (verbose) - printf(" C%02d H%01d S%02d", outputbuffer[4], outputbuffer[5], outputbuffer[6]); - nextlength = (1<<(outputbuffer[7] + 7)) + DAM_LEN; - break; - - case DAM1: - case DAM2: - if (verbose) - printf(" (%d bytes user data)", thislength - DAM_LEN); - break; - } - - phaselocked = false; - return true; - } - - return false; - -abandon_record: - if (verbose && (outputbufferpos > 4)) - printf(" misread"); - phaselocked = false; - return false; + printf("\n % 8.3fms [0x%05x] ", + (double)elapsed_time / 1e6, cursor); + else + putchar('.'); } static void decode_track_cb(int track, int side, const struct fluxmap* fluxmap) { printf("Track %02d side %d: ", track, side); - inputbuffer = fluxmap->intervals; - inputlen = fluxmap->bytes; + nanoseconds_t clock_period = fluxmap_guess_clock(fluxmap); + printf("% 4.1fus ", (double)clock_period/(double)1000); + + /* On MFM, the data clock is half the detected clock (which is the cell length). */ + clock_period /= 2; + struct encoding_buffer* decoded = fluxmap_decode(fluxmap, clock_period); + cursor = 0; - elapsed_ticks = 0; + uint64_t inputfifo = 0; + bool reading = false; int record = 0; - while (cursor < inputlen) + while (cursor < decoded->length_pulses) { - if (!phaselocked) + elapsed_time = cursor * decoded->pulselength_ns; + bool bit = decoded->bitmap[cursor++]; + inputfifo = (inputfifo << 1) | bit; + + /* + * The IAM record, which is the first one on the disk (and is optional), uses + * a distorted 0xC2 0xC2 0xC2 marker to identify it. Unfortunately, if this is + * shifted out of phase, it becomes a legal encoding, so if we're looking at + * real data we can't honour this. + * + * 0xC2 is: + * data: 1 1 0 0 0 0 1 0 + * mfm: 01 01 00 10 10 10 01 00 = 0x5254 + * special: 01 01 00 10 00 10 01 00 = 0x5224 + * ^^^^ + * shifted: 10 10 01 00 01 00 10 0. = legal, and might happen in real data + * + * Therefore, when we've read the marker, the input fifo will contain + * 0xXXXX522252225222. + * + * All other records use 0xA1 as a marker: + * + * 0xA1 is: + * data: 1 0 1 0 0 0 0 1 + * mfm: 01 00 01 00 10 10 10 01 = 0x44A9 + * special: 01 00 01 00 10 00 10 01 = 0x4489 + * ^^^^^ + * shifted: 10 00 10 01 00 01 00 1 + * + * When this is shifted out of phase, we get an illegal encoding (you + * can't do 10 00). So, if we ever see 0x448944894489 in the input + * fifo, we know we've landed at the beginning of a new record. + */ + + uint64_t masked = inputfifo & 0xFFFFFFFFFFFFLL; + if ((!reading && (masked == 0x522452245224LL)) || (masked == 0x448944894489LL)) { - while (cursor < inputlen) - { - /* Somewhere in the bitstream there'll be a sync sequence of 16 0 bytes - * followed by a special index byte, beginning with a 1 bit. - * - * These zeroes will be encoded as 10 10 10 10..., so forming a nice - * simple signal that should be easy to detect. This routine scans the - * bitstream until it finds one of these windows, and sets the clock - * accordingly. Remember that the routine can be easily spoofed by bad - * data, so you need to check for the marker byte afterwards. - * - * ...btw, the standard fill byte is 0x4e: - * - * 0 1 0 0 1 1 1 0 - * 10 01 00 10 01 01 01 00 - * - * That's four medium transitions vs two short ones. So we know we're going - * to miscalculate the clock the first time round. - */ + log_record(); - clock_period = fluxmap_seek_clock(fluxmap, &cursor, 16); + if (reading) + sql_write_record(outdb, track, side, record++, outputbuffer, outputbufferpos); - /* - * Okay, this looks good. We'll assume this is clock/2 --- 250kHz - * for HD floppies; this is one short transition. We're also going - * to assume that this was a 0 bit and set the phase, god help us. - */ + if (!reading) + memcpy(outputbuffer, "\xc2\xc2\xc2", 3); + else + memcpy(outputbuffer, "\xa1\xa1\xa1", 3); - double short_time = clock_period; - double medium_time = short_time * 1.5; - double long_time = short_time * 2.0; - - period0 = short_time - short_time * CLOCK_ERROR_BOUNDS; - period1 = (short_time + medium_time) / 2.0; - period2 = (medium_time + long_time) / 2.0; - period3 = long_time + long_time * CLOCK_ERROR_BOUNDS; - phase = true; - phaselocked = true; - has_queued = false; - - while (phaselocked && (cursor < inputlen)) - { - if (read_bit()) - goto found_byte; - } - } - found_byte:; - fifo = 1; - bitcount = 1; - outputbufferpos = 0; + reading = true; + outputbufferpos = 3; + bitcount = 0; + phase = 0; } - - if (process_byte(read_byte())) + else if (reading) { - sql_write_record(outdb, track, side, record, outputbuffer, outputbufferpos); - record++; + if (phase) + write_bit(bit); + phase = !phase; } } + if (reading) + sql_write_record(outdb, track, side, record++, outputbuffer, outputbufferpos); + if (verbose) printf("\n "); printf(" = %d records\n", record); diff --git a/cmd_testpattern.c b/cmd_testpattern.c index 1958e5cd..4c6d6871 100644 --- a/cmd_testpattern.c +++ b/cmd_testpattern.c @@ -81,13 +81,13 @@ static void open_file(void) static void write_pulsetrain(struct encoding_buffer* buffer, int cursor_ms, int length_ms, int width_us) { - int cursor_us = cursor_ms*1000; - int length_us = length_ms*1000; - while (length_us > 0) + int cursor_ns = cursor_ms*1000000; + int length_ns = length_ms*1000000; + while (length_ns > 0) { - encoding_buffer_pulse(buffer, cursor_us); - length_us -= width_us; - cursor_us += width_us; + encoding_buffer_pulse(buffer, cursor_ns); + length_ns -= width_us*1000; + cursor_ns += width_us*1000; } } @@ -101,7 +101,7 @@ void cmd_testpattern(char* const* argv) if (start_track > end_track) error("writing to track %d to track %d makes no sense", start_track, end_track); - struct encoding_buffer* buffer = create_encoding_buffer(track_length_ms*1000); + struct encoding_buffer* buffer = create_encoding_buffer(1000, track_length_ms*1000); int cursor_ms = 0; while (cursor_ms < track_length_ms) diff --git a/encoder.c b/encoder.c index 5f2b7d2f..13ad7ddc 100644 --- a/encoder.c +++ b/encoder.c @@ -2,11 +2,12 @@ #define MAX_INTERVAL_US (128/(TICK_FREQUENCY/1000000)) -struct encoding_buffer* create_encoding_buffer(int length_us) +struct encoding_buffer* create_encoding_buffer(int pulselength_ns, int length_pulses) { struct encoding_buffer* buffer = calloc(1, sizeof(*buffer)); - buffer->length_us = length_us; - buffer->bitmap = calloc(1, length_us); + buffer->pulselength_ns = pulselength_ns; + buffer->length_pulses = length_pulses; + buffer->bitmap = calloc(1, length_pulses); return buffer; } @@ -16,10 +17,11 @@ void free_encoding_buffer(struct encoding_buffer* buffer) free(buffer); } -void encoding_buffer_pulse(struct encoding_buffer* buffer, int timestamp_us) +void encoding_buffer_pulse(struct encoding_buffer* buffer, int timestamp_ns) { - if (timestamp_us < buffer->length_us) - buffer->bitmap[timestamp_us] = 1; + int pulse = timestamp_ns / buffer->pulselength_ns; + if (pulse < buffer->length_pulses) + buffer->bitmap[pulse] = 1; } struct fluxmap* encoding_buffer_encode(const struct encoding_buffer* buffer) @@ -28,12 +30,18 @@ struct fluxmap* encoding_buffer_encode(const struct encoding_buffer* buffer) int lastpulse = 0; int cursor = 1; - while (cursor < buffer->length_us) + while (cursor < buffer->length_pulses) { if ((buffer->bitmap[cursor]) || (cursor-lastpulse == MAX_INTERVAL_US)) { - uint8_t interval = (cursor - lastpulse) * (TICK_FREQUENCY/1000000); - fluxmap_append_intervals(fluxmap, &interval, 1); + int delta_ticks = (cursor - lastpulse) / TICKS_PER_NS; + while (delta_ticks >= 0xfe) + { + fluxmap_append_interval(fluxmap, 0xfe); + delta_ticks -= 0xfe; + } + + fluxmap_append_interval(fluxmap, delta_ticks); lastpulse = cursor; } cursor++; diff --git a/fluxmap.c b/fluxmap.c index 73cb702a..bee86256 100644 --- a/fluxmap.c +++ b/fluxmap.c @@ -48,6 +48,11 @@ void fluxmap_append_intervals(struct fluxmap* fluxmap, const uint8_t* intervals, fluxmap->length_us = fluxmap->length_ticks / (TICK_FREQUENCY / 1000000); } +void fluxmap_append_interval(struct fluxmap* fluxmap, uint8_t interval) +{ + fluxmap_append_intervals(fluxmap, &interval, 1); +} + int fluxmap_seek_clock(const struct fluxmap* fluxmap, int* cursor, int pulses) { int count = 0; @@ -80,6 +85,48 @@ int fluxmap_seek_clock(const struct fluxmap* fluxmap, int* cursor, int pulses) return 1; } +/* + * Tries to guess the clock by finding the smallest common interval. + * Returns nanoseconds. + */ +nanoseconds_t fluxmap_guess_clock(const struct fluxmap* fluxmap) +{ + uint32_t buckets[256] = {}; + for (int i=0; ibytes; i++) + buckets[fluxmap->intervals[i]]++; + + int peaklo = 0; + while (peaklo < 256) + { + if (buckets[peaklo] > 100) + break; + peaklo++; + } + + int peakmaxindex = peaklo; + int peakmaxvalue = buckets[peakmaxindex]; + int peakhi = peaklo; + while (peakhi < 256) + { + uint32_t v = buckets[peakhi]; + if (buckets[peakhi] < 50) + break; + if (v > peakmaxvalue) + { + peakmaxindex = peakhi; + peakmaxvalue = v; + } + peakhi++; + } + + /* + * Okay, peakmaxindex should now be a good candidate for the (or a) clock. + * How this maps onto the actual clock rate depends on the encoding. + */ + + return peakmaxindex * NS_PER_TICK; +} + void fluxmap_precompensate(struct fluxmap* fluxmap, int threshold_ticks, int amount_ticks) { uint8_t junk = 0xff; @@ -103,3 +150,34 @@ void fluxmap_precompensate(struct fluxmap* fluxmap, int threshold_ticks, int amo } } } + +struct encoding_buffer* fluxmap_decode(const struct fluxmap* fluxmap, nanoseconds_t clock_period) +{ + int pulses = (fluxmap->length_us*1000) / clock_period; + nanoseconds_t lower_threshold = clock_period * 0.75; + + struct encoding_buffer* buffer = create_encoding_buffer(clock_period, pulses); + int count = 0; + int cursor = 0; + nanoseconds_t timestamp = 0; + for (;;) + { + while (timestamp < lower_threshold) + { + if (cursor >= fluxmap->bytes) + goto abort; + uint8_t interval = fluxmap->intervals[cursor++]; + timestamp += interval * NS_PER_TICK; + } + + int clocks = (timestamp + clock_period/2) / clock_period; + count += clocks; + if (count >= buffer->length_pulses) + goto abort; + buffer->bitmap[count] = true; + timestamp = 0; + } +abort: + + return buffer; +} diff --git a/globals.h b/globals.h index 491d7b1f..6e13c3a1 100644 --- a/globals.h +++ b/globals.h @@ -16,16 +16,19 @@ #include "protocol.h" +typedef int nanoseconds_t; /* maximum value is over 2s, which is fine */ + struct encoding_buffer { - int length_us; - uint8_t* bitmap; + int pulselength_ns; + int length_pulses; + bool* bitmap; }; -extern struct encoding_buffer* create_encoding_buffer(int length_us); +extern struct encoding_buffer* create_encoding_buffer(int pulselength_ns, int length_pulses); extern void free_encoding_buffer(struct encoding_buffer* buffer); -extern void encoding_buffer_pulse(struct encoding_buffer* buffer, int timestamp_us); +extern void encoding_buffer_pulse(struct encoding_buffer* buffer, int timestamp_ns); extern struct fluxmap* encoding_buffer_encode(const struct encoding_buffer* buffer); struct fluxmap @@ -42,8 +45,11 @@ extern void free_fluxmap(struct fluxmap* fluxmap); extern struct fluxmap* copy_fluxmap(const struct fluxmap* fluxmap); extern void fluxmap_clear(struct fluxmap* fluxmap); extern void fluxmap_append_intervals(struct fluxmap* fluxmap, const uint8_t* intervals, int count); +extern void fluxmap_append_interval(struct fluxmap* fluxmap, uint8_t interval); extern int fluxmap_seek_clock(const struct fluxmap* fluxmap, int* cursor, int pulses); +extern nanoseconds_t fluxmap_guess_clock(const struct fluxmap* fluxmap); extern void fluxmap_precompensate(struct fluxmap* fluxmap, int threshold_ticks, int amount_ticks); +extern struct encoding_buffer* fluxmap_decode(const struct fluxmap* fluxmap, int clock_ns); extern void error(const char* message, ...); extern double gettime(void); @@ -65,9 +71,9 @@ extern void cmd_usbbench(char* const* argv); extern void cmd_read(char* const* argv); extern void cmd_write(char* const* argv); extern void cmd_mfmdecode(char* const* argv); -extern void cmd_fmdecode(char* const* argv); extern void cmd_testpattern(char* const* argv); extern void cmd_fluxdump(char* const* argv); extern void cmd_calibrate(char* const* argv); +extern void cmd_getclock(char* const* argv); #endif diff --git a/main.c b/main.c index 166f7056..0f618ae6 100644 --- a/main.c +++ b/main.c @@ -76,14 +76,14 @@ int main(int argc, char* const* argv) cmd_write(argv); else if (strcmp(argv[0], "mfmdecode") == 0) cmd_mfmdecode(argv); - else if (strcmp(argv[0], "fmdecode") == 0) - cmd_fmdecode(argv); else if (strcmp(argv[0], "testpattern") == 0) cmd_testpattern(argv); else if (strcmp(argv[0], "fluxdump") == 0) cmd_fluxdump(argv); else if (strcmp(argv[0], "calibrate") == 0) cmd_calibrate(argv); + else if (strcmp(argv[0], "getclock") == 0) + cmd_getclock(argv); else syntax_error(); diff --git a/protocol.h b/protocol.h index eb91b014..bebd0fd2 100644 --- a/protocol.h +++ b/protocol.h @@ -28,11 +28,13 @@ enum FRAME_SIZE = 64, TICK_FREQUENCY = 12000000, TICKS_PER_US = TICK_FREQUENCY / 1000000, - NS_PER_TICK = 1000000000 / TICK_FREQUENCY, + TICKS_PER_NS = TICK_FREQUENCY / 1000, PRECOMPENSATION_THRESHOLD_TICKS = (int)(2.25 * TICKS_PER_US), }; +#define NS_PER_TICK ((double)1000000000 / (double)TICK_FREQUENCY) + enum { F_FRAME_ERROR = 0, /* any_frame */