ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Lab - Data Over Sound] 모스부호로 데이터 전송하기
    데이터 통신 2023. 4. 5. 21:41

    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 을 통해 디지털 신호로 변환하여 아날로그 신호 (소리) 를 통해 데이터를 주고 받는 의미가 있습니다.

Designed by Tistory.