From fd56639a9e31467ba8d35cac81957510eb3c894c Mon Sep 17 00:00:00 2001 From: construidor Date: Thu, 9 Jan 2014 15:38:39 -0200 Subject: [PATCH 01/15] Added file.py. The file.py is basically the same as the send.py, with the difference that it creates a file .wav instead of reproducing the sound. example os use: bash> python2 file.py Welcome to quietnet. Use ctrl-c to exit > 123456789 > ^Cexited cleanly bash> aplay WaveTest.wav --- file.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 file.py diff --git a/file.py b/file.py new file mode 100644 index 0000000..5cd1c98 --- /dev/null +++ b/file.py @@ -0,0 +1,62 @@ +import quietnet +import options +import psk + +import wave + +CHANNELS = options.channels +RATE = options.rate +FREQ = options.freq +FREQ_OFF = 0 +FRAME_LENGTH = options.frame_length +DATASIZE = options.datasize + + +def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): + """ Takes a pattern and returns an audio buffer that encodes that pattern """ + # the key's middle value is the bit's value and the left and right bits are the bits before and after + # the buffers are enveloped to cleanly blend into each other + buffers = { + "000": quietnet.tone(off_freq, DATASIZE), + "100": quietnet.lenvelope(quietnet.tone(off_freq, DATASIZE)), + "001": quietnet.renvelope(quietnet.tone(off_freq, DATASIZE)), + "101": quietnet.envelope(quietnet.tone(off_freq, DATASIZE)), + "010": quietnet.envelope(quietnet.tone(on_freq, DATASIZE)), + "011": quietnet.lenvelope(quietnet.tone(on_freq, DATASIZE)), + "110": quietnet.renvelope(quietnet.tone(on_freq, DATASIZE)), + "111": quietnet.tone(on_freq, DATASIZE) + } + + last_bit = pattern[-1] + output_buffer = [] + for i in range(len(pattern)): + bit = pattern[i] + if i < len(pattern) - 1: + next_bit = pattern[i+1] + else: + next_bit = pattern[0] + output_buffer = output_buffer + buffers[last_bit + bit + next_bit] + last_bit = bit + return quietnet.pack_buffer(output_buffer) + +if __name__ == "__main__": + wav_file = wave.open("WaveTest.wav", "w") + wav_file.setparams((CHANNELS, 2, RATE, DATASIZE, "NONE" , "not compressed")) + + print "Welcome to quietnet. Use ctrl-c to exit" + + try: + # get user input and play message + while True: + print ">", + message = raw_input() + pattern = psk.encode(message) + buffer = make_buffer_from_bit_pattern(pattern, FREQ, FREQ_OFF) + for sample in buffer: + wav_file.writeframes(sample) + except KeyboardInterrupt: + # clean up our streams and exit + wav_file.close() + print "exited cleanly" + + From 85b78b2e2028d96c0473a7dad6159abec98dd079 Mon Sep 17 00:00:00 2001 From: construidor Date: Thu, 9 Jan 2014 18:38:52 -0200 Subject: [PATCH 02/15] Removed useless code --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc From 731f3ef7b3579b1ccb0059e20e408e6660404f95 Mon Sep 17 00:00:00 2001 From: construidor Date: Thu, 9 Jan 2014 18:43:08 -0200 Subject: [PATCH 03/15] Do not use DRUGS --- .gitignore | 1 + file.py | 21 +++++++++++++-------- quietnet.py | 10 ++-------- send.py | 21 +++++++++++++-------- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 0d20b64..5d570c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +WaveTest.wav diff --git a/file.py b/file.py index 5cd1c98..0d26fbf 100644 --- a/file.py +++ b/file.py @@ -16,15 +16,20 @@ def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): """ Takes a pattern and returns an audio buffer that encodes that pattern """ # the key's middle value is the bit's value and the left and right bits are the bits before and after # the buffers are enveloped to cleanly blend into each other + result0=quietnet.tone(off_freq, DATASIZE) + result1=quietnet.tone(on_freq, DATASIZE) + result2=quietnet.envelope(result0) + result3=quietnet.envelope(result1) + buffers = { - "000": quietnet.tone(off_freq, DATASIZE), - "100": quietnet.lenvelope(quietnet.tone(off_freq, DATASIZE)), - "001": quietnet.renvelope(quietnet.tone(off_freq, DATASIZE)), - "101": quietnet.envelope(quietnet.tone(off_freq, DATASIZE)), - "010": quietnet.envelope(quietnet.tone(on_freq, DATASIZE)), - "011": quietnet.lenvelope(quietnet.tone(on_freq, DATASIZE)), - "110": quietnet.renvelope(quietnet.tone(on_freq, DATASIZE)), - "111": quietnet.tone(on_freq, DATASIZE) + "000": result0, + "100": result2, + "001": result2, + "101": result2, + "010": result3, + "011": result3, + "110": result3, + "111": result1 } last_bit = pattern[-1] diff --git a/quietnet.py b/quietnet.py index 6b67346..d733a36 100644 --- a/quietnet.py +++ b/quietnet.py @@ -91,7 +91,7 @@ def tone(freq=400, datasize=4096, rate=44100, amp=8000.0): sine_list.append(int(samp*amp/2)) return sine_list -def envelope(in_data, left=True, right=True, rate=44100): +def envelope(in_data, rate=44100): half = float(len(in_data)) / 2 freq = math.pi / len(in_data) out_data = [] @@ -100,14 +100,8 @@ def envelope(in_data, left=True, right=True, rate=44100): samp = in_data[x] if x < half: samp = samp * math.sin(freq*x) - if x > half: + elif x > half: samp = samp * math.sin(freq*x) out_data.append(int(samp)) return out_data - -def lenvelope(in_data, rate=44100): - return envelope(in_data, left=True, right=False, rate=rate) - -def renvelope(in_data, rate=44100): - return envelope(in_data, left=False, right=True, rate=rate) diff --git a/send.py b/send.py index 2ae6c22..61abf44 100644 --- a/send.py +++ b/send.py @@ -18,15 +18,20 @@ def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): """ Takes a pattern and returns an audio buffer that encodes that pattern """ # the key's middle value is the bit's value and the left and right bits are the bits before and after # the buffers are enveloped to cleanly blend into each other + result0=quietnet.tone(off_freq, DATASIZE) + result1=quietnet.tone(on_freq, DATASIZE) + result2=quietnet.envelope(result0) + result3=quietnet.envelope(result1) + buffers = { - "000": quietnet.tone(off_freq, DATASIZE), - "100": quietnet.lenvelope(quietnet.tone(off_freq, DATASIZE)), - "001": quietnet.renvelope(quietnet.tone(off_freq, DATASIZE)), - "101": quietnet.envelope(quietnet.tone(off_freq, DATASIZE)), - "010": quietnet.envelope(quietnet.tone(on_freq, DATASIZE)), - "011": quietnet.lenvelope(quietnet.tone(on_freq, DATASIZE)), - "110": quietnet.renvelope(quietnet.tone(on_freq, DATASIZE)), - "111": quietnet.tone(on_freq, DATASIZE) + "000": result0, + "100": result2, + "001": result2, + "101": result2, + "010": result3, + "011": result3, + "110": result3, + "111": result1 } last_bit = pattern[-1] From 32fa491ea12bf5e12083081860a4362af95751fa Mon Sep 17 00:00:00 2001 From: construidor Date: Thu, 9 Jan 2014 19:04:40 -0200 Subject: [PATCH 04/15] Resolved issue with Buffer. Call up the "stream.write(sample)" sample by sample caused the problem on pyAudio. Only one called "stream.write" is made with all the content. --- .gitignore | 1 - file.py | 67 ------------------------------------------------------ send.py | 4 ++-- 3 files changed, 2 insertions(+), 70 deletions(-) delete mode 100644 file.py diff --git a/.gitignore b/.gitignore index 5d570c1..0d20b64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ *.pyc -WaveTest.wav diff --git a/file.py b/file.py deleted file mode 100644 index 0d26fbf..0000000 --- a/file.py +++ /dev/null @@ -1,67 +0,0 @@ -import quietnet -import options -import psk - -import wave - -CHANNELS = options.channels -RATE = options.rate -FREQ = options.freq -FREQ_OFF = 0 -FRAME_LENGTH = options.frame_length -DATASIZE = options.datasize - - -def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): - """ Takes a pattern and returns an audio buffer that encodes that pattern """ - # the key's middle value is the bit's value and the left and right bits are the bits before and after - # the buffers are enveloped to cleanly blend into each other - result0=quietnet.tone(off_freq, DATASIZE) - result1=quietnet.tone(on_freq, DATASIZE) - result2=quietnet.envelope(result0) - result3=quietnet.envelope(result1) - - buffers = { - "000": result0, - "100": result2, - "001": result2, - "101": result2, - "010": result3, - "011": result3, - "110": result3, - "111": result1 - } - - last_bit = pattern[-1] - output_buffer = [] - for i in range(len(pattern)): - bit = pattern[i] - if i < len(pattern) - 1: - next_bit = pattern[i+1] - else: - next_bit = pattern[0] - output_buffer = output_buffer + buffers[last_bit + bit + next_bit] - last_bit = bit - return quietnet.pack_buffer(output_buffer) - -if __name__ == "__main__": - wav_file = wave.open("WaveTest.wav", "w") - wav_file.setparams((CHANNELS, 2, RATE, DATASIZE, "NONE" , "not compressed")) - - print "Welcome to quietnet. Use ctrl-c to exit" - - try: - # get user input and play message - while True: - print ">", - message = raw_input() - pattern = psk.encode(message) - buffer = make_buffer_from_bit_pattern(pattern, FREQ, FREQ_OFF) - for sample in buffer: - wav_file.writeframes(sample) - except KeyboardInterrupt: - # clean up our streams and exit - wav_file.close() - print "exited cleanly" - - diff --git a/send.py b/send.py index 61abf44..6e0472f 100644 --- a/send.py +++ b/send.py @@ -47,8 +47,8 @@ def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): return quietnet.pack_buffer(output_buffer) def play_buffer(buffer): - for sample in buffer: - stream.write(sample) + output=''.join(buffer) + stream.write(output) if __name__ == "__main__": print "Welcome to quietnet. Use ctrl-c to exit" From 4d8688ea1361a54c3576697489403172c9d3e3a6 Mon Sep 17 00:00:00 2001 From: construidor Date: Thu, 9 Jan 2014 19:59:00 -0200 Subject: [PATCH 05/15] Ops, space... --- send.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/send.py b/send.py index 6e0472f..4d2395c 100644 --- a/send.py +++ b/send.py @@ -47,8 +47,8 @@ def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): return quietnet.pack_buffer(output_buffer) def play_buffer(buffer): - output=''.join(buffer) - stream.write(output) + output=''.join(buffer) + stream.write(sample) if __name__ == "__main__": print "Welcome to quietnet. Use ctrl-c to exit" From fa092e20d4800af9fd7200217440e0e4ea4984cc Mon Sep 17 00:00:00 2001 From: construidor Date: Sat, 11 Jan 2014 12:48:26 -0200 Subject: [PATCH 06/15] Reduced some audible noise --- .gitignore | 1 + quietnet.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0d20b64..104ffb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.wav diff --git a/quietnet.py b/quietnet.py index b61a6f1..58492e9 100644 --- a/quietnet.py +++ b/quietnet.py @@ -84,7 +84,7 @@ def decode(bytes): string += chr(int(byte, base=2)) return string -def tone(freq=400, datasize=4096, rate=44100, amp=12000.0, offset=0)): +def tone(freq=400, datasize=4096, rate=44100, amp=12000.0, offset=0): sine_list=[] for x in range(datasize): samp = math.sin(2*math.pi*freq*((x + offset)/float(rate))) @@ -101,7 +101,7 @@ def envelope(in_data, left=True, right=True, rate=44100): if (x < half and left) or (right and x >= half): samp = samp * (1 + math.sin(freq*x - (math.pi / 2))) / 2 if x > half: - samp = samp * math.sin(freq*x) + samp = samp * math.sin(freq*x) #cause audible "glitch" out_data.append(int(samp)) return out_data From 157bdad0e6954d01d2fc2a03c8a10000ee00fe99 Mon Sep 17 00:00:00 2001 From: construidor Date: Sat, 11 Jan 2014 12:56:15 -0200 Subject: [PATCH 07/15] Audible Problem solved --- quietnet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quietnet.py b/quietnet.py index 58492e9..fb279fd 100644 --- a/quietnet.py +++ b/quietnet.py @@ -100,8 +100,8 @@ def envelope(in_data, left=True, right=True, rate=44100): samp = in_data[x] if (x < half and left) or (right and x >= half): samp = samp * (1 + math.sin(freq*x - (math.pi / 2))) / 2 - if x > half: - samp = samp * math.sin(freq*x) #cause audible "glitch" + #if x > half: + # samp = samp * math.sin(freq*x) #cause audible "glitch" out_data.append(int(samp)) return out_data From 8d6a84ca27fc4ef44c236630a31ba877a83a3586 Mon Sep 17 00:00:00 2001 From: construidor Date: Mon, 13 Jan 2014 08:45:40 -0200 Subject: [PATCH 08/15] Add preamble system Guarantee of data integrity (or nearly), create a frame system with hash and size, removing the annoying noise interference --- listen.py | 29 ++++++++--- preamble.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++ send.py | 3 +- 3 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 preamble.py diff --git a/listen.py b/listen.py index c0d73df..05c02d3 100644 --- a/listen.py +++ b/listen.py @@ -7,6 +7,7 @@ import options import sys import psk +import preamble FORMAT = pyaudio.paInt16 frame_length = options.frame_length @@ -88,16 +89,32 @@ def process_points(): time.sleep(wait_for_point_timeout) def process_bits(): + cur_cur_bits = [] + cur_bits = [] while True: - cur_bits = [] - # while the last two characters are not the sigil - while len(cur_bits) < 2 or cur_bits[-len(sigil):len(cur_bits)] != sigil: + while len(cur_cur_bits) < (preamble.data_lenght + preamble.hash_lenght): try: - cur_bits.append(bits.get(False)) + cur_cur_bits.append(bits.get(False)) except Queue.Empty: time.sleep(wait_for_byte_timeout) - sys.stdout.write(psk.decode(cur_bits[:-len(sigil)])) - sys.stdout.flush() + continue + + # while the hash not work + [isValidHash, _, frameLenght] = preamble.extractInfo(cur_cur_bits) + if isValidHash: + frame = cur_cur_bits[:frameLenght] + + for bit in frame: + cur_bits.append(bit) + + # while the last two characters are not the sigil + if len(cur_bits) >= 2 and cur_bits[-len(sigil):len(cur_bits)] == sigil: + sys.stdout.write(psk.decode(cur_bits[:-len(sigil)])) + sys.stdout.flush() + cur_bits = [] + cur_cur_bits = [] + else: + del cur_cur_bits[0] # start the queue processing threads processes = [process_frames, process_points, process_bits] diff --git a/preamble.py b/preamble.py new file mode 100644 index 0000000..ab6250e --- /dev/null +++ b/preamble.py @@ -0,0 +1,137 @@ +CRC16_XMODEM_TABLE = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, + ] + +def _crc16(data, crc=1): + for byte in data: + crc = ((crc<<8)&0xff00) ^ CRC16_XMODEM_TABLE[((crc>>8)&0xff)^byte] + return crc & 0xffff + +def crc16Hash(frame): + bytes = [] + if type(frame) is list: + for i in range(0, len(frame), 8): + number = frame[i]<<7 | frame[i+1]<<6 | frame[i+2]<<5 \ + | frame[i+3]<<4 | frame[i+4]<<3 | frame[i+5]<<2 \ + | frame[i+6]<<1 | frame[i+7] + bytes.append(number) + elif type(frame) is str: + bytes=[int(frame[i:i + 8],2) for i in range(0, len(frame), 8)] + + return _crc16(bytes)&0x1f # return only 5 bits + +def sumHash(frame): + parity = 0 + for bit in frame: + if bit == '1' or bit == 1: + parity = not parity + return parity + +def noneHash(frame): + return 0 + +functionHash = crc16Hash # crc-5 it's better, but keep for now +data_lenght = 32 +hash_lenght = 16 + +def verifyFrame(frame): + return extractInfo(frame)[0] + +def fillFrame(frame, lenghtAdd): + if lenghtAdd > 0: + frame += '10' * int(lenghtAdd / 2) + if( lenghtAdd % 2 > 0 ): + frame += '0' + return frame + +def encodeInfo(lenght, hashCode): + """ + bit position: 00 11 22 33 44 + position: 0123456789111111 + 012345 + Frame format: 1LH1LH1LH1LH1LH1 + + It's not a good idea, keep a long sequences of 0s, therefore the 1s + """ + framePreample = '' + for pos,bit in [[i,1<> pos) + \ + str((hashCode & bit) >> pos) + framePreample += '1' + return framePreample + +def extractInfo(message): + """ + Return 3 values + 1. if the message is valid, hash is correct + 2. the hash + 3. the len of frame (in bits) + """ + data = message[-hash_lenght:] + frameHash = 0 + frameLenght = 0 + for pos,bit in [[1,0], [4,1], [7,2], [10,3], [13,4]]: + frameLenght |= int(data[pos]) << bit + frameHash |= int(data[pos + 1]) << bit + frameLenght += 1 + return [functionHash(message[:data_lenght]) & 0x1f == frameHash, \ + frameHash, frameLenght] + +def encode(message): + """ + Encode the message, adding on final a info preamble (postamble???), + who consist of length and hash + """ + messageString = '' + if type(message) is list: + for bit in message: + messageString += str(bit) + elif type(message) is str: + messageString = message + else: + return None + + output = [] + frames = [messageString[i:i + data_lenght] + for i in range(0, len(messageString), data_lenght)] + + for frame in frames: + frameLenght = len(frame) + if frameLenght < data_lenght: + frame = fillFrame(frame, data_lenght - frameLenght) + frameLenght -= 1 # between 0 and 31, max 0x1f, fill 5 bits + frameHash = functionHash(frame) + framePreample = encodeInfo(frameLenght, frameHash) + output.append(frame + framePreample) + return ''.join(output) diff --git a/send.py b/send.py index 3f4000b..30b7ea9 100644 --- a/send.py +++ b/send.py @@ -2,6 +2,7 @@ import quietnet import options import psk +import preamble FORMAT = pyaudio.paInt16 CHANNELS = options.channels @@ -50,7 +51,7 @@ def play_buffer(buffer): while True: message = raw_input("> ") try: - pattern = psk.encode(message) + pattern = preamble.encode(psk.encode(message)) buffer = make_buffer_from_bit_pattern(pattern, FREQ, FREQ_OFF) play_buffer(buffer) except KeyError: From 52f45b4e05059b4e810faeaaa84f9c98d0d2627b Mon Sep 17 00:00:00 2001 From: construidor Date: Tue, 14 Jan 2014 18:54:08 -0200 Subject: [PATCH 09/15] Simplified listen.py, removed all threads and queues and finalized preamble.py --- listen.py | 193 +++++++++++++++++++++++++--------------------------- preamble.py | 7 +- 2 files changed, 97 insertions(+), 103 deletions(-) diff --git a/listen.py b/listen.py index 05c02d3..7a27527 100644 --- a/listen.py +++ b/listen.py @@ -1,5 +1,3 @@ -import Queue -import threading import time import pyaudio import numpy as np @@ -17,13 +15,6 @@ sigil = [int(x) for x in options.sigil] frames_per_buffer = chunk * 10 -in_length = 4000 -# raw audio frames -in_frames = Queue.Queue(in_length) -# the value of the fft at the frequency we care about -points = Queue.Queue(in_length) -bits = Queue.Queue(in_length / frame_length) - wait_for_sample_timeout = 0.1 wait_for_frames_timeout = 0.1 wait_for_point_timeout = 0.1 @@ -32,104 +23,102 @@ # yeeeep this is just hard coded bottom_threshold = 8000 -def process_frames(): - while True: - try: - frame = in_frames.get(False) - fft = quietnet.fft(frame) - point = quietnet.has_freq(fft, search_freq, rate, chunk) - points.put(point) - except Queue.Empty: - time.sleep(wait_for_frames_timeout) - -def process_points(): - while True: - cur_points = [] - while len(cur_points) < frame_length: - try: - cur_points.append(points.get(False)) - except Queue.Empty: - time.sleep(wait_for_point_timeout) - - while True: - while np.average(cur_points) > bottom_threshold: - try: - cur_points.append(points.get(False)) - cur_points = cur_points[1:] - except Queue.Empty: - time.sleep(wait_for_point_timeout) - next_point = None - while next_point == None: - try: - next_point = points.get(False) - except Queue.Empty: - time.sleep(wait_for_point_timeout) - if next_point > bottom_threshold: - bits.put(0) - bits.put(0) - cur_points = [cur_points[-1]] - break - print '' - - last_bits = [] - while True: - if len(cur_points) == frame_length: - bit = int(quietnet.get_bit(cur_points, frame_length) > bottom_threshold) - cur_points = [] - bits.put(bit) - last_bits.append(bit) - # if we've only seen low bits for a while assume the next message might not be on the same bit boundary - if len(last_bits) > 3: - if sum(last_bits) == 0: - break - last_bits = last_bits[1:] - try: - cur_points.append(points.get(False)) - except Queue.Empty: - time.sleep(wait_for_point_timeout) - -def process_bits(): - cur_cur_bits = [] - cur_bits = [] - while True: - while len(cur_cur_bits) < (preamble.data_lenght + preamble.hash_lenght): - try: - cur_cur_bits.append(bits.get(False)) - except Queue.Empty: - time.sleep(wait_for_byte_timeout) - continue - - # while the hash not work - [isValidHash, _, frameLenght] = preamble.extractInfo(cur_cur_bits) - if isValidHash: - frame = cur_cur_bits[:frameLenght] - - for bit in frame: - cur_bits.append(bit) - - # while the last two characters are not the sigil - if len(cur_bits) >= 2 and cur_bits[-len(sigil):len(cur_bits)] == sigil: - sys.stdout.write(psk.decode(cur_bits[:-len(sigil)])) - sys.stdout.flush() - cur_bits = [] - cur_cur_bits = [] +def process_frames(frame): + fft = quietnet.fft(frame) + point = quietnet.has_freq(fft, search_freq, rate, chunk) + process_points(point) + + +def process_points(point): + if not hasattr(process_points, "estate"): + process_points.estate = 0 + + if not hasattr(process_points, "cur_points"): + process_points.cur_points = [] + + if not hasattr(process_points, "last_bits"): + process_points.last_bits = [] + + if process_points.estate == 0: + if len(process_points.cur_points) < frame_length: + process_points.cur_points.append(point) + return else: - del cur_cur_bits[0] - -# start the queue processing threads -processes = [process_frames, process_points, process_bits] -threads = [] - -for process in processes: - thread = threading.Thread(target=process) - thread.daemon = True - thread.start() + process_points.estate = 1 + + if process_points.estate >= 1 and process_points.estate <= 2: + if process_points.estate == 1: + if np.average(process_points.cur_points) > bottom_threshold: + process_points.cur_points.append(point) + process_points.cur_points = process_points.cur_points[1:] + return + else: + process_points.estate = 2 + + if process_points.estate == 2: + next_point = point + if next_point > bottom_threshold: + process_bits(0) + process_bits(0) + process_points.cur_points = [process_points.cur_points[-1]] + process_points.last_bits = [] + print '' + process_points.estate = 3 + return + + + if process_points.estate == 3: + if len(process_points.cur_points) == frame_length: + bit = int(quietnet.get_bit(process_points.cur_points, frame_length) > bottom_threshold) + process_points.cur_points = [] + process_bits(bit) + process_points.last_bits.append(bit) + # if we've only seen low bits for a while assume the next message might not be on the same bit boundary + if len(process_points.last_bits) > 3: + if sum(process_points.last_bits) == 0: + process_points.estate = 0 + return + process_points.last_bits = process_points.last_bits[1:] + process_points.cur_points.append(point) + #return + +def process_bits(bit): + if not hasattr(process_bits, "cur_bits"): + process_bits.cur_bits = [] + + if len(process_bits.cur_bits) < (preamble.data_lenght + preamble.hash_lenght): + process_bits.cur_bits.append(bit) + if len(process_bits.cur_bits) < (preamble.data_lenght + preamble.hash_lenght): + return + + # while the hash not work + [isValidHash, _, frameLenght] = preamble.extractInfo(process_bits.cur_bits) + if isValidHash: + frame = process_bits.cur_bits[:frameLenght] + + for bit in frame: + process_bits2(bit) + process_bits.cur_bits = [] + else: + del process_bits.cur_bits[0] + +def process_bits2(bit): + if not hasattr(process_bits2, "cur_bits"): + process_bits2.cur_bits = [] + + process_bits2.cur_bits.append(bit) + + # while the last two characters are not the sigil + if len(process_bits2.cur_bits) >= 2 \ + and process_bits2.cur_bits[-len(sigil):len(process_bits2.cur_bits)] == sigil: + sys.stdout.write(psk.decode(process_bits2.cur_bits[:-len(sigil)])) + sys.stdout.flush() + process_bits2.cur_bits = [] def callback(in_data, frame_count, time_info, status): frames = list(quietnet.chunks(quietnet.unpack(in_data), chunk)) for frame in frames: - if not in_frames.full(): - in_frames.put(frame, False) + process_frames(frame) return (in_data, pyaudio.paContinue) def start_analysing_stream(): diff --git a/preamble.py b/preamble.py index ab6250e..445e7a8 100644 --- a/preamble.py +++ b/preamble.py @@ -101,11 +101,16 @@ def extractInfo(message): data = message[-hash_lenght:] frameHash = 0 frameLenght = 0 + preample = True for pos,bit in [[1,0], [4,1], [7,2], [10,3], [13,4]]: + if int(data[pos - 1]) != 1: + preample = False frameLenght |= int(data[pos]) << bit frameHash |= int(data[pos + 1]) << bit + if int(data[15]) != 1: + preample = False frameLenght += 1 - return [functionHash(message[:data_lenght]) & 0x1f == frameHash, \ + return [ preample and (functionHash(message[:data_lenght]) & 0x1f == frameHash), \ frameHash, frameLenght] def encode(message): From ae65a2e462b1c639947614ed02817269680d4638 Mon Sep 17 00:00:00 2001 From: construidor Date: Tue, 14 Jan 2014 19:11:05 -0200 Subject: [PATCH 10/15] reverting changes that create new features. --- listen.py | 22 +------- preamble.py | 142 ------------------------------------------------- quietnet.py | 2 - screenshot.png | Bin 45722 -> 0 bytes send.py | 2 +- 5 files changed, 2 insertions(+), 166 deletions(-) delete mode 100644 preamble.py delete mode 100644 screenshot.png diff --git a/listen.py b/listen.py index 7a27527..6470bd1 100644 --- a/listen.py +++ b/listen.py @@ -82,27 +82,7 @@ def process_points(point): process_points.cur_points.append(point) #return -def process_bits(bit): - if not hasattr(process_bits, "cur_bits"): - process_bits.cur_bits = [] - - if len(process_bits.cur_bits) < (preamble.data_lenght + preamble.hash_lenght): - process_bits.cur_bits.append(bit) - if len(process_bits.cur_bits) < (preamble.data_lenght + preamble.hash_lenght): - return - - # while the hash not work - [isValidHash, _, frameLenght] = preamble.extractInfo(process_bits.cur_bits) - if isValidHash: - frame = process_bits.cur_bits[:frameLenght] - - for bit in frame: - process_bits2(bit) - process_bits.cur_bits = [] - else: - del process_bits.cur_bits[0] - -def process_bits2(bit): +def process_bits(bit): if not hasattr(process_bits2, "cur_bits"): process_bits2.cur_bits = [] diff --git a/preamble.py b/preamble.py deleted file mode 100644 index 445e7a8..0000000 --- a/preamble.py +++ /dev/null @@ -1,142 +0,0 @@ -CRC16_XMODEM_TABLE = [ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, - ] - -def _crc16(data, crc=1): - for byte in data: - crc = ((crc<<8)&0xff00) ^ CRC16_XMODEM_TABLE[((crc>>8)&0xff)^byte] - return crc & 0xffff - -def crc16Hash(frame): - bytes = [] - if type(frame) is list: - for i in range(0, len(frame), 8): - number = frame[i]<<7 | frame[i+1]<<6 | frame[i+2]<<5 \ - | frame[i+3]<<4 | frame[i+4]<<3 | frame[i+5]<<2 \ - | frame[i+6]<<1 | frame[i+7] - bytes.append(number) - elif type(frame) is str: - bytes=[int(frame[i:i + 8],2) for i in range(0, len(frame), 8)] - - return _crc16(bytes)&0x1f # return only 5 bits - -def sumHash(frame): - parity = 0 - for bit in frame: - if bit == '1' or bit == 1: - parity = not parity - return parity - -def noneHash(frame): - return 0 - -functionHash = crc16Hash # crc-5 it's better, but keep for now -data_lenght = 32 -hash_lenght = 16 - -def verifyFrame(frame): - return extractInfo(frame)[0] - -def fillFrame(frame, lenghtAdd): - if lenghtAdd > 0: - frame += '10' * int(lenghtAdd / 2) - if( lenghtAdd % 2 > 0 ): - frame += '0' - return frame - -def encodeInfo(lenght, hashCode): - """ - bit position: 00 11 22 33 44 - position: 0123456789111111 - 012345 - Frame format: 1LH1LH1LH1LH1LH1 - - It's not a good idea, keep a long sequences of 0s, therefore the 1s - """ - framePreample = '' - for pos,bit in [[i,1<> pos) + \ - str((hashCode & bit) >> pos) - framePreample += '1' - return framePreample - -def extractInfo(message): - """ - Return 3 values - 1. if the message is valid, hash is correct - 2. the hash - 3. the len of frame (in bits) - """ - data = message[-hash_lenght:] - frameHash = 0 - frameLenght = 0 - preample = True - for pos,bit in [[1,0], [4,1], [7,2], [10,3], [13,4]]: - if int(data[pos - 1]) != 1: - preample = False - frameLenght |= int(data[pos]) << bit - frameHash |= int(data[pos + 1]) << bit - if int(data[15]) != 1: - preample = False - frameLenght += 1 - return [ preample and (functionHash(message[:data_lenght]) & 0x1f == frameHash), \ - frameHash, frameLenght] - -def encode(message): - """ - Encode the message, adding on final a info preamble (postamble???), - who consist of length and hash - """ - messageString = '' - if type(message) is list: - for bit in message: - messageString += str(bit) - elif type(message) is str: - messageString = message - else: - return None - - output = [] - frames = [messageString[i:i + data_lenght] - for i in range(0, len(messageString), data_lenght)] - - for frame in frames: - frameLenght = len(frame) - if frameLenght < data_lenght: - frame = fillFrame(frame, data_lenght - frameLenght) - frameLenght -= 1 # between 0 and 31, max 0x1f, fill 5 bits - frameHash = functionHash(frame) - framePreample = encodeInfo(frameLenght, frameHash) - output.append(frame + framePreample) - return ''.join(output) diff --git a/quietnet.py b/quietnet.py index 0986e83..c580ff7 100644 --- a/quietnet.py +++ b/quietnet.py @@ -115,8 +115,6 @@ def envelope(in_data, left=True, right=True, rate=44100): samp = in_data[x] if (x < half and left) or (right and x >= half): samp = samp * (1 + math.sin(freq*x - (math.pi / 2))) / 2 - #if x > half: - # samp = samp * math.sin(freq*x) #cause audible "glitch" out_data.append(int(samp)) return out_data diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 4bc8b2f8ab24d48a17047f7c6ff67be8c63aa8e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45722 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 ") try: - pattern = preamble.encode(psk.encode(message)) + pattern = psk.encode(message) buffer = make_buffer_from_bit_pattern(pattern, FREQ, FREQ_OFF) play_buffer(buffer) except KeyError: From c30917bdc8cd5982889a1cf22e2d4306a67d6053 Mon Sep 17 00:00:00 2001 From: construidor Date: Tue, 14 Jan 2014 19:15:04 -0200 Subject: [PATCH 11/15] reverting changes that create new features 2. --- listen.py | 1 - quietnet.py | 2 +- send.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/listen.py b/listen.py index 6470bd1..169e7ec 100644 --- a/listen.py +++ b/listen.py @@ -5,7 +5,6 @@ import options import sys import psk -import preamble FORMAT = pyaudio.paInt16 frame_length = options.frame_length diff --git a/quietnet.py b/quietnet.py index c580ff7..efdc6f6 100644 --- a/quietnet.py +++ b/quietnet.py @@ -103,7 +103,7 @@ def tone(freq=400, datasize=4096, rate=44100, amp=12000.0, offset=0): sine_list=[] for x in range(datasize): samp = math.sin(2*math.pi*freq*((x + offset)/float(rate))) - sine_list.append(int(samp*amp/2)) + sine_list.append(int(samp*amp)) return sine_list def envelope(in_data, left=True, right=True, rate=44100): diff --git a/send.py b/send.py index 634291e..3f4000b 100644 --- a/send.py +++ b/send.py @@ -2,7 +2,6 @@ import quietnet import options import psk -import preamble FORMAT = pyaudio.paInt16 CHANNELS = options.channels From 674094991252a138d57e1f135c43adadfe4d482f Mon Sep 17 00:00:00 2001 From: construidor Date: Tue, 14 Jan 2014 19:17:26 -0200 Subject: [PATCH 12/15] Reversing the file screenshot.png --- screenshot.png | Bin 0 -> 45722 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 screenshot.png diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..4bc8b2f8ab24d48a17047f7c6ff67be8c63aa8e7 GIT binary patch literal 45722 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 Date: Tue, 14 Jan 2014 19:20:13 -0200 Subject: [PATCH 13/15] Solved some problems caused for the reversion --- listen.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/listen.py b/listen.py index 169e7ec..269539c 100644 --- a/listen.py +++ b/listen.py @@ -82,17 +82,17 @@ def process_points(point): #return def process_bits(bit): - if not hasattr(process_bits2, "cur_bits"): - process_bits2.cur_bits = [] + if not hasattr(process_bits, "cur_bits"): + process_bits.cur_bits = [] - process_bits2.cur_bits.append(bit) + process_bits.cur_bits.append(bit) # while the last two characters are not the sigil - if len(process_bits2.cur_bits) >= 2 \ - and process_bits2.cur_bits[-len(sigil):len(process_bits2.cur_bits)] == sigil: - sys.stdout.write(psk.decode(process_bits2.cur_bits[:-len(sigil)])) + if len(process_bits.cur_bits) >= 2 \ + and process_bits.cur_bits[-len(sigil):len(process_bits.cur_bits)] == sigil: + sys.stdout.write(psk.decode(process_bits.cur_bits[:-len(sigil)])) sys.stdout.flush() - process_bits2.cur_bits = [] + process_bits.cur_bits = [] def callback(in_data, frame_count, time_info, status): frames = list(quietnet.chunks(quietnet.unpack(in_data), chunk)) From 12c859188fa5d4ebb9d826b91854ec7894883ac2 Mon Sep 17 00:00:00 2001 From: construidor Date: Mon, 24 Feb 2014 18:22:25 -0300 Subject: [PATCH 14/15] Added python3 compatibility on send.py --- send.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/send.py b/send.py index 3f4000b..6ea51fd 100644 --- a/send.py +++ b/send.py @@ -39,7 +39,7 @@ def make_buffer_from_bit_pattern(pattern, on_freq, off_freq): return quietnet.pack_buffer(output_buffer) def play_buffer(buffer): - output = ''.join(buffer) + output = b''.join(buffer) stream.write(output) if __name__ == "__main__": @@ -48,7 +48,10 @@ def play_buffer(buffer): try: # get user input and play message while True: - message = raw_input("> ") + if sys.version_info[0] < 3: + message = raw_input("> ") + else: + message = input("> ") try: pattern = psk.encode(message) buffer = make_buffer_from_bit_pattern(pattern, FREQ, FREQ_OFF) From 13b0be704eb4e5cd705c7bd5c7b6b0e90c7dd881 Mon Sep 17 00:00:00 2001 From: construidor Date: Mon, 24 Feb 2014 18:29:45 -0300 Subject: [PATCH 15/15] Python3 Support on listen.py --- listen.py | 2 +- quietnet.py | 9 +++++++-- send.py | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/listen.py b/listen.py index 269539c..9cb7431 100644 --- a/listen.py +++ b/listen.py @@ -61,7 +61,7 @@ def process_points(point): process_bits(0) process_points.cur_points = [process_points.cur_points[-1]] process_points.last_bits = [] - print '' + print('') process_points.estate = 3 return diff --git a/quietnet.py b/quietnet.py index efdc6f6..3bfa4c3 100644 --- a/quietnet.py +++ b/quietnet.py @@ -1,11 +1,16 @@ import numpy as np import struct import math +import sys # split something into chunks def chunks(l, n): - for i in xrange(0, len(l), n): - yield l[i:i+n] + if sys.version_info[0] < 3: + for i in xrange(0, len(l), n): + yield l[i:i+n] + else: + for i in range(0, len(l), n): + yield l[i:i+n] def unpack(buffer): return unpack_buffer(list(chunks(buffer, 2))) diff --git a/send.py b/send.py index 6ea51fd..8fa9ffe 100644 --- a/send.py +++ b/send.py @@ -2,6 +2,7 @@ import quietnet import options import psk +import sys FORMAT = pyaudio.paInt16 CHANNELS = options.channels