中庸

https://youtu.be/iKrzUMVWFtk

모스부호 인코딩 파이썬 코드

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

개괄적인 동작원리는 이렇습니다.

  1. 녹음 시작 -> Tuning 여부 파악 -> 소리가 발생하면 Tuning End
  2. 0.1 초에 해당하는 샘플 데이터 (data frame) 를 읽어 들여서 소리가 나고 있다고 판단되면 (MORSE_THRESHOLD 보다 평균적인 샘플링 데이터의 값이 크면 '.' 으로 인식, 소리가 나고 있지 않다고 판단되면 ' ' 으로 인식
  3. 일정 시간 (UNSEENTHRES_HOLD/UNIT -> 이 코드에서는 3초) 동안 소리가 나지 않으면 녹음 종료
  4. 녹음 종료 후, 입력받은 모스 부호를 적절하게 변환 -> 가공된 모스부호 반환
  5. 가공된 모스부호를 다시 morsecode.py 에 있던 code dictionary 와의 비교를 통해 원본 문자열을 얻을 수 있습니다.

먼저 0.1 초 동안 소리가 나느냐 안나느냐로 '.' 을 입력받아 raw morse data 를 만들고 그 이후에 가공하는 방식으로 동작합니다.

 

 

이 실습의 특징은 입력받은 문자열을 모스부호로 바꾸고 모스부호 하나하나 (디지털 데이터) 를 Line Coding 을 통해 디지털 신호로 변환하여 아날로그 신호 (소리) 를 통해 데이터를 주고 받는 의미가 있습니다.

profile

中庸

@짱일모

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!