diff options
author | Sebastian Muszytowski <sebastian@muszytowski.net> | 2016-01-14 21:11:29 +0100 |
---|---|---|
committer | Sebastian Muszytowski <sebastian@muszytowski.net> | 2016-01-14 21:11:29 +0100 |
commit | e63ecbb75dafeb19de9a0eea8a6b228f0752ee37 (patch) | |
tree | 248ce06ec2fbd5a88084412f61feb1c97d40c329 | |
parent | ff61107b89ddeee39a32a94182a4bdeeabebf7fb (diff) |
add tagsu-avr-modem from Rakettitiede Oy
-rw-r--r-- | modem.c | 95 | ||||
-rw-r--r-- | modem.h | 24 | ||||
-rw-r--r-- | utilities/modem.py | 111 |
3 files changed, 230 insertions, 0 deletions
@@ -0,0 +1,95 @@ +/* Name: modem.c + * + * Audio modem for Attiny85 & other AVR chips with modifications + * + * Author: Jari Tulilahti + * Copyright: 2014 Rakettitiede Oy + * License: LGPLv3, see COPYING, and COPYING.LESSER -files for more info + */ + +#include "modem.h" + +/* Ring buffer global variables */ +static volatile uint8_t modem_buffer_head = 0, modem_buffer_tail = 0; +static volatile uint8_t modem_buffer[MODEM_BUFFER_SIZE]; + +/* + * Returns number of available bytes in ringbuffer or 0 if empty + */ +uint8_t modem_buffer_available() { + return modem_buffer_head - modem_buffer_tail; +} + +/* + * Store 1 byte in ringbuffer + */ +static inline void modem_buffer_put(const uint8_t c) { + if (modem_buffer_available() != MODEM_BUFFER_SIZE) { + modem_buffer[modem_buffer_head++ % MODEM_BUFFER_SIZE] = c; + } +} + +/* + * Fetch 1 byte from ringbuffer + */ +uint8_t modem_buffer_get() { + uint8_t b = 0; + if (modem_buffer_available() != 0) { + b = modem_buffer[modem_buffer_tail++ % MODEM_BUFFER_SIZE]; + } + return b; +} + +/* + * Pin Change Interrupt Vector. This is The Modem. + */ +ISR(PCINT0_vect) { + /* Static variables instead of globals to keep scope inside ISR */ + static uint8_t modem_bit = 0; + static uint8_t modem_bitlen = 0; + static uint8_t modem_byte = 0; + + /* Read & Zero Timer/Counter 1 value */ + uint8_t modem_pulselen = MODEM_TIMER; + MODEM_TIMER = 0; + + /* + * Check if we received Start/Sync -pulse. + * Calculate bit signal length middle point from pulse. + * Return from ISR immediately. + */ + if (modem_pulselen > MODEM_SYNC_LEN) { + modem_bitlen = (modem_pulselen >> 2); + modem_bit = 0; + return; + } + + /* + * Shift byte and set high bit according to the pulse length. + * Long pulse = 1, Short pulse = 0 + */ + modem_byte = (modem_byte >> 1) | (modem_pulselen < modem_bitlen ? 0x00 : 0x80); + + /* Check if we received complete byte and store it in ring buffer */ + if (!(++modem_bit % 0x08)) { + modem_buffer_put(modem_byte); + } +} + +/* + * Start the modem by enabling Pin Change Interrupts & Timer + */ +void modem_init() { + /* Modem pin as input */ + MODEM_DDR &= ~(1 << MODEM_PIN); + + /* Enable Pin Change Interrupts and PCINT for MODEM_PIN */ + GIMSK |= (1 << PCIE); + PCMSK |= (1 << MODEM_PIN); + + /* Timer: TCCR1: CS10, CS11 and CS12 bits: 8MHz clock with Prescaler 64 = 125kHz timer clock */ + TCCR1 = (1 << CS10) | (1 << CS11) | (1 << CS12); + + /* Enable interrupts */ + sei(); +} @@ -0,0 +1,24 @@ +/* Name: modem.h + * Author: Jari Tulilahti + * Copyright: 2014 Rakettitiede Oy + * License: LGPLv3, see COPYING, and COPYING.LESSER -files for more info + */ + +#pragma once + +#include <avr/interrupt.h> +#include <stdlib.h> + +/* Modem ring buffer size must be power of 2 */ +#define MODEM_BUFFER_SIZE 4 + +/* Modem defines */ +#define MODEM_SYNC_LEN 42 +#define MODEM_TIMER TCNT1 +#define MODEM_PIN PCINT3 +#define MODEM_DDR DDRB + +/* Public funtions */ +uint8_t modem_buffer_available(); +uint8_t modem_buffer_get(); +void modem_init(); diff --git a/utilities/modem.py b/utilities/modem.py new file mode 100644 index 0000000..f2e2b1b --- /dev/null +++ b/utilities/modem.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python + +import sys, wave + +# +# "Modem" wav creator for "Next Gen" Tagsu Modem :) +# +# Author: Jari Tulilahti +# Copyright: 2014 Rakettitiede Oy +# License: LGPLv3, see COPYING and COPYING.LESSER -files for more info +# +# Usage: +# python modem.py <input_filename> [output_wav_filename] [samplerate] +# +# Default sample rate is 48000 +# Default output to stdout if no wav filename given +# +# Working sample rates are between 16000 - 48000: +# +# 16000 (~4000 bps) +# 22050 (~5500 bps) +# 24000 (~6000 bps) +# 32000 (~8000 bps) +# 44100 (~11025 bps) +# 48000 (~12000 bps) +# +# NOTICE: +# +# Actual speed depends of the data, as zeroes and ones take +# different amount of samples, the more zeros, the faster the speed. +# Average bps has been calculated using data "0101010101..." +# + +class modem: + + bits = [[3 * chr(0), 5 * chr(0)], [3 * chr(255), 5 * chr(255)]] + sync = [17 * chr(0), 17 * chr(255)] + hilo = 0 + + # Nothing here + def __init__(self): + pass + + # Generate one sync-pulse + def syncsignal(self): + self.hilo ^= 1 + return self.sync[self.hilo] + + # Decode bits to modem signals + def modemcode(self, byte): + bleep = "" + for x in xrange(8): + self.hilo ^= 1 + bleep += self.bits[self.hilo][byte & 0x01] + byte >>= 1 + return bleep + + # Return <length> samples of silence + def silence(self, length): + return chr(127) * length + + +if len(sys.argv) < 2: + print """Usage: {0} <input_filename> [output_wav_filename] [samplerate] + + input_filename Data file as input to modem. + output_wav_filename Write output-wav to this file. If empty, output to STDOUT. + samplerate Sample rate between 16000 - 48000. Default is 48000. + """.format(sys.argv[0]) + sys.exit() + +sound = "" + +m = modem() +cnt = 0 + +# Add silence +# sound += m.silence(24000) + +# Add 4 sync signals to start +for x in xrange(4): + sound += m.syncsignal() + +# Send Actual data +f = open(sys.argv[1]) +for byte in f.read(): + sound += m.modemcode(ord(byte)) + + # Add counter + cnt += 1 + + # After every 10 bytes, send 2 sync signals + if cnt == 10: + for x in xrange(2): + sound += m.syncsignal() + cnt = 0 + +# End transmission with few sync-signals +for x in xrange(4): + sound += m.syncsignal() + +# Output the generated sound (no wav headers) +if len(sys.argv) <= 2: + print sound +else: + freq = int(sys.argv[3]) if len(sys.argv) > 3 else 48000 + wav = wave.open(sys.argv[2], 'wb') + wav.setparams((1, 1, freq, 0, "NONE", None)) + wav.writeframes(sound) + wav.close() + |