1
\$\begingroup\$

After watching Computerphile videos on WW2 cryptography and radioteletype I decided to code a translator to Baudot code. I wonder if it can be somehow improved?

def translate_to_baudot(untranslatedtext):
    from textwrap import wrap
    def getdic():
        chars=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ ∆-:$3!&#8\'().,9014‽57;2/6"') #‽ is bell and ∆ is new line
        baud=["11000","10011","01110","10010","10000","10110","01011","00101","01100","11010","11110","01001","00111","00110","00011","01101","11101","01010","10100","00001","11100","01111","11001","10111","10101","10001","00100","01000","11000","10011","01110","10010","10000","10110","01011","00101","01100","11010","11110","01001","00111","00110","00011","01101","11101","01010","10100","00001","11100","01111","11001","10111","10101","10001"]
        baudic={chars[i]: baud[i] for i in range(len(chars))}
        return baudic
    def add_shift(text,charnum):
        if charnum>0:
            if text[charnum-1] in'ABCDEFGHIJKLMNOPQRSTUVWXYZ ∆' and text[charnum] not in'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
                return '11111'
            elif text[charnum-1] not in'ABCDEFGHIJKLMNOPQRSTUVWXYZ ∆' and text[charnum] in'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
                return '11011'
            else:
                return ""
        elif charnum==0 and text[0] not in'ABCDEFGHIJKLMNOPQRSTUVWXYZ ':
            return '11111'
        else:
            return ""
    def bell_check(text):
        text=text.replace('\a','‽')
        return text
    def line_check(text):
        text=text.replace('\n','∆')
        return text
    def strip_non_baud(text):
        for char in text:
            if char not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ-:$3!&#8\'().,9014‽57;2/6" ∆':
                text=text.replace('char','')
        return text
    def text_to_baud(text):
        baudic=getdic()
        baudtext=''
        charnum=0
        for char in text:
            baudtext+=add_shift(text,charnum)
            baudtext+=baudic[char]
            charnum+=1
        return baudtext
    text=untranslatedtext
    text=bell_check(text)
    text=line_check(text)
    text=text.upper()
    text='‽‽‽∆'+strip_non_baud(text)+'∆‽‽‽'
    print(text)
    baudtext=text_to_baud(text)
    baudint=int(baudtext,2)
    print(baudint)
    return baudtext
if __name__=='__main__':
    text=input('enter your text here: ')
    translate_to_baudot('text')

I made this when I just had started coding so it's probably not even close to the best approach.

\$\endgroup\$
7
  • \$\begingroup\$ Do you actually write code with no whitespace, or is that a side-effect of copying to SE? \$\endgroup\$
    – Reinderien
    Jul 27 at 23:57
  • \$\begingroup\$ Which output do you care about: baudint, or baudtext? You output baudint but return (and don't use) baudtext. \$\endgroup\$
    – Reinderien
    Jul 28 at 0:02
  • 1
    \$\begingroup\$ Have you verified output correctness? Whereas there are 53 chars in your alphabet, there are 54 baud. \$\endgroup\$
    – Reinderien
    Jul 28 at 0:18
  • \$\begingroup\$ Oh. I care about both. Baudint is the result of taking your Baudot encoded text (a huge binary number) and convert it to a base 10 integer while baudtext is the actual baudot code (i return it to use the binary sequence later. I haven't found the way to do what I want) \$\endgroup\$ Jul 28 at 1:33
  • \$\begingroup\$ I verified it but of course this is just as good as the table I copied. I've passed texts, huge passages of Lorem Ipsum, ASCII art and even text ciphered as an input and got no errors raised so I find it weird I missed something \$\endgroup\$ Jul 28 at 1:36

1 Answer 1

1
\$\begingroup\$

You need blank lines between your functions for both PEP8 and general sanity.

This:

chars[i]: baud[i] for i in range(len(chars))

should not use range, and should use a zip instead. However, I am going to suggest that you rework this database so that your alphabets are expressed in binary order, not in alphabetic order. (If you stuck to alphabetic order you'd also want to sort your ordinals, which 3!&#8\'().,9014‽57;2/6 you have not.) If you hold these in binary order, you can simply enumerate() over them to get their code as the index.

I don't understand why you use the pseudo-printable substitutes , for bell and newline. You're going from plaintext to an encoding. If someone wants a bell, they can write alarm \a within the input string; I don't see why bell_check would be useful. Also note that (like Windows, and traditional printers) this encoding relies on CRLF pairs, so you might want to do some translation from single '\n' characters to such pairs.

I had a lot of difficulty in reconciling your alphabet data with the alphabets described on Wikipedia. In my example code I have assumed that Wikipedia is right.

The Baudot shift is stateful; that is, the terminal is either in letter mode or figure mode for a string of potentially multiple characters. Your add_shift checks both the previous and current character, but this is more complicated than it needs to be: you can just hold a state variable for whether you're in letter or figure mode. Rather than returning a blank string, consider writing an iterator function that either yields or doesn't.

Consider an optional feature that throws if there are non-encodable characters in the input.

Representing the output as one enormous integer is not useful. If anything, you would want to form a binary-packed bytes object; but I have not shown how to do this. Instead I have demonstrated an easy way to show the binary output with separation spaces.

Suggested

# Assume US TTY variant of ITA2, LSB on right
# https://en.wikipedia.org/wiki/Baudot_code#ITA2
from typing import Iterator


LETTER_SERIES = (
    '\0'  # null
    'E'
    '\n'  # linefeed
    'A SIU'
    '\r'   # carriage return
    'DRJNFCKTZLWHYPQOBG'
    '\x0E'  # shift-to-figure (shown as "shift-out")
    'MXV'
    '\x0F'  # letter page extension (shown as "shift-in")
)

FIGURE_SERIES = (
    '\0'  # null
    '3'
    '\n'  # linefeed
    '- '
    '\a'  # bell (shown as "alarm")
    '87'
    '\r'   # carriage return
    '$4'
    "'"
    ',!:(5")2#6019?&'
    '\x0E'  # figure page extension (shown as "shift-out")
    './;'
    '\x0F'  # shift-to-letter (shown as "shift-in")
)


def series_to_codes(series: str) -> Iterator[tuple[str, str]]:
    for i, c in enumerate(series):
        yield c, f'{i:05b}'


LETTER_CODES = dict(series_to_codes(LETTER_SERIES))
FIGURE_CODES = dict(series_to_codes(FIGURE_SERIES))


def text_to_baudot_codes(text: str, strict: bool = False) -> Iterator[str]:
    i_codeset = 0
    codesets = LETTER_CODES, FIGURE_CODES
    shifts = '\x0E\x0F'

    for orig in text:
        if orig in shifts:
            if strict:
                raise ValueError()
            continue

        orig = orig.upper()
        current_code = codesets[i_codeset].get(orig)
        if current_code is not None:
            yield current_code
            continue

        other_code = codesets[1 - i_codeset].get(orig)
        if other_code is not None:
            yield codesets[i_codeset][shifts[i_codeset]]
            yield other_code
            i_codeset = 1 - i_codeset
            continue

        if strict:
            raise ValueError()


def text_to_baudot(text: str, strict: bool = False) -> str:
    return ' '.join(text_to_baudot_codes(text, strict))


if __name__ == '__main__':
    text = input('Text: ')
    print(text_to_baudot(text))

Output

Text: t3rd.
10000 11011 00001 11111 01010 01001 11011 11100
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.