diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README | 27 | ||||
| -rw-r--r-- | src/common.h | 1 | ||||
| -rw-r--r-- | src/fodtmf-send.c | 557 | ||||
| -rw-r--r-- | src/goertzel.c | 72 | ||||
| -rw-r--r-- | src/goertzel.h | 18 |
6 files changed, 333 insertions, 343 deletions
@@ -15,4 +15,3 @@ _/ *.su *.gch /config.status - @@ -1,5 +1,5 @@ NAME - fodtmf - Transfer files over audio. + fodtmf - Transfer files over audio ETYMOLOGY File over DTMF (dual-tone multi-frequency signaling) @@ -16,13 +16,13 @@ DESCRIPTION The message will be enhanced with hamming code for error correction, and optionally with - redundant frequencies to help combat noice in + redundant frequencies to help combat noise in the environment. At the end of the transfer, an 'end of transmission' - byte will be send. If the message is cancelled, a - 'cancel' byte will be send. The end of the message - will be padded with this byte until a full error + byte will be sent. If the message is cancelled, a + 'cancel' byte will be sent. The end of the message + will be padded with this byte until a full error- correcting code has been constructed. For any 'data link escape' byte, any 'end of @@ -37,7 +37,7 @@ OPTIONS -f MULTIPLE In addition to any tone with the frequency F, also send, simultaneously a tone with the - frquencies F times MULTIPLE. This is adds + frequencies F times MULTIPLE. This adds redundancy to improve the reliability of the transfer. Redundant tones are not added by default because they make the tones less @@ -51,17 +51,17 @@ OPTIONS calculated by 2^N - 1, and the number of data bytes per code is calculated by 2^N - N - 1. - There are two exceptional values, that cases + There are two exceptional values, that cause the message to be transferred without any hamming code for error correction: 0 Do not include any redundancy at all, apart from any specified using the - flag -p. + flag -f. 1 Only one redundancy byte per code, apart from any specified using the - flag -p. + flag -f. This value must be non-negative. @@ -88,8 +88,8 @@ OPTIONS back. For fodtmf-recv: - Write a list, of all codes (by index) that - where not properly received and could not + Write a list of all codes (by index) that + were not properly received and could not be corrected, to file descriptor FD. -s FD @@ -107,14 +107,14 @@ NOTES xz -e9 < your-file | fodtmf-send ## sender fodtmf-recv | xz -d > your-file ## receiver - But note well, which compresion algorithm is most + But note well, which compression algorithm is most efficient depends on the file. FUTURE DIRECTIONS afodtmf (Advanced File over DTMF) will be written. It will make the computers communicate with each other bidirectionally (limited to one-to-one communication) - to repair errors that could not be correct using + to repair errors that could not be corrected using the error-correcting code. RATIONALE @@ -122,4 +122,3 @@ RATIONALE SEE ALSO scp(1), nc(1) - diff --git a/src/common.h b/src/common.h index d2fdf1b..4097f20 100644 --- a/src/common.h +++ b/src/common.h @@ -36,4 +36,3 @@ * This is the 'End of transmission' character. */ #define CHAR_END 0x04 - diff --git a/src/fodtmf-send.c b/src/fodtmf-send.c index 0c3709e..2fbe70c 100644 --- a/src/fodtmf-send.c +++ b/src/fodtmf-send.c @@ -51,12 +51,12 @@ /** * The process's name. */ -static const char* argv0; +static const char *argv0; /** * Audio descriptor. */ -static snd_pcm_t* sound_handle = NULL; +static snd_pcm_t *sound_handle = NULL; /** * Audio playback buffer for each nibble. @@ -70,7 +70,7 @@ static int parity = 3; /** * Whether to add an additional parity tone (of all - * data tones) in addition to those numerated by `parity`. + * data tones) in addition to those enumerated by `parity`. */ static int use_extra_parity = 0; @@ -87,12 +87,12 @@ static double redundant_freq_mul = 0; /** * Data buffer used for error correcting code support. */ -static int* data; +static int *data; /** * Code buffer used for error correcting code support. */ -static int* code; +static int *code; /** * The number elements that can be stored in `data`. @@ -109,7 +109,7 @@ static int code_n; * rather than sending it to the audio subsystem. * * This is available for developers of the receiver. - * It is not intend to be used for anything else. + * It is not intended to be used for anything else. */ static int output_fd = -1; @@ -121,9 +121,10 @@ static int output_fd = -1; * * @param signo The received signal. */ -static void signoop(int signo) +static void +signoop(int signo) { - (void) signo; + (void) signo; } @@ -134,44 +135,48 @@ static void signoop(int signo) * @param low Output parameter for the lower frequency. * @param high Output parameter for the higher frequency. */ -static void get_freq(int nibble, int* low, int* high) +static void +get_freq(int nibble, int *low, int *high) { - static const int LOW[] = { 697, 770, 852, 941 }; - static const int HIGH[] = { 1209, 1336, 1477, 1663 }; - - *low = LOW[(nibble >> 0) & 0x03]; - *high = HIGH[(nibble >> 2) & 0x03]; + static const int LOW[] = { 697, 770, 852, 941 }; + static const int HIGH[] = { 1209, 1336, 1477, 1663 }; + + *low = LOW[(nibble >> 0) & 0x03]; + *high = HIGH[(nibble >> 2) & 0x03]; } /** * Initialise to before for each nibble. */ -static void init_buffers(void) +static void +init_buffers(void) { #define GENERATE_TONE(tone) sin(2 * M_PI * ((double)i / (SAMPLE_RATE / (tone)))) - - size_t i; - UTYPE* buffer; - int j, low, high; - - for (j = 0; j < 16; j++) - { - buffer = buffers[j]; - get_freq(j, &low, &high); - - if (use_redundant_freq) - for (i = 0; i < N; i++) - buffer[i] = (GENERATE_TONE(low) + - GENERATE_TONE(high) + - GENERATE_TONE(low * redundant_freq_mul) + - GENERATE_TONE(high * redundant_freq_mul)) * - (SMAX / 4) - SMIN; - else - for (i = 0; i < N; i++) - buffer[i] = (GENERATE_TONE(low) + GENERATE_TONE(high)) * - (SMAX / 2) - SMIN; - } + + size_t i; + UTYPE *buffer; + int j, low, high; + + for (j = 0; j < 16; j++) { + buffer = buffers[j]; + get_freq(j, &low, &high); + + if (use_redundant_freq) { + for (i = 0; i < N; i++) { + buffer[i] = (GENERATE_TONE(low) + + GENERATE_TONE(high) + + GENERATE_TONE(low * redundant_freq_mul) + + GENERATE_TONE(high * redundant_freq_mul)) * + (SMAX / 4) - SMIN; + } + } else { + for (i = 0; i < N; i++) { + buffer[i] = (GENERATE_TONE(low) + GENERATE_TONE(high)) * + (SMAX / 2) - SMIN; + } + } + } } @@ -181,40 +186,38 @@ static void init_buffers(void) * @param n The nibble. * @return 0 on success, -1 on error. */ -static int send_nibble(int n) +static int +send_nibble(int n) { - UTYPE* buffer = buffers[n]; - snd_pcm_sframes_t frames; - int r; - - if (output_fd >= 0) - { - char* buf = (char*)buffer; - size_t ptr = 0; - ssize_t wrote; - while (ptr < N) - { - wrote = write(output_fd, buf + ptr, N - ptr); - if (wrote < 0) - return -1; - ptr += (size_t)write; + UTYPE *buffer = buffers[n]; + snd_pcm_sframes_t frames; + int r; + + if (output_fd >= 0) { + char *buf = (char *)buffer; + size_t ptr = 0; + ssize_t wrote; + while (ptr < N) { + wrote = write(output_fd, buf + ptr, N - ptr); + if (wrote < 0) + return -1; + ptr += (size_t)wrote; + } + return 0; } - return 0; - } - - r = frames = snd_pcm_writei(sound_handle, buffer, N); - if (frames < 0) - r = frames = snd_pcm_recover(sound_handle, r, 0 /* do not print error reason? */); - if (r < 0) - { - fprintf(stderr, "%s: snd_pcm_writei: %s\n", argv0, snd_strerror(r)); - errno = 0; - return -1; - } - if ((r > 0) && ((size_t)r < N)) - printf("%s: short write: expected %zu, wrote %zu\n", argv0, N, (size_t)frames); - - return 0; + + r = frames = snd_pcm_writei(sound_handle, buffer, N); + if (frames < 0) + r = frames = snd_pcm_recover(sound_handle, r, 0 /* do not print error reason? */); + if (r < 0) { + fprintf(stderr, "%s: snd_pcm_writei: %s\n", argv0, snd_strerror(r)); + errno = 0; + return -1; + } + if (r > 0 && (size_t)r < N) + printf("%s: short write: expected %zu, wrote %zu\n", argv0, N, (size_t)frames); + + return 0; } @@ -224,14 +227,15 @@ static int send_nibble(int n) * @param c The byte. * @return 0 on success, -1 on error. */ -static int send_byte(int c) +static int +send_byte(int c) { #ifdef DEBUG - fprintf(stderr, "%s: sending byte: %i\n", argv0, c); + fprintf(stderr, "%s: sending byte: %i\n", argv0, c); #endif - if (send_nibble((c >> 0) & 0x0F)) return -1; - if (send_nibble((c >> 4) & 0x0F)) return -1; - return 0; + if (send_nibble((c >> 0) & 0x0F)) return -1; + if (send_nibble((c >> 4) & 0x0F)) return -1; + return 0; } @@ -245,68 +249,67 @@ static int send_byte(int c) * * @param c The byte, the negative of that byte (intended * only for `CHAR_END` and `CHAR_CANCEL`) to fill - * the remained of the buffer with the byte. + * the remainder of the buffer with the byte. * Note that if a negative value is used, it is - * no necessary that anything will happen. + * not necessary that anything will happen. * @return 0 on success, -1 on error. */ -static int send_byte_with_ecc(int c) +static int +send_byte_with_ecc(int c) { - static int ptr = 0; - int i, j, d, p; - - if (parity < 2) - { - if (c < 0) + static int ptr = 0; + int i, j, d, p; + + if (parity < 2) { + if (c < 0) + return 0; + if (send_byte(c)) + return -1; + if (parity) + if (send_byte(c)) + return -1; + if (use_extra_parity) + if (send_byte(c)) + return -1; + return 0; + } + + /* Fill buffer. */ + if (c < 0) { + if (ptr > 0) + while (ptr < data_n) + data[ptr++] = -c; + } + else { + data[ptr++] = c; + } + + /* Is it full? */ + if (ptr < data_n) + return 0; + ptr = 0; + + /* Hamming code. */ + memset(code, 0, code_n * sizeof(*code)); + for (i = 1, j = 0; i <= (1 << parity) - 1; i++) { + if ((i & -i) == i) + continue; + for (d = i, p = 0; d; d >>= 1, p++) + if (d & 1) + code[(1 << p) - 1] ^= data[j]; + code[i - 1] = data[j]; + j++; + } + if (use_extra_parity) + for (i = 0; i < data_n; i++) + code[(1 << parity) - 1] ^= data[i]; + + /* Transmit. */ + for (i = 0; i < code_n; i++) + if (send_byte(code[i])) + return -1; + return 0; - if (send_byte(c)) - return -1; - if (parity) - if (send_byte(c)) - return -1; - if (use_extra_parity) - if (send_byte(c)) - return -1; - return 0; - } - - /* Fill buffer. */ - if (c < 0) - { - if (ptr > 0) - while (ptr < data_n) - data[ptr++] = -c; - } - else - data[ptr++] = c; - - /* Is it full? */ - if (ptr < data_n) - return 0; - ptr = 0; - - /* Hamming code. */ - memset(code, 0, code_n * sizeof(*code)); - for (i = 1, j = 0; i <= (1 << parity) - 1; i++) - { - if ((i & -i) == i) - continue; - for (d = i, p = 0; d; d >>= 1, p++) - if (d & 1) - code[(1 << p) - 1] ^= data[j]; - code[i - 1] = data[j]; - j++; - } - if (use_extra_parity) - for (i = 0; i < data_n; i++) - code[(1 << parity) - 1] ^= data[i]; - - /* Transmit. */ - for (i = 0; i < code_n; i++) - if (send_byte(code[i])) - return -1; - - return 0; } @@ -317,26 +320,26 @@ static int send_byte_with_ecc(int c) * @param n The number of bytes in the chunk. * @return 0 on success, -1 on failure. */ -static int send_chunk(char* buf, size_t n) +static int +send_chunk(char *buf, size_t n) { - size_t i; - int c; - - for (i = 0; i < n; i++) - { - c = buf[i]; - if ((c == CHAR_ESCAPE) || (c == CHAR_CANCEL) || (c == CHAR_END)) - if (send_byte_with_ecc(CHAR_ESCAPE)) - goto qfile; - if (send_byte_with_ecc(c)) - goto qfile; - } - - return 0; - qfile: - errno = 0; - fail: - return -1; + size_t i; + int c; + + for (i = 0; i < n; i++) { + c = buf[i]; + if (c == CHAR_ESCAPE || c == CHAR_CANCEL || c == CHAR_END) + if (send_byte_with_ecc(CHAR_ESCAPE)) + goto qfile; + if (send_byte_with_ecc(c)) + goto qfile; + } + + return 0; +qfile: + errno = 0; +fail: + return -1; } @@ -345,23 +348,23 @@ static int send_chunk(char* buf, size_t n) * * @return 0 on success, -1 on failure. */ -static int send_file(void) +static int +send_file(void) { - char buf[1024]; - ssize_t n; - - for (;;) - { - n = read(STDIN_FILENO, buf, sizeof(buf)); - if (n < 0) goto fail; - if (n == 0) break; - if (send_chunk(buf, (size_t)n)) - goto fail; - } - - return 0; - fail: - return -1; + char buf[1024]; + ssize_t n; + + for (;;) { + n = read(STDIN_FILENO, buf, sizeof(buf)); + if (n < 0) goto fail; + if (n == 0) break; + if (send_chunk(buf, (size_t)n)) + goto fail; + } + + return 0; +fail: + return -1; } @@ -370,39 +373,39 @@ static int send_file(void) * * @return 0 on success, -1 on failure. */ -static int send_term(void) +static int +send_term(void) { - char* buf = NULL; - size_t size = 0; - size_t ptr = 0; - ssize_t n; - void* new; - int saved_errno; - - for (;;) - { - if (ptr == size) - { - size = size ? (size << 1) : 128; - new = realloc(buf, size); - if (new == NULL) - goto fail; - buf = new; + char *buf = NULL; + size_t size = 0; + size_t ptr = 0; + ssize_t n; + void *new; + int saved_errno; + + for (;;) { + if (ptr == size) { + size = size ? (size << 1) : 128; + new = realloc(buf, size); + if (!new) + goto fail; + buf = new; + } + n = read(STDIN_FILENO, buf + ptr, size - ptr); + if (n < 0) goto fail; + if (n == 0) break; + ptr += (size_t)n; } - n = read(STDIN_FILENO, buf + ptr, size - ptr); - if (n < 0) goto fail; - if (n == 0) break; - ptr += (size_t)n; - } - if (send_chunk(buf, ptr)) - goto fail; + if (send_chunk(buf, ptr)) + goto fail; - return 0; - fail: - saved_errno = errno; - free(buf); - errno = saved_errno; - return -1; + return 0; + +fail: + saved_errno = errno; + free(buf); + errno = saved_errno; + return -1; } @@ -413,91 +416,83 @@ static int send_term(void) * @param argv Command line arguments. * @return 0 on success, 1 on failure. */ -int main(int argc, char* argv[]) +int +main(int argc, char *argv[]) { - struct sigaction act; - int r, rc = 1; - - /* Parse command line. */ - argv0 = argc ? argv[0] : ""; - for (;;) - { - r = getopt (argc, argv, "f:pr:o:"); - if (r == -1) - break; - else if (r == 'f') use_redundant_freq = 1, redundant_freq_mul = atof(optarg); - else if (r == 'p') use_extra_parity = 1; - else if (r == 'r') parity = atoi(optarg); - else if (r == 'o') output_fd = atoi(optarg); - else if (r != '?') - abort(); - } - - /* Set up signal handling. */ - siginterrupt(SIGTERM, 1); - siginterrupt(SIGQUIT, 1); - siginterrupt(SIGINT, 1); - siginterrupt(SIGHUP, 1); - sigemptyset(&(act.sa_mask)); - act.sa_handler = signoop; - act.sa_flags = 0; - sigaction(SIGTERM, &act, NULL); - sigaction(SIGQUIT, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigprocmask(SIG_SETMASK, &(act.sa_mask), NULL); - - /* Generate the tones to play. */ - init_buffers(); - /* Generate buffers for error correcting code. */ - data_n = (1 << parity) - parity - 1; - code_n = (1 << parity) - 1 + use_extra_parity; - data = alloca(data_n * sizeof(*data)); - code = alloca(code_n * sizeof(*code)); + struct sigaction act; + int r, rc = 1; - /* Set up audio. */ - if (output_fd >= 0) - goto no_audio; - r = snd_pcm_open(&sound_handle, "default", SND_PCM_STREAM_PLAYBACK, 0); - if (r < 0) - return fprintf(stderr, "%s: snd_pcm_open: %s\n", *argv, snd_strerror(r)), 1; - if (sound_handle == NULL) - perror("snd_pcm_open"); - /* Configure audio. */ - r = snd_pcm_set_params(sound_handle, FORMAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1 /* channels */, - SAMPLE_RATE, 1 /* allow resampling? */, LATENCY); - if (r < 0) - return fprintf(stderr, "%s: snd_pcm_set_params: %s\n", *argv, snd_strerror(r)), 1; - no_audio: - - /* Send message. */ - if (isatty(STDIN_FILENO)) - { - if (send_term()) - goto fail; - } - else - if (send_file()) - goto fail; - - /* Mark end of transmission. */ - if (send_byte_with_ecc(CHAR_END)) goto cleanup; - if (send_byte_with_ecc(-CHAR_END)) goto cleanup; - - /* Done! */ - rc = 0; - goto cleanup; - fail: - if (errno) - perror(argv0); - cleanup: - /* Mark aborted transmission. */ - if (rc) - { - send_byte_with_ecc(CHAR_CANCEL); - send_byte_with_ecc(-CHAR_CANCEL); - } - if (output_fd >= 0) - snd_pcm_close(sound_handle); - return rc; -} + /* Parse command line. */ + argv0 = argc ? argv[0] : ""; + for (;;) { + r = getopt (argc, argv, "f:pr:o:"); + if (r == -1) + break; + else if (r == 'f') use_redundant_freq = 1, redundant_freq_mul = atof(optarg); + else if (r == 'p') use_extra_parity = 1; + else if (r == 'r') parity = atoi(optarg); + else if (r == 'o') output_fd = atoi(optarg); + else if (r != '?') + abort(); + } + + /* Set up signal handling. */ + siginterrupt(SIGTERM, 1); + siginterrupt(SIGQUIT, 1); + siginterrupt(SIGINT, 1); + siginterrupt(SIGHUP, 1); + sigemptyset(&(act.sa_mask)); + act.sa_handler = signoop; + act.sa_flags = 0; + sigaction(SIGTERM, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigprocmask(SIG_SETMASK, &(act.sa_mask), NULL); + + /* Generate the tones to play. */ + init_buffers(); + /* Generate buffers for error correcting code. */ + data_n = (1 << parity) - parity - 1; + code_n = (1 << parity) - 1 + use_extra_parity; + data = alloca(data_n * sizeof(*data)); + code = alloca(code_n * sizeof(*code)); + + /* Set up audio. */ + if (output_fd >= 0) + goto no_audio; + r = snd_pcm_open(&sound_handle, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (r < 0) + return fprintf(stderr, "%s: snd_pcm_open: %s\n", *argv, snd_strerror(r)), 1; + if (sound_handle == NULL) + perror("snd_pcm_open"); + /* Configure audio. */ + r = snd_pcm_set_params(sound_handle, FORMAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1 /* channels */, + SAMPLE_RATE, 1 /* allow resampling? */, LATENCY); + if (r < 0) + return fprintf(stderr, "%s: snd_pcm_set_params: %s\n", *argv, snd_strerror(r)), 1; +no_audio: + /* Send message. */ + if (isatty(STDIN_FILENO) ? send_term() : send_file()) + goto fail; + + /* Mark end of transmission. */ + if (send_byte_with_ecc(CHAR_END)) goto cleanup; + if (send_byte_with_ecc(-CHAR_END)) goto cleanup; + + /* Done! */ + rc = 0; + goto cleanup; +fail: + if (errno) + perror(argv0); +cleanup: + /* Mark aborted transmission. */ + if (rc) { + send_byte_with_ecc(CHAR_CANCEL); + send_byte_with_ecc(-CHAR_CANCEL); + } + if (output_fd >= 0) + snd_pcm_close(sound_handle); + return rc; +} diff --git a/src/goertzel.c b/src/goertzel.c index a4174be..21bdfbc 100644 --- a/src/goertzel.c +++ b/src/goertzel.c @@ -20,45 +20,45 @@ -void goertzel_init(struct goertzel_state* restrict state, double freq, double rate) +void +goertzel_init(struct goertzel_state *restrict state, double freq, double rate) { - memset(state, 0, *state); - state->k = 2 * cos(2 * M_PI * (freq / rate)); + memset(state, 0, sizeof(*state)); + state->k = 2 * cos(2 * M_PI * (freq / rate)); } -double goertzel(struct goertzel_state* restrict state, const uint32_t* restrict samples, size_t n) +double +goertzel(struct goertzel_state *restrict state, const uint32_t *restrict samples, size_t n) { - double power, samp, s; - size_t i; - - double p1 = state->prev1, p2 = state->prev2; - double k = state->k, totpower = state->power; - - for (i = 0; i < n; i++) - { - samp = (double)(samples[i]) / (double)UINT32_MAX; - samp = 2 * samp - 1; - - s = samp + k * p1 - p2; - p2 = p1; - p1 = s; - - power = p1 + p2; - power *= power; - power -= (k + 2) * p1 * p2; - - totpower += samp * samp; - } - - state->prev1 = p1; - state->prev2 = p2; - state->power = totpower; - staet->n += n; - - if (state->power == 0) - state->power = 1; - - return power / state->power / (double)(state->n); -} + double power, samp, s; + size_t i; + + double p1 = state->prev1, p2 = state->prev2; + double k = state->k, totpower = state->power; + + for (i = 0; i < n; i++) { + samp = (double)(samples[i]) / (double)UINT32_MAX; + samp = 2 * samp - 1; + + s = samp + k * p1 - p2; + p2 = p1; + p1 = s; + + power = p1 + p2; + power *= power; + power -= (k + 2) * p1 * p2; + totpower += samp * samp; + } + + state->prev1 = p1; + state->prev2 = p2; + state->power = totpower; + state->n += n; + + if (state->power == 0) + state->power = 1; + + return power / state->power / (double)(state->n); +} diff --git a/src/goertzel.h b/src/goertzel.h index 98aa4f2..809f69b 100644 --- a/src/goertzel.h +++ b/src/goertzel.h @@ -19,16 +19,14 @@ -struct goertzel_state -{ - double prev1; - double prev2; - double power; - size_t n; - double k; +struct goertzel_state { + double prev1; + double prev2; + double power; + size_t n; + double k; }; -void goertzel_init(struct goertzel_state* restrict state, double freq, double rate); -double goertzel(struct goertzel_state* restrict state, const uint32_t* restrict samples, size_t n); - +void goertzel_init(struct goertzel_state *restrict state, double freq, double rate); +double goertzel(struct goertzel_state *restrict state, const uint32_t *restrict samples, size_t n); |
