{"id":21442231,"url":"https://github.com/thotypous/telecom-p1","last_synced_at":"2025-07-19T03:35:37.066Z","repository":{"id":37967253,"uuid":"504686665","full_name":"thotypous/telecom-p1","owner":"thotypous","description":"Prática 1 de Tecnologia de Comunicação","archived":false,"fork":false,"pushed_at":"2024-04-16T18:36:59.000Z","size":72,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-23T10:43:25.226Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thotypous.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-17T22:22:08.000Z","updated_at":"2023-06-15T12:55:44.000Z","dependencies_parsed_at":"2024-04-16T19:47:21.530Z","dependency_job_id":null,"html_url":"https://github.com/thotypous/telecom-p1","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thotypous%2Ftelecom-p1","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thotypous%2Ftelecom-p1/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thotypous%2Ftelecom-p1/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thotypous%2Ftelecom-p1/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thotypous","download_url":"https://codeload.github.com/thotypous/telecom-p1/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243955721,"owners_count":20374372,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-23T01:53:09.079Z","updated_at":"2025-03-17T00:45:26.835Z","avatar_url":"https://github.com/thotypous.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Modem dial-up\n\n## Introdução\n\nNesta prática, vamos implementar um modem dial-up compatível com o padrão [V.21](https://www.itu.int/rec/dologin_pub.asp?lang=e\u0026id=T-REC-V.21-198811-I!!PDF-E\u0026type=items).\n\n\n## Dependências\n\nSe quiser compilar localmente, você precisa essencialmente das bibliotecas gtest e rtaudio.\n\n```bash\nsudo pacman -S cmake rtaudio gtest python-matplotlib  # no Arch\nsudo apt-get install cmake librtaudio-dev libgtest-dev libgmock-dev python3-matplotlib  # no Ubuntu\n```\n\nO código foi testado com o rtaudio versão 6.0.1. A API do rtaudio é um pouco instável, então talvez não compile com outras versões.\n\nAté o Ubuntu 24.04, o rtaudio ainda não havia sido atualizado para a versão 6.0.1. Por isso, é necessário instalar o rtaudio manualmente. O passo a seguir **não** é necessário no Arch:\n\n```bash\n# somente no Ubuntu \u003c= 24.04\ncd\ngit clone https://github.com/thestk/rtaudio.git\ncd rtaudio\ngit checkout -b stable 6.0.1\nmkdir build \u0026\u0026 cd build\ncmake ..\nmake install\n```\n\nComo IDE, recomendo usar o VSCode com CMake Tools ou o neovim com clangd.\n\nPara testar seu código, execute:\n\n```bash\n./run-grader\n```\n\n## Passo-a-passo da implementação\n\n### UART\n\nPrimeiro, você deve trabalhar nos arquivos `uart.cpp` e `uart.hpp` para implementar um receptor de UART. Uma boa referência para entender o que deve ser feito é a [*application note* da Maxim](https://www.analog.com/en/technical-articles/determining-clock-accuracy-requirements-for-uart-communications.html). A diferença é que lá se discute um receptor com clock 16 vezes superior ao *baud rate*, ao passo que aqui temos um clock ainda mais rápido — 160 vezes superior ao *baud rate* — já que vamos usar a taxa de amostragem nativa da maioria das interfaces de áudio (48000 Hz).\n\nO método `UART_RX::put_samples` recebe um `buffer` contendo um sinal binário (cada elemento do `buffer` é 0 ou 1) amostrado à mesma taxa da interface de áudio. Esse `buffer` pode ter qualquer tamanho `n` e você deve lidar com esse fato. A forma mais fácil é fazer um loop iterando por cada amostra individual de `buffer` e nunca tentar olhar para as amostras futuras, nem olhar para amostras passadas de forma direta. Ou seja, se você precisar consultar amostras passadas, armazene-as de alguma forma como atributo da classe, mas nunca faça coisas como `buffer[i-1]`. Seguindo essas regras básicas, o seu código vai funcionar como uma máquina de estados fácil de entender e você não vai precisar ficar tratando casos especiais.\n\nSempre que você terminar de receber um byte completo, chame a função `get_byte` passando esse byte como argumento.\n\n**Sugestão**: Na *application note*, discute-se um receptor que espera uma amostra com nível lógico baixo no início do *start bit*, e três amostras com nível lógico baixo no meio do *start bit*. Como nosso clock é dez vezes maior, o análogo seria esperar 30 amostras com nível lógico baixo no meio do *start bit*. Para tolerar um pouco de erro, eu sugiro verificar se pelo menos 25 amostras dentre as últimas 30 recebidas têm nível lógico baixo. Se, além disso, a amostra que estaria no início do *start bit* tiver nível lógico baixo, considere que você achou o *start bit*. Quando isso acontecer, comece a contar múltiplos de 160 amostras a partir do meio do *start bit*, acrescentando o valor de cada amostra situada nesses múltiplos ao byte que está sendo recebido. Ao chegar no *stop bit*, passe o byte recebido para `get_byte` e volte sua lógica para o estado ocioso, no qual você deve procurar pelo próximo *start bit*.\n\nHá quatro testes para a UART — trivial, unsync, noisy e noisy\\_unsync. O teste trivial não introduz ruído nem diferença entre os clocks do receptor e transmissor. Você pode tentar passar nesse teste primeiro para se ambientar, o que deve ser possível mesmo se você começar a contagem no início do *start bit*. Mas se você começar a contagem no meio do *start bit* e estiver fazendo tudo certo, você deve passar em todos os quatro testes de uma vez só.\n\n\n### V21\n\nUma vez funcionando a UART, você deve trabalhar nos arquivos `v21.cpp` e `v21.hpp` para implementar um demodulador FSK.\n\nO método `V21_RX::demodulate` recebe um array `in_analog_samples` contendo um sinal \"analógico\" (entre aspas porque esse sinal é quantizado) proveniente da interface de áudio. Esse sinal pode ter qualquer tamanho `n` e você deve lidar com isso, similar ao passo anterior. A dica é a mesma de antes: tratar uma amostra por vez e nunca olhar para o futuro, nem olhar para o passado diretamente no array de entrada.\n\nSempre que você tiver demodulado uma quantidade conveniente de amostras binárias, chame `get_digital_samples` passando um buffer com essas amostras binárias e o tamanho desse buffer. No final das contas, esse buffer vai acabar sendo passado para a UART.\n\nMinha sugestão é implementar um demodulador que não se importa com a sincronia entre relógios, ou seja, que deixa isso a cargo da UART. Para isso, você pode chamar `get_digital_samples` uma única vez ao final do método `V21_RX::demodulate`, passando um buffer do mesmo tamanho de `in_analog_samples`. Essa parte já vem inclusive pronta no esqueleto de código fornecido a vocês.\n\nFaça dois filtros passa-bandas, um para o tom de marca, outro para o tom de espaço. Subtraia a saída de um dos filtros passa-bandas da saída do outro, e filtre essa diferença com um passa-baixas. Quando a diferença filtrada indicar uma energia maior na frequência de espaço, insira um 0 no buffer de saída. Caso contrário, insira um 1.\n\nImplemente uma estratégia para detectar a ausência de uma portadora (ou seja, quando não existe nem tom de marca nem tom de espaço). Enquanto não houver portadora, insira sempre 1 no buffer de saída.\n\nA seção \"Demodulando um sinal FSK\" [deste notebook Jupyter](https://colab.research.google.com/drive/1tjileevEYGz6IMGCzqgFq9Sy4GvYtBuL?usp=sharing) ensina a fazer tudo isso em Python de forma *offline*. Traduzir esse código para C++ não é muito difícil. Mas atente-se para o fato de que você deve transformá-lo em um código que opere de forma *online*, ou seja, tratando uma amostra de entrada por vez e guardando todo estado que for necessário como atributo da classe.\n\nOs testes de V21 traçam o gráfico de BER vs Eb/N0 e comparam alguns pontos desse gráfico com valores de referência. Se você implementar tudo certo, deve passar nos dois testes de uma vez só. O teste unsync corresponde a relógios ligeiramente diferentes no transmissor e no receptor, mas a própria UART deve conseguir lidar com isso.\n\n\n### Recuperação do clock (opcional)\n\nUm desafio opcional é implementar o descrito na seção \"Recuperando o sinal de relógio com um filtro\" do [notebook Jupyter](https://colab.research.google.com/drive/1tjileevEYGz6IMGCzqgFq9Sy4GvYtBuL?usp=sharing). Nesse caso, em vez de chamar `get_digital_samples` uma única vez no final do método `V21_RX::demodulate`, você poderia chamar `get_digital_samples` passando um buffer de 160 amostras idênticas assim que demodulasse cada símbolo do sinal. Assim, o V21 mandaria o sinal já com a temporização correta para a UART\n\nPor favor, note que a abordagem de deixar a sincronização a cargo da UART já funciona significativamente bem. **Os testes não vão avaliar se você implementou ou não este desafio.** Encare-o somente se tiver curiosidade e se tiver tempo de sobra.\n\n\n### Testes de bancada\n\nDe 10 pontos da nota, 8 são atribuídos automaticamente pelo robô de correção.\n\nPara conseguir os 2 pontos restantes, seu grupo deve submeter a implementação a um teste de bancada, interoperando com [um modem dial-up disponível comercialmente](https://pt.aliexpress.com/item/2032456154.html).\n\nPara isso, é necessário saber como **usar** o modem, e não só como executar os testes automatizados.\n\nComo cada ponta do enlace usa um par de frequências diferente para transmitir, a convenção é escolher esse par de frequências de acordo com o papel da ponta na chamada telefônica. Se foi quem discou (em inglês, *caller*), utiliza-se um par de frequências; se foi quem recebeu a chamada (em inglês, *answerer*), utiliza-se o outro. Por isso, será necessário passar essa informação ao modem.\n\n#### Linux\n\nEm um terminal, execute:\n\n```bash\nexport PIPEWIRE_NOJACK=1\n./Modem call   # na outra ponta, ./Modem ans\n```\n\nEm outro terminal, execute o picocom passando o dispositivo informado na saída do Modem:\n\n```bash\npicocom -b 115200 --echo /dev/pts/N\n```\n\nCom o picocom, você pode trocar mensagens de texto diretamente com a outra ponta.\n\nEm vez de usar o picocom, você também pode usar o slattach para subir uma interface SLIP:\n\n```bash\nsudo slattach -v -p slip /dev/pts/N\n\n# em outro terminal:\nsudo ifconfig sl0 192.168.123.x pointopoint 192.168.123.y\n```\n\nou até mesmo usar seu modem em conjunto com as suas [práticas da disciplina de Redes](https://github.com/thotypous/redes-s1)!\n\n\n#### Windows\n\nAntes de usar o modem no Windows, você precisa instalar o [com0com](https://sourceforge.net/projects/com0com/files/latest/download). Depois de instalar, reinicie o computador e faça uma atualização do driver pelo Windows Update. Você provavelmente terá de habilitar essa atualização manualmente na lista de atualizações opcionais de drivers. **Isso é muito importante**, pois a assinatura do driver que vem no instalador do com0com expirou, então o driver não funciona a menos que você o atualize pelo Windows Update.\n\nSe você tiver dificuldades para montar o ambiente de compilação no Windows, basta pegar o binário pronto nos artefatos gerados pelo GitHub Actions no seu repositório. Lá você vai encontrar um arquivo `Modem.exe` compilado a partir do código que você enviou para o GitHub.\n\nComo o modem precisa de algumas bibliotecas, descompacte as [DLLs das bibliotecas](https://drive.google.com/file/d/1pl44LOPE9bJrzsEediIDPpgSQ73rVrng/view?usp=sharing) no mesmo diretório que você colocar o `Modem.exe`.\n\nAgora precisamos esclarecer o funcionamento do com0com. Ao contrário do Linux, em que o subsistema pty permite alocar portos seriais virtuais dinamicamente, com o com0com o nosso modem precisa conectar a um porto serial virtual previamente configurado.\n\nDurante a instalação do com0com, se você não tiver alterado nenhuma opção, ele terá criado um par de portos seriais virtuais COM3/COM4. O padrão do modem é conectar em COM3, de forma que ele ficará acessível para você na COM4. Se você precisar mudar isso, pode passar a opção `-s PORTO` para o modem.\n\nAbra um Prompt do MS-DOS ou um terminal do PowerShell e execute `Modem call` ou `Modem ans`.\n\nUtilize o Putty para conectar-se à COM4 se quiser trocar mensagens de texto diretamente com a outra ponta.\n\nUtilize o discador do Windows se quiser subir uma interface de rede. Infelizmente, o Windows 7 parece ter sido a última versão do Windows a suportar SLIP. Mas você pode tentar usar PPP.\n\n\n#### Modem comercialmente disponível\n\nOs modems comercialmente disponíveis também apresentam-se ao usuário como dispositivos seriais mas, como eles suportam uma diversidade de protocolos e modos de operação, é necessário operá-los usando [comandos AT](https://www.thinkpenguin.com/files/CX930xx-manual.pdf) (também conhecidos como comandos Hayes).\n\nConecte-se ao dispositivo do modem utilizando o picocom (no Linux), Putty (no Windows) ou outro software que funcione como um terminal serial. No Linux, modems USB costumam ser detectados como `/dev/ttyUSB0` ou `/dev/ttyACM0`.\n\nSe você estiver usando o simulador de linha telefônica (em vez do PABX), use o seguinte comando para que o modem ignore a ausência de um tom de discagem:\n\n```\nATX3\n```\n\nUse o seguinte comando para colocar o modem em modo V.21:\n\n```\nAT+MS=V21,0\n```\n\nPara estabelecer a conexão, caso você esteja na ponta que efetua a chamada, use o comando:\n\n```\nATDT1\n```\n\nAcima, `1` é um número de telefone, que será chamado usando tons [DTMF](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling). Perceba que o modem que nós implementamos não possui essa funcionalidade de chamar um número — ele assume que a chamada telefônica já está estabelecida no momento que ele começa a operar.\n\nCaso você esteja na ponta que recebe a chamada, use o comando:\n\n```\nATA\n```\n\nA partir daí, você pode trocar mensagens de texto diretamente com a outra ponta.\n\nSe estiver no picocom e quiser subir uma interface SLIP, use as teclas Ctrl+A seguidas de Ctrl+Q para sair do picocom sem desligar a chamada (sem \"colocar o telefone no gancho\"). Em seguida, utilize o comando `slattach`, passando o dispositivo serial do modem.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthotypous%2Ftelecom-p1","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthotypous%2Ftelecom-p1","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthotypous%2Ftelecom-p1/lists"}