모스부호 인코딩 파이썬 코드
https://github.com/CNU-DataNetwork/3week_morse-OneEpitome
모스부호 디코딩 with Android Studio
https://github.com/OneEpitome/DataComm_Lab_AndroidMorseCode
이 실습의 핵심 요소는 다음과 같습니다.
- 입력받은 문자열을 모스부호로 인코딩하기
- 인코딩 된 모스부호를 아날로그 신호 (소리) 로 변환
- 모스부호로 인코딩 된 아날로그 신호를 다시 텍스트로 디코딩
def start(self, text ='', test=False, filename = FILE_NAME):
if test is False:
while True:
print('Type Any Text (only English)')
text = input('User input: ').strip()
if re.match(r'[A-Za-z0-9 ]+', text):
break
# 텍스트 -> 모스부호 문자열로 만들기
morse = self.text2morse(text)
self.init_morse = morse
print(f'MorseCode: {morse}')
#모스부호 문자열 -> 오디오 파일로 만들기
audio = self.morse2audio(morse, filename)
print(f'AudioSize: {len(audio)}')
print('Play Audio ...')
self.play_audio(audio)
print("Done")
위 코드를 살펴보면, Alphabet & Numeric 으로 이루어진 문자열을 입력받아 text2morse 를 수행합니다. 그리고 변환된 morse 를 가지고 morse2audio 를 수행합니다. 그리고 audio 데이터를 이용하여 play_audio 를 수행합니다.
입력받은 문자열을 모스부호로 인코딩하기
text2morse 를 살펴보겠습니다.
def text2morse(self, text):
text = text.upper()
morse = ''
for t in text:
if t == ' ':
morse = morse + '/'
for key, value in morsecode.code.items():
if t == key:
morse += value
morse = morse + ' '
return morse[:-1]
#morsecode.py
code = {'A':'.-' , 'B':'-...' , 'C':'-.-.' ,
'D':'-..' , 'E':'.' , 'F':'..-.' ,
'G':'--.' , 'H':'....' , 'I':'..' ,
'J':'.---' , 'K':'-.-' , 'L':'.-..' ,
'M':'--' , 'N':'-.' , 'O':'---' ,
'P':'.--.' , 'Q':'--.-' , 'R':'.-.' ,
'S':'...' , 'T':'-' , 'U':'..-' ,
'V':'...-' , 'W':'.--' , 'X':'-..-_',
'Y':'-.--' , 'Z':'--..' ,
'0':'-----', '1':'.----', '2':'..---',
'3':'...--', '4':'....-', '5':'.....',
'6':'-....', '7':'--...', '8':'---..',
'9':'----.', '0':'-----'}
위 코드를 살펴보면, 텍스트를 전부 Uppercase 로 변환 후 한글자씩 인코딩 후 morse 에 저장합니다.
입력된 character 가 ' '(공백) 일 경우 '/' 라는 기호로 encoding 하고 그 외의 경우에는 morsecode.py 의 code dictionary 에서 미리 encoding 된 모스부호를 가져옵니다.
인코딩 후 각 글자 사이에 무조건 ' '을 집어넣음으로써 모스부호로 변환된 결과를 글자 단위로 끊어 읽을 수 있습니다.
인코딩 된 모스부호를 아날로그 신호 (소리) 로 변환하기
morse2audio 를 살펴보겠습니다.
def morse2audio(self, morse, filename=False):
audio = []
for m in morse:
if m == '.':
for i in range(math.ceil(SAMPLERATE*UNIT)*1): #1초에 sample rate 만큼 샘플 만들고 이것을 unit 초 동안 녹음
audio.append(int(SHORTMAX*math.sin(2 * math.pi * FREQUENCY * (i/ SAMPLERATE))))
elif m == '-':
for i in range(math.ceil(SAMPLERATE*UNIT)*3):
audio.append(int(SHORTMAX*math.sin(2* math.pi * FREQUENCY * (i/SAMPLERATE))))
# (i / samplerate) 는 샘플 개수를 sin 함수 진행 시간에 mapping 시킴.
elif m == ' ':
for i in range(math.ceil(SAMPLERATE*UNIT)*1):
audio.append(int(0))
elif m == '/':
for i in range(math.ceil(SAMPLERATE*UNIT)*1):
audio.append(int(0))
for i in range(math.ceil(SAMPLERATE*UNIT)*1):
audio.append(int(0))
if filename:
self.audio = audio
self.audio2file(audio, filename)
return audio
위 코드를 살펴보면, 인코딩 된 모스부호를 하나하나 꺼내서 audio 라는 list 에 int 형 자료를 집어넣고 있습니다.
아날로그 소리 데이터를 디지털 데이터로 표현하려면 어떻게 했었는지 기억나시나요? 표본화 - 양자화 - 부호화 과정을 거쳐 디지털 데이터로 변환 후 저장가능했습니다. 즉 audio list 에 int 를 저장하는 것은 부호화된 소리의 디지털 정보를 저장하는 것이라 볼 수 있습니다. 다시 한 번 정리하면 소리는 하나의 int 로 저장됩니다.
이 코드에서는 '.' 에 해당하는 모스부호를 ShortMax*sin(2 * pi * FREQUENCY * time) 에 해당하는 사인파형의 소리를 0.1 초로 변환하고, '-' 에 해당하는 모스부호를 ShortMax*sin(2 * pi * FREQUENCY * time) 에 해당하는 사인파형의 소리 0.3초로 변환합니다. 즉, 0.1 초 동안의 샘플 데이터 (SAMPLERATE * 0.1) 개의 데이터를 저장하거나 0.3 초 동안의 샘플데이터(data frame) (SAMPLETRATE * 0.3) 개의 데이터를 audio list 에 저장합니다.
그 외의 공백이나 '/' 으로 인코딩 된 모스부호는 0 의 데이터를 0.1초에 해당하는 샘플 데이터를 audio 에 저장합니다.
text2morse 에서 ' ' 을 글자 사이에 넣어줬던 것처럼, 각 모스부호를 하나하나 구분지을 수 있도록 0.1 초 길이에 해당하는 0 을 가진 샘플을 audio 에 저장합니다.
time 이 i / SAMPLERATE 으로 표현되는 이유는 샘플의 개수로 시간 (second) 를 표현하기 위함입니다.
다시 한 번 정리하면, '.', '-', ' ', '/' 을 같은 주파수를 갖는 소리를 사용하되, 이 소리의 길이(지속시간)를 사용하여 모스부호를 표현하고 있습니다.
모스부호로 인코딩 된 아날로그 신호를 다시 텍스트로 디코딩
record_audio 는 입력으로 들어오는 소리를 모스부호로 해석합니다.
def record_audio():
unit_size = math.ceil(SAMPLERATE*UNIT)
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=CHANNELS,
rate=SAMPLERATE,
frames_per_buffer=SAMPLERATE,
input=True)
tuning = True
abs_sum = 0
count = 0
unseen = 0
morse = ''
recording = True
while recording:
data = stream.read(unit_size, exception_on_overflow = False)
for i in range(0, len(data), 2):
d = struct.unpack('<h', data[i:i+2])[0]
if tuning:
if abs(d) > MORSE_THRESHOLD:
tuning = False
if DEBUG:
print('Tuning end')
else:
abs_sum = abs_sum + abs(d)
count = count + 1
if count == unit_size:
if (abs_sum / count) >= MORSE_THRESHOLD:
morse = morse + '.'
unseen = 0
else:
morse = morse + ' '
unseen = unseen + 1
count = 0
abs_sum = 0
if DEBUG:
print(f'Current Morse: {morse}')
if unseen >= (UNSEEN_THRESHOLD/UNIT):
recording = False
break
print(f'RawMorse: {morse}')
stream.stop_stream()
stream.close()
p.terminate()
morse = morse.strip()
morse = morse.replace('...', '-')
morse = morse.replace(' ', 'm')
morse = morse.replace(' ', 's')
morse = morse.replace(' ', '')
morse = morse.replace('s', ' ')
morse = morse.replace('m', ' / ')
morse = morse
return morse
개괄적인 동작원리는 이렇습니다.
- 녹음 시작 -> Tuning 여부 파악 -> 소리가 발생하면 Tuning End
- 0.1 초에 해당하는 샘플 데이터 (data frame) 를 읽어 들여서 소리가 나고 있다고 판단되면 (MORSE_THRESHOLD 보다 평균적인 샘플링 데이터의 값이 크면 '.' 으로 인식, 소리가 나고 있지 않다고 판단되면 ' ' 으로 인식
- 일정 시간 (UNSEENTHRES_HOLD/UNIT -> 이 코드에서는 3초) 동안 소리가 나지 않으면 녹음 종료
- 녹음 종료 후, 입력받은 모스 부호를 적절하게 변환 -> 가공된 모스부호 반환
- 가공된 모스부호를 다시 morsecode.py 에 있던 code dictionary 와의 비교를 통해 원본 문자열을 얻을 수 있습니다.
먼저 0.1 초 동안 소리가 나느냐 안나느냐로 '.' 을 입력받아 raw morse data 를 만들고 그 이후에 가공하는 방식으로 동작합니다.
이 실습의 특징은 입력받은 문자열을 모스부호로 바꾸고 모스부호 하나하나 (디지털 데이터) 를 Line Coding 을 통해 디지털 신호로 변환하여 아날로그 신호 (소리) 를 통해 데이터를 주고 받는 의미가 있습니다.
'데이터 통신' 카테고리의 다른 글
[Data Communication] IP 주소를 왜 쓸까? (0) | 2023.04.18 |
---|---|
[Data Communication] Switching, 회선 교환과 패킷 교환 (0) | 2023.04.15 |
[Data Communication] 다중화 (Multiplexing) (0) | 2023.04.04 |
[Data Communication] 디지털 데이터 -> 디지털 신호, 디지털 데이터의 전송 방식 (0) | 2023.03.27 |
[Data Communication] 디지털 데이터 -> 아날로그 신호로의 변환 (0) | 2023.03.20 |