Successfully locking on first frame
This commit is contained in:
parent
861dafb169
commit
e6df77cf59
95
decode.py
95
decode.py
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
""" @package docstring
|
""" @package docstring
|
||||||
Manchester encoder
|
Manchester decoder
|
||||||
|
|
||||||
Encodes a file using the manchester encoding and outputs it as audio file
|
Decodes a file encoded with the manchester code
|
||||||
See https://www.youtube.com/watch?v=8BhjXqw9MqI&list=PLowKtXNTBypH19whXTVoG3oKSuOcw_XeW&index=3
|
See https://www.youtube.com/watch?v=8BhjXqw9MqI&list=PLowKtXNTBypH19whXTVoG3oKSuOcw_XeW&index=3
|
||||||
and following videos by awesome Ben Eater
|
and following videos by awesome Ben Eater
|
||||||
Very slow and inefficient implementation, meant only to be clear and didactic
|
Very slow and inefficient implementation, meant only to be clear and didactic
|
||||||
@ -32,7 +32,7 @@ NAME = 'manchester-decoder'
|
|||||||
VERSION = '0.1'
|
VERSION = '0.1'
|
||||||
DESCRIPTION = 'Decodes a file using the manchester encoding'
|
DESCRIPTION = 'Decodes a file using the manchester encoding'
|
||||||
|
|
||||||
FRAME_DELIMITER = 129 # (1, 0, 0, 0, 0, 0, 0, 1)
|
FRAME_DELIMITER = 126 # (01111110)
|
||||||
PREAMBLE_DURATION = 128
|
PREAMBLE_DURATION = 128
|
||||||
ZERO_POINT = 0 # The 0 value: values less than this are considered 0, more than this 1
|
ZERO_POINT = 0 # The 0 value: values less than this are considered 0, more than this 1
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ class Main:
|
|||||||
try:
|
try:
|
||||||
self.syncWithClock()
|
self.syncWithClock()
|
||||||
self._log.info("Sync: clock duration is {}".format(self.clockDuration))
|
self._log.info("Sync: clock duration is {}".format(self.clockDuration))
|
||||||
#self.waitForStart()
|
self.waitForStart()
|
||||||
#self._log.info("Found start of data")
|
#self._log.info("Found start of data")
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
self._log.error("Ran out of input data before completing initialization!")
|
self._log.error("Ran out of input data before completing initialization!")
|
||||||
@ -60,35 +60,32 @@ class Main:
|
|||||||
|
|
||||||
def syncWithClock(self):
|
def syncWithClock(self):
|
||||||
# Uses the preamble to obtain the clock duration
|
# Uses the preamble to obtain the clock duration
|
||||||
i = 0
|
analyzedCycles = 0
|
||||||
while True:
|
while True:
|
||||||
(duration, value) = self.readRawBit()
|
(cycles, raising) = self.goToNextZeroCrossing(True)
|
||||||
|
analyzedCycles = analyzedCycles + cycles
|
||||||
|
|
||||||
print(duration, value)
|
if analyzedCycles > PREAMBLE_DURATION * self.clockDuration / 4:
|
||||||
|
# At this point we should have an idea of the clock duration, move on
|
||||||
if i < PREAMBLE_DURATION/4:
|
return
|
||||||
# First cycles are used to extimate clock duration
|
|
||||||
self.clockDuration = self.clockDuration + duration / 2
|
|
||||||
i = i + 1
|
|
||||||
# We are now synced with clock. Add a 25% "slack" to make following samples at
|
|
||||||
# 1/4 and 3/4 to clock cycle: remember that every cycle has 2 values: 01 if the
|
|
||||||
# encoded bit was 1 and 10 if it was 0
|
|
||||||
for x in range(0, int(self.clockDuration / 4)):
|
|
||||||
self.readRawBit()
|
|
||||||
|
|
||||||
def waitForStart(self):
|
def waitForStart(self):
|
||||||
# After the clock has been extimated, continue reading and wait for first delimiter
|
# After the clock has been extimated, continue reading and wait for first delimiter
|
||||||
lastByte = 0
|
lastByte = 0
|
||||||
while True:
|
while True:
|
||||||
value = decodeBit()
|
value = self.decodeBit()
|
||||||
|
|
||||||
# Shift the byte to left and add the read bit in the least significant position
|
# Shift the byte to left
|
||||||
lastByte = lastByte << 1
|
lastByte = lastByte << 1
|
||||||
|
# Truncate the length to 8 bits
|
||||||
|
lastByte = lastByte & 255 # 0 11111111
|
||||||
|
# Add the read bit in the least significant position
|
||||||
if value:
|
if value:
|
||||||
lastByte = lastByte + 1
|
lastByte = lastByte + 1
|
||||||
|
|
||||||
if lastByte == FRAME_DELIMITER:
|
if lastByte == FRAME_DELIMITER:
|
||||||
# Found frame delimiter! We are in sync! YAY!
|
# Found frame delimiter! We are in sync! YAY!
|
||||||
|
self._log.info("Found first frame delimiter")
|
||||||
return
|
return
|
||||||
|
|
||||||
#def decodeFile():
|
#def decodeFile():
|
||||||
@ -99,32 +96,24 @@ class Main:
|
|||||||
#except ValueError as e:
|
#except ValueError as e:
|
||||||
# Completed reading file
|
# Completed reading file
|
||||||
|
|
||||||
def readRawBit(self):
|
|
||||||
# Reads a raw bit. Returns the duration of that bit and the value.
|
|
||||||
duration = 0
|
|
||||||
prev = None
|
|
||||||
while True:
|
|
||||||
frame = self.audioSource.readframes(1)
|
|
||||||
if not frame:
|
|
||||||
raise ValueError('No more data to read')
|
|
||||||
|
|
||||||
v = int(struct.unpack('<h', frame)[0]) > ZERO_POINT
|
|
||||||
|
|
||||||
if prev is None:
|
|
||||||
prev = v
|
|
||||||
else:
|
|
||||||
# Detect zero crossing
|
|
||||||
if prev < ZERO_POINT and v > ZERO_POINT:
|
|
||||||
break
|
|
||||||
if prev > ZERO_POINT and v < ZERO_POINT:
|
|
||||||
break
|
|
||||||
|
|
||||||
prev = v
|
|
||||||
duration = duration + 1
|
|
||||||
|
|
||||||
return (duration, prev > ZERO_POINT)
|
|
||||||
|
|
||||||
def decodeBit(self):
|
def decodeBit(self):
|
||||||
|
# Decodes a bit. Searches for the phase invertion at 75% to 125% of the clock cycle
|
||||||
|
bitDuration = 0
|
||||||
|
while True:
|
||||||
|
(duration, raising) = self.goToNextZeroCrossing(False)
|
||||||
|
bitDuration = bitDuration + duration
|
||||||
|
if bitDuration < self.clockDuration * 0.75:
|
||||||
|
# Ignore: half-cycle crossing due to two equal digits one near the other
|
||||||
|
continue
|
||||||
|
if bitDuration > self.clockDuration * 1.25:
|
||||||
|
# Lost tracking!
|
||||||
|
raise Exception("Lost tracking! No phase inversion found between {} and {} samples from the last one".format(self.clockDuration * 0.75, self.clockDuration * 1.25))
|
||||||
|
|
||||||
|
# This is our phase inversion signal
|
||||||
|
return raising
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Reads and decodes 2 raw bits into 1 decoded bit. 01 (raising) = 1, 10 (falling) = 0
|
# Reads and decodes 2 raw bits into 1 decoded bit. 01 (raising) = 1, 10 (falling) = 0
|
||||||
# Works only once clock is synced
|
# Works only once clock is synced
|
||||||
# The bits are read at 1/4 and 3/4 of clock cycle
|
# The bits are read at 1/4 and 3/4 of clock cycle
|
||||||
@ -147,8 +136,26 @@ class Main:
|
|||||||
# Lost sync!!!
|
# Lost sync!!!
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
|
def goToNextZeroCrossing(self, adjustClockDuration):
|
||||||
|
# Find the next zero crossing and returns:
|
||||||
|
# (cycles since last inversion, True if is raising, False if is falling)
|
||||||
|
cyclesSinceLastInversion = 0
|
||||||
|
prev = None
|
||||||
|
while True:
|
||||||
|
frame = self.audioSource.readframes(1)
|
||||||
|
if not frame:
|
||||||
|
raise ValueError('No more data to read')
|
||||||
|
|
||||||
|
v = int(struct.unpack('<h', frame)[0]) > ZERO_POINT
|
||||||
|
if prev == None:
|
||||||
|
prev = v
|
||||||
|
if v != prev:
|
||||||
|
# Zero-point crossing!
|
||||||
|
if adjustClockDuration:
|
||||||
|
self.clockDuration = (self.clockDuration + cyclesSinceLastInversion) / 2
|
||||||
|
return (cyclesSinceLastInversion, v)
|
||||||
|
|
||||||
|
cyclesSinceLastInversion = cyclesSinceLastInversion + 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
12
encode.py
12
encode.py
@ -32,7 +32,7 @@ NAME = 'manchester-encoder'
|
|||||||
VERSION = '0.1'
|
VERSION = '0.1'
|
||||||
DESCRIPTION = 'Encodes a file using the manchester encoding and outputs it as audio file'
|
DESCRIPTION = 'Encodes a file using the manchester encoding and outputs it as audio file'
|
||||||
|
|
||||||
FRAME_DELIMITER = 129 # (1, 0, 0, 0, 0, 0, 0, 1)
|
FRAME_DELIMITER = 126 # (01111110)
|
||||||
PREAMBLE_DURATION = 128
|
PREAMBLE_DURATION = 128
|
||||||
AUDIO_VOLUME = 16384 # 0 to 32767
|
AUDIO_VOLUME = 16384 # 0 to 32767
|
||||||
AUDIO_BITRATE = 44100
|
AUDIO_BITRATE = 44100
|
||||||
@ -69,14 +69,14 @@ class Main:
|
|||||||
if not byte:
|
if not byte:
|
||||||
# Finished reading file
|
# Finished reading file
|
||||||
# Terminate with delimiter and exit
|
# Terminate with delimiter and exit
|
||||||
self.encodeByte(FRAME_DELIMITER)
|
self.encodeByte(FRAME_DELIMITER, encode_frame_delimiter=False)
|
||||||
break
|
break
|
||||||
byte = byte[0]
|
byte = byte[0]
|
||||||
|
|
||||||
# Every 64 bytes, outputs a frame delimiter: 01111110
|
# Every 64 bytes, outputs a frame delimiter: 01111110
|
||||||
# This is used by receiver to syncronize to the start of a byte
|
# This is used by receiver to syncronize to the start of a byte
|
||||||
if position % 64 == 0:
|
if position % 64 == 0:
|
||||||
self.encodeByte(FRAME_DELIMITER)
|
self.encodeByte(FRAME_DELIMITER, encode_frame_delimiter=False)
|
||||||
position = position + 1
|
position = position + 1
|
||||||
|
|
||||||
# Encode byte
|
# Encode byte
|
||||||
@ -85,7 +85,7 @@ class Main:
|
|||||||
self.audioSink.close()
|
self.audioSink.close()
|
||||||
self._log.info('Completed')
|
self._log.info('Completed')
|
||||||
|
|
||||||
def encodeByte(self, byte):
|
def encodeByte(self, byte, encode_frame_delimiter=True):
|
||||||
# Encodes a byte with the Mancester Encoding
|
# Encodes a byte with the Mancester Encoding
|
||||||
# Note that the byte is read from the most important to the least important bit
|
# Note that the byte is read from the most important to the least important bit
|
||||||
# Es: 10000010 is not 130, but 65
|
# Es: 10000010 is not 130, but 65
|
||||||
@ -98,13 +98,13 @@ class Main:
|
|||||||
# Write bit
|
# Write bit
|
||||||
self.encodeBit(lastBit)
|
self.encodeBit(lastBit)
|
||||||
|
|
||||||
# If we have 5 consecutive "1", add a 0 after, to avoid being interpreted as a frame delimiter
|
# If we have 5 consecutive "1", add a 0 after, to avoid being interpreted as a frame delimiter (01111110))
|
||||||
if lastBit:
|
if lastBit:
|
||||||
consecutiveOnes = consecutiveOnes + 1
|
consecutiveOnes = consecutiveOnes + 1
|
||||||
else:
|
else:
|
||||||
consecutiveOnes = 0
|
consecutiveOnes = 0
|
||||||
|
|
||||||
if consecutiveOnes == 5:
|
if consecutiveOnes == 5 and encode_frame_delimiter:
|
||||||
self.encodeBit(0)
|
self.encodeBit(0)
|
||||||
consecutiveOnes = 0
|
consecutiveOnes = 0
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user