├── uuport.h ├── debian ├── source │ └── format ├── changelog ├── rules ├── control └── copyright ├── scripts ├── kill_job.sh ├── get_nodename.sh ├── clean_files.sh ├── disable_monitor.sh ├── enable_monitor.sh ├── TODO ├── get_systems.sh ├── restart_system.sh ├── vara_watchdog.sh ├── alias.sh ├── decrypt.sh ├── encrypt.sh ├── create_html.sh ├── run_tests.sh ├── run_tests-evc.sh ├── decompress_image.sh ├── caller.sh └── compress_image.sh ├── manual ├── releases │ ├── manual_en.pdf │ ├── manual_es.pdf │ ├── manual_pt.pdf │ ├── manual_pt_v2.pdf │ ├── equipment_en-0.1.pdf │ ├── hermes_pt-br-0.1.pdf │ ├── hermes-software_en-0.1.pdf │ └── user-manual_pt-br-0.1.pdf ├── Makefile ├── equipment_en.tex ├── hermes-software-en.tex └── report.tex ├── gui ├── iconfont │ ├── MaterialIcons-Regular.eot │ ├── MaterialIcons-Regular.ttf │ ├── MaterialIcons-Regular.woff │ ├── MaterialIcons-Regular.woff2 │ ├── README.md │ └── material-icons.css ├── get_name.php ├── running.php ├── list.php ├── get_systems.php ├── header.php ├── shutdown.php ├── clean_files.php ├── kill_job.php ├── restart_system.php ├── view_log.php ├── list_job.php ├── erase_queue.php ├── index_nocss.php ├── decrypt.php ├── uucico.php ├── index.php ├── tx.php ├── receive.php ├── load.php ├── style.css ├── styles.css └── upload.php ├── initscripts ├── caller.service └── uuardopd.service ├── examples ├── uucp │ ├── port │ ├── config │ ├── passwd │ ├── dial │ ├── Poll │ ├── call │ ├── expire │ └── sys └── run_ardops_aloop.sh ├── .gitignore ├── common.h ├── call_uucico.h ├── net.h ├── serial.h ├── vara.h ├── shm.h ├── ardop.h ├── Makefile ├── common.c ├── net.c ├── shm.c ├── circular_buffer.h ├── uuardopd.h ├── README.md ├── serial.c ├── uuport.c ├── call_uucico.c ├── circular_buffer.c ├── uuardopd.c ├── vara.c └── ardop.c /uuport.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /scripts/kill_job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo killall uucico 4 | sudo killall uuport 5 | -------------------------------------------------------------------------------- /scripts/get_nodename.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | egrep -v '^\s*#' /etc/uucp/config | grep nodename | cut -f 2 3 | -------------------------------------------------------------------------------- /manual/releases/manual_en.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/manual_en.pdf -------------------------------------------------------------------------------- /manual/releases/manual_es.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/manual_es.pdf -------------------------------------------------------------------------------- /manual/releases/manual_pt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/manual_pt.pdf -------------------------------------------------------------------------------- /manual/releases/manual_pt_v2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/manual_pt_v2.pdf -------------------------------------------------------------------------------- /scripts/clean_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo rm -rf /var/www/html/uploads/* 4 | sudo rm -rf /var/www/html/arquivos/* 5 | -------------------------------------------------------------------------------- /scripts/disable_monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export LD_LIBRARY_PATH=/opt/vc/lib/ 4 | /opt/vc/bin/vcgencmd display_power 0 5 | -------------------------------------------------------------------------------- /scripts/enable_monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export LD_LIBRARY_PATH=/opt/vc/lib/ 4 | /opt/vc/bin/vcgencmd display_power 1 5 | -------------------------------------------------------------------------------- /manual/releases/equipment_en-0.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/equipment_en-0.1.pdf -------------------------------------------------------------------------------- /manual/releases/hermes_pt-br-0.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/hermes_pt-br-0.1.pdf -------------------------------------------------------------------------------- /gui/iconfont/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/gui/iconfont/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /gui/iconfont/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/gui/iconfont/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /gui/iconfont/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/gui/iconfont/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /gui/iconfont/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/gui/iconfont/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /scripts/TODO: -------------------------------------------------------------------------------- 1 | . Make a script to modify a reference RPi image with correct gpg keyring and 2 | uucp files with correct map and nodename set. 3 | -------------------------------------------------------------------------------- /manual/releases/hermes-software_en-0.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/hermes-software_en-0.1.pdf -------------------------------------------------------------------------------- /manual/releases/user-manual_pt-br-0.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalHERMES/rhizo-uuardop/HEAD/manual/releases/user-manual_pt-br-0.1.pdf -------------------------------------------------------------------------------- /scripts/get_systems.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #egrep -v '^\s*#' /etc/uucp/sys | grep system | cut -f 2 -d " " 4 | egrep -v '^\s*#' /etc/uucp/sys | grep alias | cut -f 2 -d " " 5 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | rhizo-uuardop (0.1-1) unstable; urgency=medium 2 | 3 | * Initial release. 4 | 5 | -- Rafael Diniz Thu, 15 Jul 2021 19:41:17 -0300 6 | -------------------------------------------------------------------------------- /scripts/restart_system.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sudo systemctl stop uuardopd 4 | sudo systemctl stop ardop 5 | sleep 1 6 | sudo systemctl start ardop 7 | sudo systemctl start uuardopd 8 | -------------------------------------------------------------------------------- /gui/get_name.php: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ 5 | 6 | #override_dh_auto_install: 7 | # dh_auto_install -- prefix=/usr 8 | 9 | #override_dh_install: 10 | # dh_install --list-missing -X.pyc -X.pyo 11 | -------------------------------------------------------------------------------- /scripts/vara_watchdog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PORT=8300 3 | CMD="nc -z localhost ${PORT}" 4 | 5 | ${CMD} 6 | retval=$? 7 | while [ "${retval}" != "0" ] 8 | do 9 | sleep 1 10 | ${CMD} 11 | retval=$? 12 | done 13 | -------------------------------------------------------------------------------- /scripts/alias.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #read $1 3 | #echo nome 4 | line=$(grep -n $1 /etc/uucp/sys|cut -d ':' -f 1) 5 | linePlus=$((line+1)) 6 | #echo $line 7 | name=$(head -$linePlus /etc/uucp/sys|tail -1|cut -d ' ' -f 2) 8 | echo -n $name 9 | -------------------------------------------------------------------------------- /gui/running.php: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /initscripts/caller.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=HERMES Gateway caller daemon 3 | After=vnc.service 4 | Requires=vnc.service 5 | 6 | [Service] 7 | Type=exec 8 | ExecStart=/usr/bin/caller.sh 9 | KillSignal=SIGTERM 10 | IgnoreSIGPIPE=no 11 | Restart=always 12 | TimeoutSec=infinity 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /scripts/decrypt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # uso: 3 | # decrypt.sh arquivo_para_cifrar.gpg 4 | 5 | if [ $# -lt 3 ]; then 6 | echo "Usage: $0 filename.gpg filename password" 7 | echo "Output: filename" 8 | exit 1 9 | fi 10 | 11 | # dec_file=$(dirname "${1}")/$(basename ${1} .gpg) 12 | 13 | gpg -o "${2}" -d --batch --passphrase "${3}" --yes "${1}" 14 | -------------------------------------------------------------------------------- /scripts/encrypt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # uso: 3 | # encrypt.sh arquivo_para_cifrar.abc 4 | 5 | if [ $# -lt 2 ]; then 6 | echo "Usage: $0 filename password" 7 | echo "Output: filename.gpg" 8 | exit 1 9 | fi 10 | 11 | enc_file="${1}.gpg" 12 | 13 | gpg -o "${enc_file}" --symmetric --cipher-algo AES256 --batch --passphrase "${2}" --yes "${1}" 14 | 15 | rm "${1}" 16 | -------------------------------------------------------------------------------- /gui/iconfont/README.md: -------------------------------------------------------------------------------- 1 | The recommended way to use the Material Icons font is by linking to the web font hosted on Google Fonts: 2 | 3 | ```html 4 | 6 | ``` 7 | 8 | Read more in our full usage guide: 9 | http://google.github.io/material-design-icons/#icon-font-for-the-web 10 | -------------------------------------------------------------------------------- /gui/list.php: -------------------------------------------------------------------------------- 1 | "; 10 | } 11 | closedir($dh); 12 | } 13 | } 14 | ?> 15 | -------------------------------------------------------------------------------- /initscripts/uuardopd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=UUARDOPD daemon 3 | After=vnc.service ubitx.service 4 | Requires=vnc.service ubitx.service 5 | 6 | [Service] 7 | Type=exec 8 | ExecStart=/usr/bin/uuardopd -a 127.0.0.1 -p 8300 -r vara -o shm 9 | KillSignal=SIGTERM 10 | IgnoreSIGPIPE=no 11 | Restart=always 12 | TimeoutSec=infinity 13 | ExecStartPre=/usr/bin/vara_watchdog.sh 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /gui/get_systems.php: -------------------------------------------------------------------------------- 1 | "; 12 | echo $sysnames[$i]; 13 | echo ""; 14 | } 15 | } 16 | 17 | ?> 18 | -------------------------------------------------------------------------------- /scripts/create_html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | quality="40k" 4 | 5 | echo "" 6 | 7 | echo "Avaliação de qualidade" 8 | 9 | echo "" 10 | 11 | for i in *; do 12 | 13 | input_file=\"${i}\" 14 | no_extension=${i%.*} 15 | 16 | size=$(stat -c%s "../encoded-vvc-${quality}/${no_extension}.vvc") 17 | 18 | echo "Size: ${size}


" 19 | 20 | done 21 | 22 | echo "" 23 | echo "" 24 | -------------------------------------------------------------------------------- /examples/uucp/port: -------------------------------------------------------------------------------- 1 | # 2 | # port This file defines the possible dialout ports you have 3 | # on your system. Normally you have only one, and it's 4 | # most probably /dev/ttyS[0-3]. Define that port here. 5 | # 6 | # If you have multiple dialout ports, you can ofcourse 7 | # define them all if you want. 8 | # 9 | port HFP 10 | type pipe 11 | command /usr/bin/uuport -c \Z 12 | 13 | # 14 | # Description for the TCP port - pretty trivial. DON'T DELETE. 15 | # 16 | port TCP 17 | type tcp 18 | -------------------------------------------------------------------------------- /gui/header.php: -------------------------------------------------------------------------------- 1 | "; 4 | echo "

Estação: "; 5 | include('get_name.php'); 6 | echo "

"; 7 | echo "

Estado do Sistema: "; 8 | include 'running.php'; 9 | echo "

"; 10 | echo "

Voltar para Página Principal

"; 11 | echo "
"; 12 | echo ""; 13 | 14 | ?> 15 | -------------------------------------------------------------------------------- /manual/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | pdflatex report.tex 3 | pdflatex report.tex 4 | pdflatex equipment_en.tex 5 | pdflatex equipment_en.tex 6 | pdflatex hermes-software-en.tex 7 | pdflatex hermes-software-en.tex 8 | 9 | clean: 10 | rm -f report.aux report.out report.log report.toc report.pdf 11 | rm -f equipment_en.aux equipment_en.out equipment_en.log equipment_en.toc equipment_en.pdf 12 | rm -f hermes-software-en.aux hermes-software-en.out hermes-software-en.log hermes-software-en.toc hermes-software-en.pdf 13 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: rhizo-uuardop 2 | Section: network 3 | Priority: optional 4 | Maintainer: Rafael Diniz 5 | Build-Depends: debhelper-compat (= 12) 6 | Standards-Version: 4.5.0 7 | Homepage: www.rhizomatica.org/hermes 8 | 9 | Package: rhizo-uuardop 10 | Architecture: any 11 | Multi-Arch: foreign 12 | Depends: libc6 (>= 2.14) 13 | Description: A set of tools which allow UUCP to use ARDOP or VARA as 14 | modem. With this integration, UUCP is fully functional over HF 15 | links. 16 | -------------------------------------------------------------------------------- /examples/uucp/config: -------------------------------------------------------------------------------- 1 | # 2 | # config This is the configuration file for uucp. Most configurable 3 | # things are commented out as they are the default (compiled 4 | # in) setting. The only thing you might want to change is the 5 | # "nodename". That's your UUCP system name - change it if it's 6 | # different from your normal hostname. 7 | # 8 | 9 | nodename AM4AAB 10 | 11 | # Defaults. 12 | #sysfile /etc/uucp/sys 13 | #portfile /etc/uucp/port 14 | #dialfile /etc/uucp/dial 15 | #callfile /etc/uucp/call 16 | #passwdfile /etc/uucp/passwd 17 | -------------------------------------------------------------------------------- /examples/uucp/passwd: -------------------------------------------------------------------------------- 1 | # 2 | # passwd If you give access to your UUCP system over TCP to others, 3 | # and you run uucico directly from crontab, then uucico needs 4 | # to be able to read the password file to check the password 5 | # of the remote system. 6 | # 7 | # With a shadow system that's impossible - only root can read 8 | # the passwords from the shadow file. Therefore, if you're 9 | # in that situation, you can define the login passwords 10 | # here in plain text. 11 | # 12 | # Format: loginname password 13 | # 14 | 15 | #Utester grumblesmurf 16 | user pass 17 | -------------------------------------------------------------------------------- /examples/uucp/dial: -------------------------------------------------------------------------------- 1 | # 2 | # dial This file defines "dialers". Nothing special here - you 3 | # just define the init and dial strings for the modems. 4 | # 5 | # Most systems should be able to just use the "hayes" entry. 6 | # Try that first before changing this file. 7 | # 8 | dialer hayes 9 | chat "" ATZ\r\d\c OK\r \dATM0L0E1Q0\r\d\c OK\r ATDT\D CONNECT 10 | chat-fail RING 11 | chat-fail NO\sCARRIER 12 | chat-fail ERROR 13 | chat-fail NO\sDIALTONE 14 | chat-fail BUSY 15 | chat-fail NO\sANSWER 16 | chat-fail VOICE 17 | complete \d\d+++\d\dATM0H\r\c 18 | abort \d\d+++\d\dATM0H\r\c 19 | 20 | # Add extra dialer definitions here. 21 | -------------------------------------------------------------------------------- /examples/uucp/Poll: -------------------------------------------------------------------------------- 1 | # 2 | # Poll This file contains a list of the systems which are to be 3 | # polled, and the hours during which they are to be polled. 4 | # Normally, uudemon.hr processes this file hourly. 5 | # 6 | # Version: @(#)Poll 2.10 19-Dec-1996 7 | # 8 | # Example: 9 | # schedule alphen 18 10 | # poll alphen 18 19 20 02 03 04 05 06 11 | # 12 | # Note that every system needs 1 line for schedule AND 13 | # 1 line for poll. The schedule action only touches an empty 14 | # file so that UUCP knows there is work to do for the remote 15 | # system. The "poll" is then only done if that file is present. 16 | # This is a crude dialing retry system. 17 | # 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /examples/run_ardops_aloop.sh: -------------------------------------------------------------------------------- 1 | # some examples 2 | 3 | # run ardop in a also loopback device: 4 | ardop1ofdm 8515 hw:1,0 hw:1,0 5 | ardop1ofdm 8517 hw:1,1 hw:1,1 6 | 7 | # example ~/.asoundrc 8 | pcm.aloop { 9 | type hw 10 | card 0 11 | } 12 | 13 | ctl.aloop { 14 | type hw 15 | card 0 16 | } 17 | ## 18 | 19 | 20 | # run as root (example): 21 | uuardopd -i /tmp/uucp2.fifo -o /tmp/uucp1.fifo -c BB2UIT -d PP2UIT -a 127.0.0.1 -p 8515 -t 60 22 | 23 | # for job status: 24 | uustat -a 25 | 26 | # for killing a job: 27 | uustat -k job_name 28 | 29 | # to send a file: 30 | uucp ginga.txt remote\!\~/ginga.txt 31 | 32 | # or 33 | uuto FILE central\! 34 | 35 | 36 | #for sending a file: 37 | uucp TODO central\!centrauser\!/TODO 38 | 39 | -------------------------------------------------------------------------------- /gui/shutdown.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

14 | Desligando... 15 |

16 |
17 | 18 |
19 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /gui/clean_files.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

14 | Arquivos Removidos 15 |

16 |
17 | 18 |
19 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /gui/kill_job.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

14 | Transmissão Cancelada 15 |

16 |
17 | 18 |
19 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /gui/restart_system.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

14 | Sistema Interno Reiniciado 15 |

16 |
17 | 18 |
19 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /gui/view_log.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 |

Histórico do Sistema

15 |
16 | 17 |
18 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /gui/list_job.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

Fila de Transmissão

14 |
15 | 16 |
17 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /gui/iconfont/material-icons.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Material Icons'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(MaterialIcons-Regular.eot); /* For IE6-8 */ 6 | src: local('Material Icons'), 7 | local('MaterialIcons-Regular'), 8 | url(MaterialIcons-Regular.woff2) format('woff2'), 9 | url(MaterialIcons-Regular.woff) format('woff'), 10 | url(MaterialIcons-Regular.ttf) format('truetype'); 11 | } 12 | 13 | .material-icons { 14 | font-family: 'Material Icons'; 15 | font-weight: normal; 16 | font-style: normal; 17 | font-size: 24px; /* Preferred icon size */ 18 | display: inline-block; 19 | line-height: 1; 20 | text-transform: none; 21 | letter-spacing: normal; 22 | word-wrap: normal; 23 | white-space: nowrap; 24 | direction: ltr; 25 | 26 | /* Support for all WebKit browsers. */ 27 | -webkit-font-smoothing: antialiased; 28 | /* Support for Safari and Chrome. */ 29 | text-rendering: optimizeLegibility; 30 | 31 | /* Support for Firefox. */ 32 | -moz-osx-font-smoothing: grayscale; 33 | 34 | /* Support for IE. */ 35 | font-feature-settings: 'liga'; 36 | } 37 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: rhizo-uuardop 3 | Upstream-Contact: Rafael Diniz 4 | Source: www.rhizomatica.org/hermes 5 | Copyright: Rhizomatica 2019-2021 6 | License: GPL-3+ 7 | 8 | This is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 3, or (at your option) 11 | any later version. 12 | . 13 | This software is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | . 18 | You should have received a copy of the GNU General Public License 19 | along with this software; see the file COPYING. If not, write to 20 | the Free Software Foundation, Inc., 51 Franklin Street, 21 | Boston, MA 02110-1301, USA. 22 | . 23 | On Debian systems, the complete text of the GNU General Public License 24 | Version 3 can be found in `/usr/share/common-licenses/GPL-3'. 25 | -------------------------------------------------------------------------------- /gui/erase_queue.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

Arquivos Removidos da Fila de Transmissão 14 |

15 |
16 | 17 |
18 | 39 |
40 | Carregar arquivos 41 | 42 |
43 | 44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /gui/index_nocss.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 |

Estação:

12 |
Estado do Sistema:
13 |
14 | 15 |

16 |

Carregar Arquivo para Transmissão

17 |

18 | 19 |

20 |

Transmitir

21 |

22 | 23 |

24 |

Listar Arquivos Carregados para Transmissão

25 |

26 | 27 |

28 |

Recebimento de arquivos

29 |

30 |

31 |

Cancelar transmissão

32 |

33 |

34 |

Fila de transmissão

35 |

36 |

37 |

Ver log

38 |

39 |
40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CODEC=vvc 4 | 5 | SOURCE_DIR=/home/hermes/content/images 6 | ENCODED_DIR=/home/hermes/content/encoded-vvc 7 | RECONSTRUCTED_DIR=/home/hermes/content/reconstructed-vvc 8 | 9 | COMPRESS_IMAGE=/home/hermes/rhizo-uuardop/scripts/compress_image.sh 10 | DECOMPRESS_IMAGE=/home/hermes/rhizo-uuardop/scripts/decompress_image.sh 11 | 12 | cd ${SOURCE_DIR} 13 | 14 | mkdir -p ${ENCODED_DIR}/ 15 | mkdir -p ${RECONSTRUCTED_DIR}/ 16 | 17 | rm -f ${ENCODED_DIR}/* 18 | rm -f ${RECONSTRUCTED_DIR}/* 19 | 20 | # compress data 21 | for i in *; do 22 | 23 | no_extension=${i%.*} 24 | 25 | input_file=\"${i}\" 26 | output_file=\"${ENCODED_DIR}/${no_extension}.${CODEC}\" 27 | echo "==== INPUT: ${input_file}" 28 | echo "==== OUTPUT: ${output_file}" 29 | eval ${COMPRESS_IMAGE} \"${i}\" \"${ENCODED_DIR}/${no_extension}.${CODEC}\" 30 | 31 | echo "==== DONE" 32 | done 33 | 34 | cd ${ENCODED_DIR} 35 | 36 | # decompress data 37 | for i in *; do 38 | 39 | no_extension=${i%.*} 40 | 41 | # input_file=\"${i}\" 42 | # output_file=\"${ENCODED_DIR}/${no_extension}.${CODEC}\" 43 | # echo compress ${COMPRESS_IMAGE} ${input_file} ${output_file} 44 | eval ${DECOMPRESS_IMAGE} \"${i}\" \"${RECONSTRUCTED_DIR}/${no_extension}.jpg\" 45 | 46 | done 47 | -------------------------------------------------------------------------------- /scripts/run_tests-evc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CODEC=evc 4 | 5 | SOURCE_DIR=/home/hermes/content/images 6 | ENCODED_DIR=/home/hermes/content/encoded-evc 7 | RECONSTRUCTED_DIR=/home/hermes/content/reconstructed-evc 8 | 9 | COMPRESS_IMAGE=/home/hermes/rhizo-uuardop/scripts/compress_image.sh 10 | DECOMPRESS_IMAGE=/home/hermes/rhizo-uuardop/scripts/decompress_image.sh 11 | 12 | cd ${SOURCE_DIR} 13 | 14 | mkdir -p ${ENCODED_DIR}/ 15 | mkdir -p ${RECONSTRUCTED_DIR}/ 16 | 17 | rm -f ${ENCODED_DIR}/* 18 | rm -f ${RECONSTRUCTED_DIR}/* 19 | 20 | # compress data 21 | for i in *; do 22 | 23 | no_extension=${i%.*} 24 | 25 | input_file=\"${i}\" 26 | output_file=\"${ENCODED_DIR}/${no_extension}.${CODEC}\" 27 | echo "==== INPUT: ${input_file}" 28 | echo "==== OUTPUT: ${output_file}" 29 | eval ${COMPRESS_IMAGE} \"${i}\" \"${ENCODED_DIR}/${no_extension}.${CODEC}\" 30 | 31 | echo "==== DONE" 32 | done 33 | 34 | cd ${ENCODED_DIR} 35 | 36 | # decompress data 37 | for i in *; do 38 | 39 | no_extension=${i%.*} 40 | 41 | # input_file=\"${i}\" 42 | # output_file=\"${ENCODED_DIR}/${no_extension}.${CODEC}\" 43 | # echo compress ${COMPRESS_IMAGE} ${input_file} ${output_file} 44 | eval ${DECOMPRESS_IMAGE} \"${i}\" \"${RECONSTRUCTED_DIR}/${no_extension}.jpg\" 45 | 46 | done 47 | -------------------------------------------------------------------------------- /gui/decrypt.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 | "; 36 | echo "Senha correta.
"; 37 | echo "".basename($str).""; 38 | echo "
"; 39 | 40 | } else { 41 | echo "
Senha incorreta!

"; 42 | echo "
"; 43 | unlink($outfile); 44 | } 45 | ?> 46 | 47 | 48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-UUCP 2 | * Copyright (C) 2018 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Common functions used by any type of modem 21 | */ 22 | 23 | /** 24 | * @file common.h 25 | * @author Rafael Diniz 26 | * @date 09 May 2018 27 | * @brief Common functions used by any type of modem 28 | * 29 | * Common functions used by any type of modem. 30 | * 31 | */ 32 | 33 | #ifndef HAVE_COMMON__ 34 | #define HAVE_COMMON__ 35 | 36 | #include 37 | #include 38 | 39 | #include "uuardopd.h" 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | bool inotify_wait(char *file_name); 46 | 47 | #ifdef __cplusplus 48 | }; 49 | #endif 50 | 51 | #endif /* HAVE_COMMON */ 52 | -------------------------------------------------------------------------------- /call_uucico.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop: Tools to integrate Ardop to UUCP 2 | * Copyright (C) 2019 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | */ 21 | 22 | /** 23 | * @file call_uucico.h 24 | * @author Rafael Diniz 25 | * @date 26 Jul 2019 26 | * @brief Routines to call uucico when receiving a call 27 | * 28 | * Code to call uucico 29 | * 30 | */ 31 | 32 | #ifndef HAVE_CALL_UUCICO_H__ 33 | #define HAVE_CALL_UUCICO_H__ 34 | 35 | #include 36 | #include "uuardopd.h" 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | bool call_uucico(rhizo_conn *connector); 43 | void *uucico_thread(void *conn); 44 | void *uucico_read_thread(void *conn); 45 | void *uucico_write_thread(void *conn); 46 | 47 | #endif /* HAVE_CALL_UUCICO__ */ 48 | -------------------------------------------------------------------------------- /scripts/decompress_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # usage: 3 | # decompress_image.sh file.vvc file.jpg 4 | 5 | if [ $# -lt 2 ]; then 6 | echo "Usage: $0 image_filename.{vvc,evc,avif,heic} output.jpg" 7 | exit 1 8 | fi 9 | 10 | VVC_DEC=${VVC_DEC:=/root/vvdec/install/bin/vvdecapp} 11 | EVC_DEC=${EVC_DEC:=/root/xevd/build/bin/xevd_app} 12 | CJPEG_ENC=${CJPEG:=/opt/mozjpeg/bin/cjpeg} 13 | 14 | input_file=${1} 15 | output_file=${2} 16 | 17 | IMAGE_FORMAT="${input_file##*.}" 18 | 19 | TEMPFILEYUV=/tmp/temp-$$.yuv 20 | TEMPFILEYUV2=/tmp/temp-$$-2.yuv 21 | 22 | if [ ${IMAGE_FORMAT} = "vvc" ]; then 23 | 24 | 25 | resolution=$(${VVC_DEC} -t 2 -b "${input_file}" -o ${TEMPFILEYUV} | grep SizeInfo | cut -d " " -f 4) 26 | # echo ${resolution} 27 | ffmpeg -pix_fmt yuv420p10le -s ${resolution} -y -i ${TEMPFILEYUV} -pix_fmt yuv420p ${TEMPFILEYUV2} 28 | 29 | # ${CJPEG} 30 | # convert-im6 -size ${resolution} -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV2} -quality 89 $"{output_file}" 31 | convert-im6 -size ${resolution} -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV2} pnm:- | ${CJPEG_ENC} -quality 95 -outfile "${output_file}" 32 | 33 | 34 | rm -f ${TEMPFILEYUV} 35 | rm -f ${TEMPFILEYUV2} 36 | 37 | elif [ ${IMAGE_FORMAT} = "evc" ]; then 38 | 39 | resolution=$(${EVC_DEC} -i "${input_file}" -o ${TEMPFILEYUV} | grep Resolution| cut -f 2 -d = | tr -d '[:space:]') 40 | 41 | convert-im6 -size ${resolution} -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV} pnm:- | ${CJPEG_ENC} -quality 95 -outfile "${output_file}" 42 | 43 | rm -f ${TEMPFILEYUV} 44 | fi 45 | -------------------------------------------------------------------------------- /net.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-connector: A connector to different HF modems 2 | * Copyright (C) 2018 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Network related functions 21 | */ 22 | 23 | /** 24 | * @file net.h 25 | * @author Rafael Diniz 26 | * @date 12 Apr 2018 27 | * @brief File containing network related functions 28 | * 29 | * For the sake of reusable and clean code, some network related functions. 30 | * 31 | */ 32 | 33 | #ifndef HAVE_NET_H__ 34 | #define HAVE_NET_H__ 35 | 36 | #include 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | #define TCP_BLOCK 512 43 | 44 | bool tcp_connect(char *ip, int port, int *sockt); 45 | bool tcp_read(int sockt, uint8_t *buffer, size_t rx_size); 46 | bool tcp_write(int sockt, uint8_t *buffer, size_t rx_size); 47 | 48 | #ifdef __cplusplus 49 | }; 50 | #endif 51 | 52 | #endif /* HAVE_NET_H__ */ 53 | -------------------------------------------------------------------------------- /serial.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuhf: Tools to integrate HF TNCs to UUCP 2 | * Copyright (C) 2020-2021 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | */ 21 | 22 | #ifndef HAVE_SERIAL_H__ 23 | #define HAVE_SERIAL_H__ 24 | 25 | #include 26 | 27 | #define MAX_MODEM_PATH 4096 28 | #define MAX_BUF_SIZE 4096 29 | 30 | #define RADIO_TYPE_ICOM 0 31 | #define RADIO_TYPE_UBITX 1 32 | #define RADIO_TYPE_SHM 2 33 | 34 | void key_on(int serial_fd, int radio_type); 35 | void key_off(int serial_fd, int radio_type); 36 | 37 | void connected_led_on(int serial_fd, int radio_type); 38 | void connected_led_off(int serial_fd, int radio_type); 39 | 40 | void sys_led_on(int serial_fd, int radio_type); 41 | void sys_led_off(int serial_fd, int radio_type); 42 | 43 | int open_serial_port(char *ttyport); 44 | void set_fixed_baudrate(char *baudname, int target_fd); 45 | 46 | #endif // HAVE_SERIAL_H__ 47 | -------------------------------------------------------------------------------- /gui/uucico.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Serviços de Comunicação Digital 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |

Iniciando Transmissão

14 | 15 |
16 | /dev/null &"; 22 | // echo "UUCP Command: " . $command . "
"; 23 | ob_start(); 24 | system($command , $return_var); 25 | $output = ob_get_contents(); 26 | ob_end_clean(); 27 | } else { 28 | $source = substr ($_POST['myname'], 0, 6); 29 | if($source == $_POST['prefix']) 30 | { 31 | echo "ERRO: Estação de origem é igual estação de destino!
"; 32 | exit; 33 | } 34 | else { 35 | $cmd= "alias.sh ".substr ($_POST['myname'], 0, 6); 36 | $source = shell_exec($cmd); 37 | if ($source == $_POST['prefix']) 38 | { 39 | echo "ERRO: Estação de origem é igual estação de destino!
"; 40 | exit; 41 | } 42 | } 43 | 44 | $command = "nohup sudo uucico -S " . $_POST['prefix'] . " > /dev/null &"; 45 | // echo "UUCP Command: " . $command . "
"; 46 | ob_start(); 47 | system($command , $return_var); 48 | $output = ob_get_contents(); 49 | ob_end_clean(); 50 | } 51 | 52 | ?> 53 |
54 | 55 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /vara.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-connector: A connector to different HF modems 2 | * Copyright (C) 2018 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Vara support routines 21 | */ 22 | 23 | /** 24 | * @file vara.h 25 | * @author Rafael Diniz 26 | * @date 12 Apr 2018 27 | * @brief VARA modem support functions 28 | * 29 | * All the specific code for supporting VARA. 30 | * 31 | */ 32 | 33 | #ifndef HAVE_VARA_H__ 34 | #define HAVE_VARA_H__ 35 | 36 | #include 37 | #include 38 | 39 | #include "uuardopd.h" 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | // here we have the maximum number of messages when we stop sending and wait to get ack from other side (TODO) 46 | #define MAX_VARA_BUFFER 8192 47 | #define MAX_VARA_PACKET_SAFE 16384 48 | 49 | bool initialize_modem_vara(rhizo_conn *connector); 50 | 51 | #ifdef __cplusplus 52 | }; 53 | #endif 54 | 55 | #endif /* HAVE_VARA__ */ 56 | -------------------------------------------------------------------------------- /shm.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop: Tools to integrate Ardop to UUCP 2 | * Copyright (C) 2019 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | */ 21 | 22 | 23 | #ifndef HAVE_SHM_H__ 24 | #define HAVE_SHM_H__ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define SYSV_SHM_KEY_STR 66664 // key for the rhizo_conn struct 33 | #define SYSV_SHM_KEY_IB 66666 // in buffer key (key+1 is also used) 34 | #define SYSV_SHM_KEY_OB 66668 // out buffer key (key+1 is also used) 35 | 36 | // from ubitx controller 37 | #define SYSV_SHM_CONTROLLER_KEY_STR 66650 38 | 39 | bool shm_is_created(key_t key, size_t size); 40 | 41 | // only creates if already not created! 42 | bool shm_create(key_t key, size_t size); 43 | 44 | bool shm_destroy(key_t key, size_t size); 45 | 46 | void *shm_attach(key_t key, size_t size); 47 | 48 | bool shm_dettach(key_t key, size_t size, void *ptr); 49 | 50 | #endif // HAVE_SHM_H__ 51 | -------------------------------------------------------------------------------- /examples/uucp/call: -------------------------------------------------------------------------------- 1 | # 2 | # call This file is used when the ``call-login'' or ``call-password'' 3 | # commands are used in the sys file with a "*" argument (e.g., 4 | # ``call-login *''). The system name is looked up in this file, 5 | # and the login name and password are used. 6 | # 7 | # The point of this is that the sys file may then be publically 8 | # readable, while still concealing the login names and passwords 9 | # used to connect to the remote system. 10 | # 11 | # The format is just system-name login-name password. 12 | # 13 | 14 | #alphen Utester grumblesmurf 15 | AM4AAA user pass 16 | AM4AAB user pass 17 | AM4AAC user pass 18 | AM4AAD user pass 19 | AM4AAE user pass 20 | AM4AAF user pass 21 | AM4AAG user pass 22 | AM4AAH user pass 23 | AM4AAI user pass 24 | AM4AAJ user pass 25 | AM4AAK user pass 26 | AM4AAL user pass 27 | AM4AAM user pass 28 | AM4AAN user pass 29 | AM4AAO user pass 30 | AM4AAP user pass 31 | AM4AAQ user pass 32 | AM4AAR user pass 33 | AM4AAS user pass 34 | AM4AAT user pass 35 | AM4AAU user pass 36 | AM4AAV user pass 37 | AM4AAW user pass 38 | AM4AAX user pass 39 | AM4AAY user pass 40 | AM4AAZ user pass 41 | AM4ABA user pass 42 | AM4ABB user pass 43 | AM4ABC user pass 44 | AM4ABD user pass 45 | AM4ABE user pass 46 | AM4ABF user pass 47 | AM4ABG user pass 48 | AM4ABH user pass 49 | AM4ABI user pass 50 | AM4ABJ user pass 51 | AM4ABK user pass 52 | AM4ABL user pass 53 | AM4ABM user pass 54 | AM4ABN user pass 55 | AM4ABO user pass 56 | AM4ABP user pass 57 | AM4ABQ user pass 58 | AM4ABR user pass 59 | AM4ABS user pass 60 | AM4ABT user pass 61 | AM4ABU user pass 62 | AM4ABV user pass 63 | AM4ABW user pass 64 | AM4ABX user pass 65 | AM4ABY user pass 66 | AM4ABZ user pass 67 | -------------------------------------------------------------------------------- /ardop.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-connector: A connector to different HF modems 2 | * Copyright (C) 2018 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Ardop support routines 21 | */ 22 | 23 | /** 24 | * @file ardop.h 25 | * @author Rafael Diniz 26 | * @date 12 Apr 2018 27 | * @brief Ardop modem support functions 28 | * 29 | * All the specific code for supporting Ardop. 30 | * 31 | */ 32 | 33 | #ifndef HAVE_ARDOP_H__ 34 | #define HAVE_ARDOP_H__ 35 | 36 | #include 37 | #include 38 | 39 | #include "uuardopd.h" 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | // John Wiseman says 4096 is the maximum... 46 | #define MAX_ARDOP_PACKET 1024 47 | 48 | // Maximum internal safe ardop buffer size (being safer...) 49 | #define MAX_ARDOP_BUFFER 6000 50 | 51 | #define MAX_TIMEOUT 240 // maximum timeout 52 | 53 | // 2 bytes max - standard is not clear which is the max size... 54 | #define MAX_ARDOP_PACKET_SAFE 65535 55 | 56 | bool initialize_modem_ardop(rhizo_conn *connector); 57 | 58 | #ifdef __cplusplus 59 | }; 60 | #endif 61 | 62 | #endif /* HAVE_ARDOP_H__ */ 63 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Rhizo-uuardop: UUCP <-> HF tools 2 | # Copyright (C) 2019-2021 Rhizomatica 3 | # Author: Rafael Diniz 4 | # 5 | # This is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # This software is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this software; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | # 20 | 21 | 22 | prefix=/usr 23 | CC=gcc 24 | CFLAGS=-g -Wall -pedantic -std=gnu11 -pthread -fstack-protector 25 | 26 | 27 | all: uuardopd uuport 28 | 29 | %.o : %.c %.h 30 | gcc -c $(CFLAGS) $< -o $@ 31 | 32 | uuardopd: ardop.o shm.o common.o net.o circular_buffer.o uuardopd.o call_uucico.o serial.o vara.o 33 | $(CC) $(CFLAGS) -o $@ $^ 34 | 35 | uuport: uuport.o shm.o circular_buffer.o 36 | $(CC) $(CFLAGS) -o $@ $^ 37 | 38 | 39 | .PHONY: clean install doc 40 | install: uuardopd uuport 41 | install -D uuardopd $(DESTDIR)$(prefix)/bin/uuardopd 42 | install -D uuport $(DESTDIR)$(prefix)/bin/uuport 43 | install -m 644 -D initscripts/uuardopd.service $(DESTDIR)/etc/systemd/system/uuardopd.service 44 | install scripts/vara_watchdog.sh $(DESTDIR)$(prefix)/bin 45 | 46 | install_gateway: install 47 | install -m 644 -D initscripts/caller.service $(DESTDIR)/etc/systemd/system/caller.service 48 | install scripts/caller.sh $(DESTDIR)$(prefix)/bin 49 | 50 | doc: 51 | doxygen doxyfile 52 | 53 | clean: 54 | rm -rf uuardopd uuport *.o *~ doc 55 | -------------------------------------------------------------------------------- /gui/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 |
19 |

20 | send Transmitir 21 |

22 |
23 | 24 |
25 |

26 | get_app 27 | Arquivos Recebidos 28 |

29 |
30 | 31 | 37 | 38 | 44 | 45 | 51 | 52 | 58 | 59 | 64 | 65 | 70 | 71 |
72 |
73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/uucp/expire: -------------------------------------------------------------------------------- 1 | # 2 | # This configuration file is used by uudemon.day 3 | # uudemon.day is run daily from cron.daily and sends status information 4 | # to the administrator. It also bounces old mail and kills old news. 5 | # Furthermore it reports a list of old jobs to the admin. 6 | # 7 | # You can configure the time after which news and mail expire as well as how 8 | # old jobs must be so they get reported in the daily status information. 9 | # You can set a global default and override this number for each system. 10 | # 11 | 12 | 13 | #### NEWS #### 14 | 15 | # Specify how long rnews commands stay in the spool before 16 | # they are killed. You can specify a default for all systems 17 | # and override this setting on a per system basis. 18 | # 19 | # The time is to be given in hours with 0 meaning that 20 | # jobs will be kept forever. 21 | 22 | # news will be kept for 2 weeks by default 23 | $news = 336; 24 | 25 | # # for system alpamayo only for 24 hours 26 | # $news{'alpamayo'} = 24; 27 | 28 | 29 | 30 | #### MAIL #### 31 | 32 | # Specify how long rmail commands stay in the spool before 33 | # they are killed and a bounce message is sent to the author 34 | # or the mail message. You can specify a default for all systems 35 | # and override this on a per system basis. 36 | # 37 | # Time in hours, 0 means jobs will be kept forever 38 | 39 | # mail will be bounced after 672 hours or 4 weeks. 40 | $mail = 672; 41 | 42 | # # but keep it longer for system marvin and defiant 43 | # $mail{'marvin'} = 672*4; 44 | # $mail{'defiant'} = 672*2; 45 | 46 | 47 | 48 | #### LISTING #### 49 | 50 | # Jobs of a certain age will be listed in the daily report the 51 | # local administrator gets. 52 | 53 | # Jobs older than one week will be listed in the daily report 54 | $list = 168; 55 | 56 | # # Don't report jobs for system defiant at all. 57 | # $list{'defiant'} = 0; 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | # After which time (__in days__) to expire files in uucppublic 66 | 67 | $expire_uucppublic = 14; # NOTE: This time is in days; 68 | # system specific settings are not supported 69 | 70 | 71 | 72 | # If this is set to 1, only important reports will be sent. A report is important 73 | # if there's more than just uustat and uutraf/uurate output. 74 | $important_only = 0; 75 | 76 | # Don't edit stuff below here 77 | $expireversion = 1; 78 | 1; 79 | -------------------------------------------------------------------------------- /gui/tx.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 18 | 19 | 20 |
21 | 22 | 23 | 24 |
25 |
26 |

27 | Especificar Estação de Destino? 28 | 29 |

30 | 31 |

32 | 33 | 36 |

37 |
38 | 39 |
40 |

41 | 42 |

43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 |

Fila de Transmissão

51 | 52 | 62 |
63 | 64 | 65 |
66 |
67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /gui/receive.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | $value){ 21 | $path = realpath($dir.DIRECTORY_SEPARATOR.$value); 22 | if(is_dir($path) && $value != "." && $value != "..") { 23 | echo "
Arquivos de Origem da Estação ".$value."

"; 24 | $files_st = scandir($dir.DIRECTORY_SEPARATOR.$value); 25 | $class="bodywt"; 26 | foreach($files_st as $key_st => $value_st){ 27 | $path_st = realpath($dir.DIRECTORY_SEPARATOR.$value.DIRECTORY_SEPARATOR.$value_st); 28 | if(!is_dir($path_st)) { 29 | $file_ext = pathinfo($path_st, PATHINFO_EXTENSION); 30 | if ($class == "bodywt"){ 31 | echo "
"; 32 | $class="body"; 33 | } else { 34 | echo "
"; 35 | $class="bodywt"; 36 | } 37 | if ($file_ext=="gpg") { 38 | echo $value_st; 39 | echo "
"; 40 | echo "
Senha: "; 41 | echo ""; 42 | echo ""; 43 | echo ""; 44 | echo "
"; 45 | } 46 | else { 47 | echo "".$value_st.""; 48 | } 49 | /* echo "
"; */ 50 | echo "
"; 51 | } 52 | /* $results[] = $path; */ 53 | } 54 | } 55 | } 56 | 57 | if ($class == "bodywt"){ 58 | echo "
"; 59 | } else { 60 | echo "
"; 61 | } 62 | 63 | echo "cancel 64 | Limpar Todos Arquivos"; 65 | 66 | echo "
"; 67 | ?> 68 | 69 | 70 | 71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-HF-connector: A connector to different HF modems 2 | * Copyright (C) 2018 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Functions used by any type of modem 21 | */ 22 | 23 | /** 24 | * @file common.c 25 | * @author Rafael Diniz 26 | * @date 09 May 2018 27 | * @brief Common functions used by any type of modem 28 | * 29 | * Common functions used by any type of modem. 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "uuardopd.h" 50 | #include "common.h" 51 | 52 | 53 | bool inotify_wait(char *file_name){ 54 | struct stat buffer; 55 | if (stat(file_name, &buffer) != 0) 56 | { 57 | if (mkfifo(file_name, S_IRWXU | S_IRWXG | S_IRWXO) != 0){ 58 | fprintf(stderr, "Failed to create fifo: %s\n", file_name); 59 | return false; 60 | } 61 | chown(file_name, 10, 20); 62 | } 63 | 64 | #define EVENT_SIZE (sizeof(struct inotify_event)) 65 | #define BUF_LEN (1024 * (EVENT_SIZE + 16)) 66 | int length, i = 0; 67 | int fd; 68 | int wd; 69 | char buffer_inot[BUF_LEN]; 70 | 71 | fd = inotify_init(); 72 | 73 | if (fd < 0) { 74 | perror("inotify_init"); 75 | } 76 | 77 | wd = inotify_add_watch(fd, file_name, IN_CREATE | IN_DELETE); 78 | 79 | length = read(fd, buffer_inot, BUF_LEN); 80 | 81 | if (length < 0) { 82 | perror("read"); 83 | } 84 | 85 | i = 0; 86 | while (i < length) { 87 | struct inotify_event *event = (struct inotify_event *) &buffer_inot[i]; 88 | i += EVENT_SIZE + event->len; 89 | // fprintf(stderr, "Got some unnamed event!\n"); 90 | } 91 | 92 | inotify_rm_watch(fd, wd); 93 | close(fd); 94 | 95 | sleep(1); 96 | 97 | return true; 98 | } 99 | -------------------------------------------------------------------------------- /gui/load.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serviços de Comunicação Digital 6 | 7 | 18 | 19 | 20 |
21 | 22 | 23 |
24 |
25 | radio 26 | Estação de Destino 27 | 28 | 31 |
32 | 33 |
34 | open_in_browser 35 | Arquivo 36 | 37 |
38 | 39 |
40 | enhanced_encryption 41 | Proteger com Senha? 42 | 43 |
44 | 45 | 46 | 47 |
48 |
49 | 50 | 51 | 52 | 53 |
54 | 55 | 56 |
57 |

Fila de Transmissão

58 | 68 |
69 | 70 |
71 | Os arquivos acima estão na fila para transmissão antes do seu. Deseja cancelar a transmissão desses arquivos?
72 |
73 | 74 |
75 |
76 | 77 | 78 | 79 | 80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /net.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-connector: A connector to different HF modems 2 | * Copyright (C) 2018 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Network related functions 21 | */ 22 | 23 | /** 24 | * @file net.c 25 | * @author Rafael Diniz 26 | * @date 12 Apr 2018 27 | * @brief File containing network related functions 28 | * 29 | * For the sake of reusable and clean code, some network related functions. 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "net.h" 44 | 45 | bool tcp_connect(char *ip, int port, int *sockt){ 46 | struct sockaddr_in ardop_addr; 47 | 48 | *sockt = socket(PF_INET, SOCK_STREAM, 0); 49 | 50 | ardop_addr.sin_family = AF_INET; 51 | ardop_addr.sin_port = htons(port); 52 | ardop_addr.sin_addr.s_addr = inet_addr(ip); 53 | if (ardop_addr.sin_addr.s_addr == INADDR_NONE){ 54 | fprintf(stderr, "Invalid IP address.\n"); 55 | return false; 56 | } 57 | 58 | memset(ardop_addr.sin_zero, 0, sizeof(ardop_addr.sin_zero)); 59 | 60 | if (connect(*sockt, (struct sockaddr *) &ardop_addr, sizeof(ardop_addr)) != 0) 61 | return false; 62 | 63 | return true; 64 | } 65 | 66 | bool tcp_read(int sockt, uint8_t *buffer, size_t rx_size){ 67 | ssize_t len; 68 | size_t rcv_counter = 0; 69 | 70 | while (rcv_counter < rx_size){ 71 | int trx_size = (rx_size - rcv_counter > TCP_BLOCK)? TCP_BLOCK : rx_size - rcv_counter; 72 | 73 | len = recv(sockt, buffer + rcv_counter, trx_size, 0); 74 | if (len == 0){ 75 | fprintf(stderr, "tcp_read: socket read error.\n"); 76 | return false; 77 | } 78 | rcv_counter += len; 79 | } 80 | 81 | return true; 82 | } 83 | 84 | bool tcp_write(int sockt, uint8_t *buffer, size_t tx_size){ 85 | size_t len = send(sockt, buffer, tx_size, 0); 86 | if (len != tx_size) { 87 | fprintf(stderr, "tcp_write: socket write error.\n"); 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | -------------------------------------------------------------------------------- /scripts/caller.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # delay between each call 4 | DELAY=2 5 | 6 | # delay for errors or in moments outside any scheduled operation 7 | DELAY_MAINLOOP=15 8 | 9 | # central station email server uucp address 10 | EMAIL_SERVER="gw" 11 | 12 | LATEST_SERVER_CALL_TIME=$(date -u +%s) 13 | TIME_TO_RUN=$(( ${LATEST_SERVER_CALL_TIME} + ${DELAY_MAINLOOP} )) 14 | 15 | while true 16 | do 17 | hosts=($(curl -s http://localhost/api/caller/ | jq --raw-output '.[0] | .stations[]' 2> /dev/null)) 18 | timers_start=($(curl -s http://localhost/api/caller | jq --raw-output '.[] | select( .enable | contains(1)) | .starttime ' 2> /dev/null)) 19 | timers_stop=($(curl -s http://localhost/api/caller | jq --raw-output '.[] | select( .enable | contains(1)) | .stoptime ' 2> /dev/null)) 20 | 21 | TIME_NOW=$(date -u +%s) 22 | if [ "${TIME_NOW}" -gt "${TIME_TO_RUN}" ] 23 | then 24 | uucico -D -S ${EMAIL_SERVER} 25 | LATEST_SERVER_CALL_TIME=$(date -u +%s) 26 | TIME_TO_RUN=$(( ${LATEST_SERVER_CALL_TIME} + ${DELAY_MAINLOOP} )) 27 | fi 28 | 29 | if [[ -z ${hosts} ]] || [[ -z ${timers_start} ]] || [[ -z ${timers_stop} ]] 30 | then 31 | echo "No schedule enabled or API call failed" 32 | sleep ${DELAY_MAINLOOP} 33 | continue 34 | fi 35 | 36 | run_at_least_once=0 37 | 38 | for (( c=0; c<${#timers_start[@]}; c++ )); do 39 | 40 | start_time_hour=$((10#$(echo ${timers_start[c]} | cut -d ':' -f 1))) 41 | start_time_minute=$((10#$(echo ${timers_start[c]} | cut -d ':' -f 2))) 42 | end_time_hour=$((10#$(echo ${timers_stop[c]} | cut -d ':' -f 1))) 43 | end_time_minute=$((10#$(echo ${timers_stop[c]} | cut -d ':' -f 2))) 44 | 45 | current_hour=$((10#$(date +%H))) 46 | current_minute=$((10#$(date +%M))) 47 | 48 | echo "Schedule ${c}" 49 | # echo "current time ${current_hour}h ${current_minute}min" 50 | # echo "start time ${start_time_hour}h ${start_time_minute}min" 51 | # echo "end time ${end_time_hour}h ${end_time_minute}min" 52 | 53 | if [[ ${current_hour} -eq ${start_time_hour} && 54 | ${current_minute} -ge ${start_time_minute} ]] || 55 | [[ ${current_hour} -gt ${start_time_hour} && 56 | ${current_hour} -lt ${end_time_hour} ]] || 57 | [[ ${current_hour} -eq ${end_time_hour} && 58 | ${current_minute} -le ${end_time_minute} ]] 59 | 60 | then 61 | 62 | run_at_least_once=1 63 | for t in ${hosts[*]}; do 64 | echo "Calling ${t}" 65 | uucico -D -S ${t} 66 | sleep ${DELAY} 67 | # here we sync with server again... as connection times can be veeery long 68 | uucico -D -S ${EMAIL_SERVER} 69 | done 70 | 71 | else 72 | echo "Schedule ${c} will not run now." 73 | fi 74 | 75 | done 76 | 77 | if [[ ${run_at_least_once} -eq 0 ]] 78 | then 79 | sleep ${DELAY_MAINLOOP} 80 | fi 81 | 82 | done 83 | -------------------------------------------------------------------------------- /examples/uucp/sys: -------------------------------------------------------------------------------- 1 | # 2 | # sys This is the file where you define all the remote 3 | # UUCP systems you know about, whether you dial in to them 4 | # or they dial in to you. 5 | # 6 | # Note that if you want to dial into another system, you 7 | # have to enter the loginname and password NOT here but 8 | # in the file /etc/uucp/call. 9 | # 10 | 11 | # 12 | # First some defaults. These are for all other entries (unless overridden). 13 | # 14 | #protocol gvG 15 | #protocol-parameter G packet-size 1024 16 | # protocol-parameter G window 7 17 | #protocol-parameter G short-packets 18 | 19 | protocol y 20 | protocol-parameter y packet-size 512 21 | protocol-parameter y timeout 1800 22 | chat-timeout 100 23 | 24 | 25 | call-login * 26 | call-password * 27 | time any 28 | port HFP 29 | chat "" \r 30 | 31 | local-send / 32 | local-receive / 33 | remote-send / 34 | remote-receive / 35 | 36 | # 37 | # Our remote uucp connection. 38 | # 39 | # system remote 40 | 41 | system AM4AAA 42 | alias ISA 43 | system AM4AAB 44 | alias Sao_Bento 45 | system AM4AAC 46 | alias Rio_Novo 47 | system AM4AAD 48 | alias Sao_Francisco 49 | system AM4AAE 50 | alias Manelito 51 | system AM4AAF 52 | alias Cachoeirinha 53 | system AM4AAG 54 | alias Morro_Verde 55 | system AM4AAH 56 | alias Morro_Anfrisio 57 | system AM4AAI 58 | alias Paulo_Afonso 59 | system AM4AAJ 60 | alias Baliza 61 | system AM4AAK 62 | alias Gabiroto 63 | system AM4AAL 64 | alias Bela_Vista 65 | system AM4AAM 66 | alias am4AAM 67 | system AM4AAN 68 | alias am4AAN 69 | system AM4AAO 70 | alias am4AAO 71 | system AM4AAP 72 | alias am4AAP 73 | system AM4AAQ 74 | alias am4AAQ 75 | system AM4AAR 76 | alias am4AAR 77 | system AM4AAS 78 | alias am4AAS 79 | system AM4AAT 80 | alias am4AAT 81 | system AM4AAU 82 | alias am4AAU 83 | system AM4AAV 84 | alias am4AAV 85 | system AM4AAW 86 | alias am4AAW 87 | system AM4AAX 88 | alias am4AAX 89 | system AM4AAY 90 | alias am4AAY 91 | system AM4AAZ 92 | alias am4AAZ 93 | system AM4ABA 94 | alias am4ABA 95 | system AM4ABB 96 | alias am4ABB 97 | system AM4ABC 98 | alias am4ABC 99 | system AM4ABD 100 | alias am4ABD 101 | system AM4ABE 102 | alias am4ABE 103 | system AM4ABF 104 | alias am4ABF 105 | system AM4ABG 106 | alias am4ABG 107 | system AM4ABH 108 | alias am4ABH 109 | system AM4ABI 110 | alias am4ABI 111 | system AM4ABJ 112 | alias am4ABJ 113 | system AM4ABK 114 | alias am4ABK 115 | system AM4ABL 116 | alias am4ABL 117 | system AM4ABM 118 | alias am4ABM 119 | system AM4ABN 120 | alias am4ABN 121 | system AM4ABO 122 | alias am4ABO 123 | system AM4ABP 124 | alias am4ABP 125 | system AM4ABQ 126 | alias am4ABQ 127 | system AM4ABR 128 | alias am4ABR 129 | system AM4ABS 130 | alias am4ABS 131 | system AM4ABT 132 | alias am4ABT 133 | system AM4ABU 134 | alias am4ABU 135 | system AM4ABV 136 | alias am4ABV 137 | system AM4ABW 138 | alias am4ABW 139 | system AM4ABX 140 | alias am4ABX 141 | system AM4ABY 142 | alias am4ABY 143 | system AM4ABZ 144 | alias am4ABZ 145 | -------------------------------------------------------------------------------- /shm.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop: Tools to integrate Ardop to UUCP 2 | * Copyright (C) 2019 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Routines to call uucico when receiving a call 21 | */ 22 | 23 | /** 24 | * @file shm.c 25 | * @author Rafael Diniz 26 | * @date 14 Aug 2019 27 | * @brief Shared memory routines 28 | * 29 | * Shared memory routines 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "shm.h" 56 | 57 | bool shm_is_created(key_t key, size_t size) 58 | { 59 | int shmid = shmget(key, size, 0); 60 | 61 | if (shmid == -1) 62 | { 63 | return false; 64 | } 65 | 66 | return true; 67 | } 68 | 69 | // check of key is already not created before calling this! 70 | bool shm_create(key_t key, size_t size) 71 | { 72 | int shmid = shmget(key, size, 0666 | IPC_CREAT | IPC_EXCL); 73 | 74 | if (shmid == -1) 75 | { 76 | return false; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | bool shm_destroy(key_t key, size_t size) 83 | { 84 | int shmid = shmget(1, size, 0); 85 | 86 | if (shmid == -1) 87 | { 88 | return false; 89 | } 90 | 91 | shmctl(shmid,IPC_RMID,NULL); 92 | 93 | return true; 94 | } 95 | 96 | void *shm_attach(key_t key, size_t size) 97 | { 98 | int shmid = shmget(key, size, 0); 99 | 100 | if (shmid == -1) 101 | { 102 | return NULL; 103 | } 104 | 105 | return shmat(shmid, NULL, 0); 106 | } 107 | 108 | bool shm_dettach(key_t key, size_t size, void *ptr) 109 | { 110 | int shmid = shmget(key, size, 0); 111 | 112 | if (shmid == -1) 113 | { 114 | return false; 115 | } 116 | 117 | shmdt(ptr); 118 | 119 | return true; 120 | } 121 | -------------------------------------------------------------------------------- /circular_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef CIRCULAR_BUFFER_H_ 2 | #define CIRCULAR_BUFFER_H_ 3 | 4 | #include 5 | 6 | // The definition of our circular buffer structure is hidden from the user 7 | struct circular_buf_t_aux { 8 | size_t head; 9 | size_t tail; 10 | size_t max; //of the buffer 11 | bool full; 12 | atomic_flag acquire; 13 | }; 14 | 15 | struct circular_buf_t { 16 | struct circular_buf_t_aux *internal; 17 | uint8_t *buffer; 18 | }; 19 | 20 | /// Opaque circular buffer structure 21 | typedef struct circular_buf_t circular_buf_t; 22 | 23 | /// Handle type, the way users interact with the API 24 | typedef circular_buf_t *cbuf_handle_t; 25 | 26 | /// Pass in a storage buffer and size, returns a circular buffer handle 27 | /// Requires: buffer is not NULL, size > 0 28 | /// Ensures: cbuf has been created and is returned in an empty state 29 | cbuf_handle_t circular_buf_init(uint8_t *buffer, size_t size); 30 | 31 | cbuf_handle_t circular_buf_init_shm(size_t size, key_t key); 32 | 33 | cbuf_handle_t circular_buf_connect_shm(size_t size, key_t key); 34 | 35 | /// Free a circular buffer structure 36 | /// Requires: cbuf is valid and created by circular_buf_init 37 | /// Does not free data buffer; owner is responsible for that 38 | void circular_buf_free(cbuf_handle_t cbuf); 39 | 40 | void circular_buf_free_shm(cbuf_handle_t cbuf, size_t size, key_t key); 41 | 42 | /// Reset the circular buffer to empty, head == tail. Data not cleared 43 | /// Requires: cbuf is valid and created by circular_buf_init 44 | void circular_buf_reset(cbuf_handle_t cbuf); 45 | 46 | /// Put version 1 continues to add data if the buffer is full 47 | /// Old data is overwritten 48 | /// Requires: cbuf is valid and created by circular_buf_init 49 | void circular_buf_put(cbuf_handle_t cbuf, uint8_t data); 50 | 51 | /// Put Version 2 rejects new data if the buffer is full 52 | /// Requires: cbuf is valid and created by circular_buf_init 53 | /// Returns 0 on success, -1 if buffer is full 54 | int circular_buf_put2(cbuf_handle_t cbuf, uint8_t data); 55 | 56 | /// Retrieve a value from the buffer 57 | /// Requires: cbuf is valid and created by circular_buf_init 58 | /// Returns 0 on success, -1 if the buffer is empty 59 | int circular_buf_get(cbuf_handle_t cbuf, uint8_t * data); 60 | 61 | /// CHecks if the buffer is empty 62 | /// Requires: cbuf is valid and created by circular_buf_init 63 | /// Returns true if the buffer is empty 64 | bool circular_buf_empty(cbuf_handle_t cbuf); 65 | 66 | /// Checks if the buffer is full 67 | /// Requires: cbuf is valid and created by circular_buf_init 68 | /// Returns true if the buffer is full 69 | bool circular_buf_full(cbuf_handle_t cbuf); 70 | 71 | /// Check the capacity of the buffer 72 | /// Requires: cbuf is valid and created by circular_buf_init 73 | /// Returns the maximum capacity of the buffer 74 | size_t circular_buf_capacity(cbuf_handle_t cbuf); 75 | 76 | /// Check the number of elements stored in the buffer 77 | /// Requires: cbuf is valid and created by circular_buf_init 78 | /// Returns the current number of elements in the buffer 79 | size_t circular_buf_size(cbuf_handle_t cbuf); 80 | 81 | size_t circular_buf_free_size(cbuf_handle_t cbuf); 82 | 83 | /// Check the number of free elements in the buffer 84 | /// Requires: cbuf is valid and created by circular_buf_init 85 | /// Returns the current number of free elements in the buffer 86 | size_t circular_buf_free_size(cbuf_handle_t cbuf); 87 | 88 | int circular_buf_get_range(cbuf_handle_t cbuf, uint8_t *data, size_t len); 89 | 90 | int circular_buf_put_range(cbuf_handle_t cbuf, uint8_t * data, size_t len); 91 | 92 | #endif //CIRCULAR_BUFFER_H_ 93 | -------------------------------------------------------------------------------- /uuardopd.h: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuhf: Tools to integrate HF TNCs to UUCP 2 | * Copyright (C) 2019-2021 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | */ 21 | 22 | /** 23 | * @file uuardopd.h 24 | * @author Rafael Diniz 25 | * @date 10 Jul 2019 26 | * @brief UUCP ARDOP daemon header 27 | * 28 | * UUCP ARDOP daemon 29 | * 30 | */ 31 | 32 | #ifndef HAVE_UUARDOPD_H__ 33 | #define HAVE_UUARDOPD_H__ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "circular_buffer.h" 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | #define UUCP_CONFIG "/etc/uucp/config" 47 | 48 | // 30s 49 | #define TIMEOUT_DEFAULT 30 50 | 51 | #define INTERNAL_BUFFER_SIZE 1048576 // (2 ^ 20) 52 | 53 | #define BUFFER_SIZE 4096 54 | 55 | typedef struct{ 56 | // modem related data 57 | char call_sign[32]; 58 | char remote_call_sign[32]; 59 | int tcp_base_port; 60 | char ip_address[32]; 61 | char modem_type[32]; 62 | int radio_type; 63 | bool serial_keying; 64 | int serial_fd; 65 | char serial_path[1024]; 66 | bool ofdm_mode; 67 | uint16_t vara_mode; 68 | bool ask_login; 69 | int timeout; 70 | int data_socket; 71 | int control_socket; 72 | 73 | // pipe fd's for talking to exec'ed uucico (recv call) 74 | int pipefd1[2]; 75 | int pipefd2[2]; 76 | 77 | // state variables (TODO: use FSM!) 78 | // C11 atomic is used here instead of a more pedantic code with mutexes and so on... 79 | atomic_bool shutdown; 80 | atomic_bool connected; 81 | atomic_bool waiting_for_connection; 82 | atomic_bool clean_buffers; 83 | atomic_bool uucico_active; 84 | atomic_bool send_break; 85 | 86 | atomic_int session_counter_read; 87 | atomic_int session_counter_write; 88 | 89 | // internal ardop buffer size 90 | atomic_int buffer_size; 91 | 92 | // uuardopd private buffers 93 | cbuf_handle_t in_buffer; 94 | cbuf_handle_t out_buffer; 95 | //uuport private buffers 96 | cbuf_handle_t in_buffer_p; 97 | cbuf_handle_t out_buffer_p; 98 | } rhizo_conn; 99 | 100 | // from ubitx_controller.h - TODO: share the same header across projects! 101 | typedef struct{ 102 | 103 | uint8_t service_command[5]; 104 | pthread_mutex_t cmd_mutex; 105 | pthread_cond_t cmd_condition; 106 | 107 | pthread_mutex_t response_mutex; 108 | 109 | uint8_t response_service[5]; 110 | atomic_bool response_service_type; 111 | atomic_bool response_available; 112 | 113 | // special response for ptt 114 | atomic_uchar ptt_last_response; 115 | 116 | // Protection alert ON! 117 | atomic_bool protection_alert; 118 | 119 | int radio_fd; 120 | 121 | } controller_conn; 122 | 123 | 124 | #ifdef __cplusplus 125 | }; 126 | #endif 127 | 128 | #endif /* HAVE_UUARDOPD_H__ */ 129 | -------------------------------------------------------------------------------- /gui/style.css: -------------------------------------------------------------------------------- 1 | .header { 2 | padding: 20px; 3 | text-align: left; 4 | background: #f90; 5 | color: white; 6 | font-size: 14px; 7 | } 8 | 9 | .header h1 { 10 | font-size: 40px; 11 | 12 | } 13 | 14 | body { 15 | max-width: 800px; 16 | margin: 0 auto; 17 | font-family: Arial, Helvetica, sans-serif; 18 | 19 | 20 | } 21 | 22 | h1 { 23 | font-size: 25px; 24 | text-align: left; 25 | } 26 | 27 | h2 { 28 | font-size: 20px; 29 | text-align: left; 30 | } 31 | 32 | h2 a { 33 | text-decoration: none; 34 | color: #000; 35 | } 36 | 37 | .results { 38 | font-size: 12px; 39 | background: #484848; 40 | color: white; 41 | padding: 20px; 42 | } 43 | 44 | .body { 45 | font-family: Arial, Helvetica, sans-serif; 46 | padding: 20px; 47 | margin: 0; 48 | font-size: 20px; 49 | background: #fff; 50 | border-top: 3px solid #f60; 51 | 52 | } 53 | 54 | .body:hover { 55 | background: #eee; 56 | 57 | } 58 | 59 | .bodywt { 60 | font-family: Arial, Helvetica, sans-serif; 61 | padding: 20px; 62 | margin: 0; 63 | font-size: 20px; 64 | border-top: 3px solid #f60; 65 | } 66 | 67 | .bodywt:hover { 68 | background: #eee; 69 | 70 | } 71 | 72 | .navbar { 73 | overflow: hidden; 74 | background-color: #333; 75 | position: sticky; 76 | position: -webkit-sticky; 77 | top: 0; 78 | } 79 | 80 | h1 a { 81 | text-decoration: none; 82 | color: #000; 83 | } 84 | 85 | h1:hover a { 86 | color: #f60; 87 | } 88 | 89 | 90 | 91 | /* Style the navigation bar links */ 92 | .navbar a { 93 | float: left; 94 | display: block; 95 | color: white; 96 | text-align: center; 97 | padding: 14px 20px; 98 | text-decoration: none; 99 | } 100 | 101 | 102 | /* Right-aligned link */ 103 | .navbar a.right { 104 | float: right; 105 | } 106 | 107 | /* Change color on hover */ 108 | .navbar a:hover { 109 | background-color: #ddd; 110 | color: black; 111 | } 112 | 113 | /* Active/current link */ 114 | .navbar a.active { 115 | background-color: #666; 116 | color: white; 117 | } 118 | 119 | /* Column container */ 120 | .row { 121 | display: -ms-flexbox; /* IE10 */ 122 | display: flex; 123 | -ms-flex-wrap: wrap; /* IE10 */ 124 | flex-wrap: wrap; 125 | } 126 | 127 | /* Create two unequal columns that sits next to each other */ 128 | /* Sidebar/left column */ 129 | .side { 130 | -ms-flex: 30%; /* IE10 */ 131 | flex: 30%; 132 | background-color: #f1f1f1; 133 | padding: 20px; 134 | } 135 | 136 | /* Main column */ 137 | .main { 138 | -ms-flex: 70%; /* IE10 */ 139 | flex: 70%; 140 | background-color: white; 141 | padding: 20px; 142 | } 143 | 144 | /* Fake image, just for this example */ 145 | .fakeimg { 146 | background-color: #aaa; 147 | width: 100%; 148 | padding: 20px; 149 | } 150 | 151 | /* Footer */ 152 | .footer { 153 | padding: 20px; 154 | text-align: center; 155 | background: #ddd; 156 | } 157 | 158 | /* Responsive layout - when the screen is less than 700px wide, make the two columns stack on top of each other instead of next to each other */ 159 | @media screen and (max-width: 700px) { 160 | .row { 161 | flex-direction: column; 162 | } 163 | } 164 | 165 | /* Responsive layout - when the screen is less than 400px wide, make the navigation links stack on top of each other instead of next to each other */ 166 | @media screen and (max-width: 400px) { 167 | .navbar a { 168 | float: none; 169 | width: 100%; 170 | } 171 | } 172 | 173 | .form { 174 | margin: 0 0 2em 0; 175 | background-color: #aaa; 176 | display: list-item; 177 | } 178 | 179 | @font-face { 180 | font-family: 'Material Icons'; 181 | font-style: normal; 182 | font-weight: 400; 183 | src: url(iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */ 184 | src: local('Material Icons'), 185 | local('MaterialIcons-Regular'), 186 | url(iconfont/MaterialIcons-Regular.woff2) format('woff2'), 187 | url(iconfont/MaterialIcons-Regular.woff) format('woff'), 188 | url(iconfont/MaterialIcons-Regular.ttf) format('truetype'); 189 | } 190 | .material-icons { 191 | font-family: 'Material Icons'; 192 | font-weight: normal; 193 | font-style: normal; 194 | font-size: 40px; /* Preferred icon size */ 195 | display: inline-block; 196 | line-height: 1; 197 | text-transform: none; 198 | letter-spacing: normal; 199 | word-wrap: normal; 200 | white-space: nowrap; 201 | direction: ltr; 202 | 203 | /* Support for all WebKit browsers. */ 204 | -webkit-font-smoothing: antialiased; 205 | /* Support for Safari and Chrome. */ 206 | text-rendering: optimizeLegibility; 207 | 208 | /* Support for Firefox. */ 209 | -moz-osx-font-smoothing: grayscale; 210 | 211 | /* Support for IE. */ 212 | font-feature-settings: 'liga'; 213 | } 214 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rhizo-uuardop 2 | RHIZO-UUARDOP is a set of tools which allow UUCP to use ARDOP or VARA as modem. With 3 | this integration, UUCP is fully functional over HF links. 4 | 5 | Rhizo-uuardop comes with two tools: uuardopd and uuport. 6 | 7 | UUARDOPD is the daemon which keeps connected to ARDOP or VARA modem and properly 8 | receive calls (calling uucico) and initiate calls (uucico calls thought 9 | UUPORT connection). 10 | 11 | UUPORT is the command invoked by UUCICO (using port type = pipe) when 12 | initiating a call (uucico master mode). Communication between uuport and uuardopd is done over shared memory. 13 | 14 | ## UUARDOPD Usage 15 | 16 | | Option | Description | 17 | | --- | --- | 18 | | -c callsign | Station Callsign (Eg: PU2HFF) | 19 | | -d remote_callsign | Remote Station Callsign (optional) | 20 | | -r [ardop,vara] | Choose modem/radio type | 21 | | -a tnc_ip_address | IP address of the TNC | 22 | | -p tcp_base_port | TCP base port of the TNC. ARDOP uses ports tcp_base_port and tcp_base_port+1 | 23 | | -t timeout | Time to wait before disconnect when idling (ARDOP ONLY) | 24 | | -f features | Enable/Disable features. | 25 | | | Supported features ARDOP: ofdm, noofdm (default: ofdm) | 26 | | | Supported features VARA, BW mode: 500, 2300 or 2750 (default: 2300) | 27 | | -s serial_device | Set the serial device file path for keying the radio (VARA ONLY) | 28 | | -l | Tell UUCICO to ask login prompt (default: disabled) | 29 | | -o [icom,ubitx,shm] | Sets radio type (supported: icom, ubitx or shm). | 30 | | -h | Prints this help | 31 | 32 | 33 | ## UUPORT Usage 34 | 35 | | Option | Description | 36 | | --- | --- | 37 | | -c system_name | Name of the remote system (default is don't change). | 38 | | -e logfile.txt | Log file (default is stderr). | 39 | | -h | Prints this help | 40 | 41 | ### Install 42 | 43 | To compile and install, type: 44 | 45 | $ make 46 | $ make install 47 | 48 | ### Configuration 49 | 50 | Port configuration example at "/etc/uucp/port": 51 | 52 | port HFP 53 | type pipe 54 | command /usr/bin/uuport 55 | 56 | An alternative Port configuration if you use a patched uucp ( for "\Z" 57 | support, available in "improved-pipe.patch" which was added to uucp debian 58 | package version 1.07-27 ), where uuport pass 59 | the callsign of the station to be called to uuardopd with the uucp remote 60 | station name (allowing a single uuardopd instance to be used for different 61 | remote station callsigns): 62 | 63 | port HFP 64 | type pipe 65 | command /usr/bin/uuport -c \Z 66 | 67 | Sys protocol example (tested and works fine) at "/etc/uucp/sys": 68 | 69 | protocol y 70 | protocol-parameter y packet-size 512 71 | protocol-parameter y timeout 540 72 | chat-timeout 200 73 | 74 | Sys configuration example of remote system at "/etc/uucp/sys" (without login prompt): 75 | 76 | system remote 77 | call-login * 78 | call-password * 79 | time any 80 | port HFP 81 | chat "" \r 82 | 83 | Sys configuration example of remote system at "/etc/uucp/sys" (with login prompt - should call uuardopd with "-l"): 84 | 85 | system remote 86 | call-login * 87 | call-password * 88 | time any 89 | port HFP 90 | chat "" \r\c ogin: \L word: \P 91 | 92 | ### Running uuardopd 93 | 94 | Examples of uuardopd invocation: 95 | 96 | $ uuardopd -a 127.0.0.1 -c PU2BBB -p 8515 -t 60 -r ardop 97 | $ uuardopd -a 127.0.0.1 -p 8300 -r vara -o icom -s /dev/ttyUSB0 -f 2750 98 | 99 | ### UUCP with "improved-pipe.patch" for Raspberry OS (32 bits) 100 | 101 | While UUCP package for Debian 11 (Bullseye) and onwards already have the patch included and the package works fine in Debian 10 (Buster), in the case of the Raspberry Pi Zero and 1, there is a need for a UUCP armhf package compiled for armv6l (Debian's armhf port is compiled for armv7l then incompatible with the Pi Zero and 1). We made available UUCP for Raspberry OS, and to get it installed do: 102 | 103 | $ wget http://www.telemidia.puc-rio.br/~rafaeldiniz/public_files/hermes-repo/rafaeldiniz.gpg.key 104 | $ apt-key add rafaeldiniz.gpg.key 105 | $ echo deb http://www.telemidia.puc-rio.br/~rafaeldiniz/public_files/hermes-repo/ buster main >> /etc/apt/sources.list 106 | $ apt-get update 107 | $ apt-get install uucp 108 | 109 | ## Web interface (DEPRECATED) 110 | 111 | The current interface is a prototype, a new version is on its way. This 112 | one needs the following added to "/etc/sudoers": 113 | 114 | debian ALL=(ALL) NOPASSWD: ALL 115 | www-data ALL=(ALL) NOPASSWD: ALL 116 | uucp ALL=(ALL) NOPASSWD: ALL 117 | 118 | ## C compiler defines 119 | 120 | No specific C compiler define needs to be used to compile the code. 121 | 122 | ## Author 123 | 124 | Rafael Diniz 125 | 126 | ## License 127 | 128 | GPLv3 129 | -------------------------------------------------------------------------------- /scripts/compress_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # uso: 3 | # compress_image.sh image_filename.{png,gif,...} [output.{jpg,avif,heic,vvc}] 4 | 5 | ## env vars: 6 | # VVC_ENC: vvc enc binary 7 | # TARGET SIZE: target size 8 | 9 | # initial VVC QP... it will only get bigger... 10 | VVC_QP=39 11 | 12 | QUALITY=75 # initial start quality to try for jpeg 13 | 14 | VVC_ENC=${VVC_ENC:=/root/vvenc/install/bin/vvencapp} 15 | EVC_ENC=${EVC_ENC:=/root/xeve/build/bin/xeve_app} 16 | AV1_ENC=${AV1_ENC:=/root/aom/build2/aomenc} 17 | 18 | # reduce... 19 | TARGET_SIZE=${TARGET_SIZE:=80000} # 10kB == 80000 bits 20 | 21 | # logic for qp-based bitrate control 22 | MAX_SIZE=$((${TARGET_SIZE} / 8)) # 10kB file size limit 23 | 24 | #echo ${VVC_ENC} 25 | 26 | # vvc and evc are the state of the art, no integration to userlad 27 | # avif and heic are already implemented and integrated to userland 28 | # jpg is the legacy format 29 | # IMAGE_FORMAT=${IMAGE_FORMAT:=heic} 30 | 31 | if [ $# -lt 2 ]; then 32 | echo "Usage: $0 image_filename.{png,gif,...} [output.{jpg,avif,heic,vvc}]" 33 | exit 1 34 | fi 35 | 36 | input_file=${input_file:=${1}} 37 | output_file=${output_file:=${2}} 38 | 39 | IMAGE_FORMAT="${output_file##*.}" 40 | 41 | TEMPFILE=/tmp/temp-$$.${IMAGE_FORMAT} 42 | TEMPFILEYUV=/tmp/temp-$$.yuv 43 | 44 | echo "Original file size = $(stat -c%s "${input_file}")" 45 | 46 | # while [[ stat -c ${input_file} ]] 47 | # echo $(stat -c%s "${input_file}") 48 | cp -f "${input_file}" ${TEMPFILE} 49 | 50 | if [ ${IMAGE_FORMAT} = "evc" ]; then 51 | resolution=$(convert-im6 -debug all -resize "840x840>" "${input_file}" -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV} 2>&1 | grep -i "Heap " | cut -d " " -f 7 | sed -n 5p) 52 | width=$(echo -n ${resolution} | cut -f 1 -d x) 53 | height=$(echo -n ${resolution} | cut -f 2 -d x) 54 | 55 | # my ugly workaround to round up the dimensions 56 | width=$(( (${width} / 8) * 8 )) 57 | height=$(( (${height} / 8) * 8 )) 58 | resolution=${width}x${height} 59 | 60 | rm -f ${TEMPFILEYUV} 61 | 62 | convert-im6 -resize "${resolution}!" "${input_file}" -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV} 63 | 64 | 65 | echo ${EVC_ENC} -w ${width} -h ${height} -z 1 -m 2 --profile main --preset medium --bitrate $(( ${TARGET_SIZE} / 1000 )) -i ${TEMPFILEYUV} -o ${TEMPFILE} 66 | ${EVC_ENC} -w ${width} -h ${height} -z 1 -m 2 --profile main --preset medium --bitrate $(( ${TARGET_SIZE} / 1000 )) -i ${TEMPFILEYUV} -o ${TEMPFILE} 67 | 68 | rm -f ${TEMPFILEYUV} 69 | 70 | elif [ ${IMAGE_FORMAT} = "vvc" ]; then 71 | 72 | # ffmpeg -i ${input_file} -c:v rawvideo -pixel_format yuv420p -vf scale=-1:840 output_720x480p.yuv 73 | resolution=$(convert-im6 -debug all -resize "840x840>" "${input_file}" -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV} 2>&1 | grep -i "Heap " | cut -d " " -f 7 | sed -n 5p) 74 | width=$(echo -n ${resolution} | cut -f 1 -d x) 75 | height=$(echo -n ${resolution} | cut -f 2 -d x) 76 | 77 | # my ugly workaround to round up the dimensions 78 | width=$(( (${width} / 4) * 4 )) 79 | height=$(( (${height} / 4) * 4 )) 80 | resolution=${width}x${height} 81 | 82 | rm -f ${TEMPFILEYUV} 83 | 84 | convert-im6 -resize "${resolution}!" "${input_file}" -sampling-factor 4:2:0 -depth 8 -colorspace Rec709YCbCr ${TEMPFILEYUV} 85 | 86 | ## bitrate control using rc 87 | # ${VVC_ENC} -i ${TEMPFILEYUV} --profile main_10_still_picture --qpa 1 -t 2 -r 1 -b ${TARGET_SIZE} -s ${resolution} --preset medium -c yuv420 -o ${TEMPFILE} 88 | 89 | ${VVC_ENC} -i ${TEMPFILEYUV} --profile main_10_still_picture --qpa 1 -t 2 -r 1 --qp ${VVC_QP} -s ${resolution} --preset medium -c yuv420 -o ${TEMPFILE} 90 | 91 | while [ "$(stat -c%s "${TEMPFILE}")" -gt "$MAX_SIZE" ] && [ "$VVC_QP" -lt "61" ]; do 92 | VVC_QP=$((VVC_QP+3)) 93 | ${VVC_ENC} -i ${TEMPFILEYUV} --profile main_10_still_picture --qpa 1 -t 2 -r 1 --qp ${VVC_QP} -s ${resolution} --preset medium -c yuv420 -o ${TEMPFILE} 94 | 95 | done; 96 | 97 | rm -f ${TEMPFILEYUV} 98 | 99 | elif [ ${IMAGE_FORMAT} = "avif" ]; then 100 | 101 | # TODO 102 | ${AV1_ENC} --target-bitrate=${TARGET_SIZE} --end-usage=cbr --bit-depth=8 ... 103 | 104 | elif [ ${IMAGE_FORMAT} = "jpg" ]; then 105 | 106 | while [ "$(stat -c%s "${TEMPFILE}")" -gt "$MAX_SIZE" ] && [ "${QUALITY}" -gt "5" ]; do 107 | convert -resize "840x840>" "${input_file}" pnm:- | /opt/mozjpeg/bin/cjpeg -quality ${QUALITY} > ${TEMPFILE} 108 | QUALITY=$((QUALITY-10)) 109 | done; 110 | 111 | elif [ ${IMAGE_FORMAT} = "heic" ]; then 112 | 113 | # TODO 114 | while [ "$(stat -c%s "${TEMPFILE}")" -gt "$MAX_SIZE" ] && [ "$QUALITY" -gt "5" ]; do 115 | convert -resize "840x840>" "${input_file}" -quality ${QUALITY} ${TEMPFILE} 116 | QUALITY=$((QUALITY-10)) 117 | done; 118 | 119 | else 120 | echo "Unsupported extension: ${output_file##*.}" 121 | exit 122 | fi 123 | 124 | # in place 125 | #if [ $# -eq 1 ]; then 126 | # mv ${TEMPFILE} "${input_file}" 127 | #fi 128 | 129 | echo "Final file size: $(stat -c%s "${TEMPFILE}")" 130 | 131 | 132 | # with output file specified 133 | if [ $# -eq 2 ]; then 134 | mv ${TEMPFILE} "${output_file}" 135 | fi 136 | -------------------------------------------------------------------------------- /gui/styles.css: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * * 3 | * CUSTOM STYLE-SHEET STUFFS FOR THE SUBVERSION BOOK IN HTML FORM * 4 | * * 5 | **************************************************************************** 6 | * * 7 | * Copyright (c) 2003-2009 * 8 | * Ben Collins-Sussman, Brian W. Fitzpatrick, C. Michael Pilato. * 9 | * * 10 | * This work is licensed under the Creative Commons Attribution License. * 11 | * To view a copy of this license, visit * 12 | * http://creativecommons.org/licenses/by/2.0/ or send a letter to * 13 | * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, * 14 | * USA. * 15 | * * 16 | ****************************************************************************/ 17 | 18 | body { 19 | background: white; 20 | background-repeat: repeat-y !important; 21 | margin: 1in; 22 | font-family: serif; 23 | } 24 | table { 25 | border-collapse: collapse; 26 | } 27 | p, li, ul, ol, dd, dt { 28 | font-style: normal; 29 | font-weight: normal; 30 | color: black; 31 | line-height: 1.33em; 32 | } 33 | sup { 34 | vertical-align: top; 35 | font-size: 0.8em; 36 | } 37 | tt, pre { 38 | font-family: monospace; 39 | } 40 | a { 41 | color: blue; 42 | text-decoration: underline; 43 | } 44 | a:hover { 45 | background: rgb(75%,75%,100%); 46 | color: blue; 47 | text-decoration: underline; 48 | } 49 | a:visited { 50 | color: purple; 51 | text-decoration: underline; 52 | } 53 | img { 54 | border: none; 55 | } 56 | h1.title { 57 | font-size: 250%; 58 | font-style: normal; 59 | font-weight: bold; 60 | color: black; 61 | } 62 | h2.subtitle { 63 | font-size: 150%; 64 | font-style: italic; 65 | color: black; 66 | } 67 | h2.title { 68 | font-size: 150%; 69 | font-style: normal; 70 | font-weight: bold; 71 | color: black; 72 | } 73 | h3.title { 74 | font-size: 125%; 75 | font-style: normal; 76 | font-weight: bold; 77 | color: black; 78 | } 79 | h4.title { 80 | font-size: 100%; 81 | font-style: normal; 82 | font-weight: bold; 83 | color: black; 84 | } 85 | strong { 86 | font-weight: normal; 87 | } 88 | .toc b { 89 | font-family: sans-serif; 90 | font-size: 120%; 91 | font-style: normal; 92 | font-weight: bold; 93 | color: black; 94 | } 95 | .title { 96 | font-family: sans-serif; 97 | } 98 | .screen, .programlisting, .structname { 99 | font-family: monospace; 100 | font-style: normal; 101 | font-weight: normal; 102 | } 103 | .userinput { 104 | font-weight: normal; 105 | } 106 | .command { 107 | font-style: italic; 108 | } 109 | .filename { 110 | font-family: serif; 111 | font-style: italic; 112 | } 113 | .figure, .example, .informalexample, .table { 114 | margin: 0.125in 0.25in; 115 | } 116 | .figure p.title b, .example p.title b, .table p.title b { 117 | font-family: serif; 118 | font-size: 80%; 119 | font-style: italic; 120 | font-weight: normal; 121 | } 122 | .table table { 123 | border-width: 1px; 124 | border-style: solid; 125 | border-color: black; 126 | border-spacing: 0; 127 | background: rgb(240,240,240); 128 | } 129 | .table td, .table th { 130 | border: none; 131 | border-right: 1px black solid; 132 | border-bottom: 1px black solid; 133 | padding: 2px; 134 | } 135 | .table th { 136 | background: rgb(180,180,180); 137 | } 138 | .table p.title, .figure p.title, .example p.title { 139 | text-align: left !important; 140 | font-size: 100% !important; 141 | } 142 | .table-break, .figure-break, .example-break { 143 | display: none; 144 | } 145 | .author, .pubdate { 146 | margin: 0; 147 | font-size: 100%; 148 | font-style: italic; 149 | font-weight: normal; 150 | color: black; 151 | } 152 | .preface div.author, .preface .pubdate { 153 | font-size: 80%; 154 | } 155 | .sidebar { 156 | border-top: dotted 1px black; 157 | border-left: dotted 1px black; 158 | border-right: solid 1px black; 159 | border-bottom: solid 1px black; 160 | background: #ddd; 161 | padding: 0 0.12in; 162 | margin: 0.25in; 163 | } 164 | .note .programlisting, .note .screen, 165 | .tip .programlisting, .tip .screen, 166 | .warning .programlisting, .warning .screen, 167 | .sidebar .programlisting, .sidebar .screen { 168 | border: none; 169 | background: none; 170 | } 171 | .sidebar p.title { 172 | text-align: center; 173 | font-size: 125%; 174 | } 175 | .note, .tip, .warning { 176 | border-color: black; 177 | border-style: double; 178 | border-width: 3px 0; 179 | margin: 0.25in 0; 180 | font-size: 90%; 181 | } 182 | .note td, .tip td, .warning td { 183 | padding: 0.125in; 184 | } 185 | .note .title, .tip .title, .warning .title, 186 | .note th, .tip th, .warning th { 187 | display: none; 188 | } 189 | .programlisting, .screen { 190 | font-size: 90%; 191 | color: black; 192 | padding: 0.5em; 193 | } 194 | .navheader, .navfooter { 195 | border: black solid 1px; 196 | background-color: rgb(182,38,133); 197 | color: white; 198 | } 199 | .navheader a, .navfooter a { 200 | color: white; 201 | } 202 | .navheader hr, .navfooter hr { 203 | display: none; 204 | } 205 | #vcws-version-notice { 206 | margin-bottom: 1em; 207 | background-color: yellow; 208 | padding: 0.5em 1em; 209 | } 210 | #vcws-version-notice p, #vcws-footer p { 211 | margin: 0; 212 | } 213 | #vcws-version-notice hr, #vcws-footer hr { 214 | display: none; 215 | } 216 | #vcws-footer { 217 | margin-top: 1em; 218 | font-size: 80%; 219 | text-align: center; 220 | } 221 | 222 | /* --------------------- */ 223 | /* PRINT MEDIA OVERRIDES */ 224 | /* --------------------- */ 225 | 226 | @media print { 227 | body { 228 | margin: 0; 229 | } 230 | .navheader, .navfooter { 231 | display: none; 232 | } 233 | #vcws-version-notice { 234 | display: none; 235 | } 236 | #vcws-footer hr { 237 | display: block; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /gui/upload.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Serviços de Comunicação Digital 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | Resultado da Submissão do Arquivo: 15 |
16 | 17 |
18 |
19 | 20 | "; 37 | exit; 38 | } 39 | 40 | $cmd= "alias.sh ".substr ($_POST['myname'], 0, 6); 41 | $source = shell_exec($cmd); 42 | // echo $cmd."
"; 43 | 44 | 45 | if ($source == $_POST['prefix']) 46 | { 47 | echo "ERRO: Estação de origem é igual estação de destino!
"; 48 | exit; 49 | } 50 | 51 | $imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION)); 52 | 53 | // Check if image file is a actual image or fake image 54 | if(isset($_POST["submit"])) { 55 | $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]); 56 | if($check !== false) { 57 | // echo "File is an image - " . $check["mime"] . "."; 58 | $uploadPic = 1; 59 | $uploadOk = 1; 60 | } else { 61 | // echo "File is not an image. Proceding normally"; 62 | $uploadOk = 1; 63 | $uploadPic = 0; 64 | } 65 | } 66 | // Check if file already exists 67 | if (file_exists($target_file)) { 68 | // echo "Sorry, file already exists, cotinuing..."; 69 | $uploadOk = 1; 70 | } 71 | 72 | 73 | // Image but not jpg case 74 | if($imageFileType != "jpg" && $imageFileType != "JPG" && $imageFileType != "jpeg" 75 | && $imageFileType != "JPEG" && $uploadPic == 1) { 76 | if (($_FILES["fileToUpload"]["size"] > 50*1024) && $uploadPic == 1 ) { 77 | if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) 78 | { 79 | $arr = explode("." . $imageFileType, $target_file); 80 | $new_target = $arr[0] . ".jpg"; 81 | $command = "compress_image.sh \"" . $target_file . "\" \"" . $new_target . "\""; 82 | // echo "Command: " . $command . "
"; 83 | ob_start(); 84 | system($command , $return_var); 85 | ob_end_clean(); 86 | unlink($target_file); 87 | $target_file = $new_target; 88 | $uploadOk = 1; 89 | $file_in_place = 1; 90 | } else { 91 | $uploadOk = 0; 92 | echo "Erro ao mover o arquivo para pasta temporária.
"; 93 | } 94 | 95 | } 96 | } 97 | 98 | // Check file size and if it is picture, reduce the size... 99 | // limit not to reduce size is 50k! 100 | if (($_FILES["fileToUpload"]["size"] > 50*1024) && $uploadPic == 1 && $file_in_place == 0 && $uploadOk == 1) { 101 | if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) { 102 | $command = "compress_image.sh \"" . $target_file . "\""; 103 | // echo "Command: " . $command . "
"; 104 | ob_start(); 105 | system($command , $return_var); 106 | ob_end_clean(); 107 | $uploadOk = 1; 108 | $file_in_place = 1; 109 | } else { 110 | $uploadOk = 0; 111 | echo "Erro ao mover o arquivo para pasta temporária.
"; 112 | } 113 | 114 | } 115 | 116 | // Check file size of a normal file. 117 | // limit is 50k! 118 | if (($_FILES["fileToUpload"]["size"] > 50*1024) && $uploadPic == 0 ) { // 10MB max 119 | echo "Arquivo muito grande. Máximo permitido: 51200 bytes, tamanho do arquivo: " . $_FILES["fileToUpload"]["size"] . " bytes.
"; 120 | $uploadOk = 0; 121 | } 122 | 123 | // Check if $uploadOk is set to 0 by an error 124 | if ($uploadOk == 0) { 125 | // echo "Erro no pré-processamento do arquivo.
"; 126 | // if everything is ok, try to upload file 127 | } else { 128 | if ($file_in_place == 0) 129 | { 130 | if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) { 131 | $file_in_place = 1; 132 | } 133 | else { 134 | echo "Erro ao mover o arquivo para pasta temporária.
"; 135 | $uploadOk = 0; 136 | } 137 | } 138 | 139 | if (isset($_POST['encrypt']) && $file_in_place == 1) 140 | { 141 | $command = "encrypt.sh \"" . $target_file . "\" \"" . $_POST['password'] . "\""; 142 | // echo "encrypt command: " . $command . "
"; 143 | ob_start(); 144 | system($command , $return_var); 145 | $output = ob_get_contents(); 146 | ob_end_clean(); 147 | unlink($target_file); 148 | $target_file = $target_file . ".gpg"; 149 | // echo "Criptografia ativada!
"; 150 | } 151 | 152 | if ($file_in_place == 1) { 153 | if (isset($_POST['sendnow'])) 154 | { 155 | $command = "uucp -C -d \"" . $target_file . "\" " . $_POST['prefix'] . "\!\"" . $remote_dir . $source . "/\""; 156 | echo "Arquivo ".basename($target_file)." adicionado com sucesso e transmissão iniciada.
"; 157 | } else 158 | { 159 | $command = "uucp -r -C -d \"" . $target_file . "\" " . $_POST['prefix'] . "\!\"" . $remote_dir . $source . "/\""; 160 | echo "Arquivo ".basename($target_file)." adicionado com sucesso.
"; 161 | 162 | } 163 | // echo "UUCP Command: " . $command . "
"; 164 | ob_start(); 165 | system($command , $return_var); 166 | $output = ob_get_contents(); 167 | ob_end_clean(); 168 | } 169 | 170 | } 171 | unlink($target_file); 172 | ?> 173 | 174 |
175 | 176 |
177 |

Fila de Transmissão

178 | 179 | 189 |
190 | 191 |
192 | Transmitir 193 |
194 |
195 | 196 | 197 | -------------------------------------------------------------------------------- /manual/equipment_en.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt,a4paper]{article} 2 | % \usepackage[brazil]{babel} % carrega portugues brasileiro 3 | \usepackage[utf8]{inputenc} 4 | \usepackage[T1]{fontenc} 5 | \usepackage[top=2cm, bottom=2cm, left=2cm, right=2cm]{geometry} %margens menores! 6 | \usepackage{graphicx} % incluir figuras .eps 7 | \usepackage{tabularx} 8 | \usepackage{color} % colorir texto 9 | \usepackage{indentfirst} 10 | \usepackage{textcomp} 11 | \usepackage[colorlinks=true]{hyperref} 12 | \usepackage{amssymb,amsmath} 13 | \usepackage{float} 14 | % \usepackage{siunitx} 15 | % \usepackage[ampersand]{easylist} 16 | 17 | \title{HERMES Equipment List and Recommendations} 18 | 19 | \author{ 20 | \large 21 | \textsc{Rafael Diniz} 22 | \mbox{}\\ % 23 | rafael@rhizomatica.org\\ 24 | \mbox{Rhizomatica} \\ % 25 | \normalsize 26 | \texttt{Brasília - Brasil}\\ 27 | } 28 | \date{\today} 29 | 30 | 31 | \begin{document} 32 | 33 | \maketitle 34 | 35 | \begin{abstract} 36 | This document lists the equipment needed to set up a HF station capable 37 | of sending and receiving digital data. This document is based on field 38 | experience acquired through HF systems deployed in the south of Mexico 39 | and in the Amazon region in South America. The software stack for digital 40 | telecommunications system is described in another document. 41 | \end{abstract} 42 | 43 | \newpage 44 | 45 | \tableofcontents 46 | 47 | \section{Digital HF station} 48 | 49 | An HF station capable of transmitting digital data is composed by the 50 | following main set of components: 51 | 52 | \begin{itemize} 53 | \item HF Radio Transceiver 54 | \item Antenna 55 | \item Mini-Computer 56 | \item Power source 57 | \end{itemize} 58 | 59 | Each part has its own dedicated subsection, and also a section with 60 | important tools to have in order to properly adjust the HF station is 61 | present. 62 | 63 | \subsection{HF Radio Transceiver} 64 | 65 | We consider best options for HF radio transceiver the 100W (or more) 66 | transceivers which have a USB (Universal Serial Bus) port and internal 67 | analog/digital converters, which provide seamless and error-free 68 | connection between the HF transceiver and a computer. Taking in 69 | consideration the cost of the transceivers, usually the most affordable ones 70 | are made for the amateur (ham) radio market. Ham radio transceivers are 71 | usually blocked to transmit only in the ham radio allocated bands, and need 72 | to be modified to transmit in all the HF bands (See example here: 73 | \url{https://radioaficion.com/cms/ic-7100-marscap-modification/}). 74 | 75 | In order to improve transmitting data rate a passband of at least 2.8kHz is 76 | recommended in SSB mode (the mode typically used for data and voice 77 | transmissions in HF). 78 | 79 | Among this category we list as examples (supported frequency bands between parenthesis): 80 | 81 | \begin{itemize} 82 | \item ICOM IC-7100 (UHF + VHF + HF) 83 | \item ICOM IC-7300 (HF) 84 | \item Yaesu FT-991A (HF) 85 | \end{itemize} 86 | 87 | Other HF transceivers can also be used for digital telecommunications, but 88 | need an external interface to connect the radio to a computer. Examples 89 | of transceivers in the category are: 90 | 91 | \begin{itemize} 92 | \item ICOM IC-78 (HF) 93 | \item ICOM IC-718 (HF) 94 | \item Vertex VX-1700 (HF) 95 | \item Alinco DX-SR8T (HF) 96 | \item Yaesu FT-857D (UHF + VHF + HF) 97 | \item Yaesu FT-891 (HF) 98 | \end{itemize} 99 | 100 | If an external interface is needed to connect a transceiver to computer, be 101 | aware that an adjust procedure to identify the correct audio levels to 102 | correctly drive the transceiver and to optimally receive the audio needs to 103 | be done. With some transceivers like the ICOM IC-78 and IC-718 there is a 104 | need to set the MIC gain level to 0 when transmitting using its ACC 105 | connector. Different transceivers have different quirks and issues, so be 106 | aware to always carry tests before taking any equipment to the field. 107 | 108 | Examples of external interfaces which support all transceiver 109 | models (PC connection between parenthesis): 110 | \begin{itemize} 111 | \item Tigertronics Signalink USB (USB) 112 | \item DigiMaster MiniProSC (USB) 113 | \item West Mountain Radio RIGblaster (USB and Bluetooth) 114 | \end{itemize} 115 | 116 | \subsection{Antenna} 117 | 118 | The antenna is a very special part of the system. If not well tuned or well 119 | installed, nothing works. We recommend the use of the appropriate antenna 120 | for the desired coverage. For Near Vertical Incidence Skywave (NVIS), which 121 | exceeds 600km radius of coverage, we recommend a simple quarter wavelength 122 | antenna with a balun for impedance matching. If a portable antenna is 123 | needed, the Buddipole antenna is a good option, but with worse performance 124 | then a simple 1/4 wavelength dipole (See 125 | \url{https://www.buddipole.com/}). Frequency bands below 7MHz are 126 | recommended for NVIS operation. 127 | 128 | 129 | \subsection{Mini-computer} 130 | 131 | The mini-computer will host the HF modem application, the network 132 | stack and services. In the a minimal setup, the computer will run the HF 133 | modem application and a Web server which provides access to the HF 134 | telecommunication system services over a WiFi network. A Raspberry Pi 4 has 135 | enough processing power and memory for this use case. 136 | 137 | In more complex setup, in which the computer will run the HF modem and also 138 | control GSM or LTE networks connected to the HF telecommunication 139 | facilities, a more capable Intel-based mini-pc is recommended. 140 | 141 | \subsection{Power source} 142 | 143 | A common HF transceiver uses a 12V voltage input and at least 1A current in 144 | receiving mode, and peaks up to 25A when transmitting. So it is important 145 | to have a charge controller which can provide at least 30A, in order to 146 | allow some headroom for fluctuations of consumption. 147 | 148 | As a example scenario, take a transceiver using 20A when transmitting full 149 | power, 1A while listening. Lets assume we want to transmit for 5 minutes 150 | every 24 hours and listen all the time when we're not transmitting. 151 | 152 | The daily power budget of the system (@12V) is:\\ 153 | HF rx: $1A * 24h = 24Ah$ \\ 154 | HF tx: $20A * 5/60h = 100/60 = 1.7Ah$\\ 155 | mini-pc: $0.5A (~ 1.2A@5V) * 24h = 12Ah$\\ 156 | 157 | So total daily battery consumption would be $24+1.7+12 = 37.7Ah$ 158 | 159 | Let's say we want to discharge our batteries 15\% on a normal day, so 160 | this load would represent a battery capacity of 100/15 * 37.7 = 251.3Ah. 161 | 162 | We'd probably also want some spare capacity, say, an extra day of using the 163 | system when there's no sun. (solar panels are really bad at producing output 164 | on cloudy days: expect 8\%-20\% of normal output). So we'll either have to 165 | add more batteries or stop listening all day (nearly all our power 166 | consumption is for listening, not talking). for the solar panels recharging 167 | the battery, it is a good practice to size things at somewhere around 2 168 | times the normal load every day, so that the system recharges in about 1 day 169 | after 1 day of operating without sun, plus add around 15\%-20\% for 170 | inefficiencies. 171 | 172 | The solar maps for the Amazon region show a yearly average insolation 173 | of about $4.0 kWh/m^2/d$. Based on this,\\ 174 | $4h * Cpanel(W) = 37.7Ah*12.5V*2.2$\\ 175 | $Cpanel(W) = 2.2*(37.7Ah*12.5V)/(4h)$\\ 176 | $= 259.2W$ 177 | 178 | So there is a need for around 250W to 300W of solar panels. 179 | 180 | Another good practice would be to double the battery capacity for this 181 | scenario to make the system workable for more than 1 sunless day without 182 | discharging the batteries too deeply. 183 | 184 | Recommended power related parts: 185 | \begin{itemize} 186 | \item 250Ah stationary battery 187 | \item One 250W or 300W solar panel, or two 250W panels 188 | \item Charge controller which can output at least 30A of current. 40A 189 | recommended. 190 | \end{itemize} 191 | 192 | In the case of using the HF transceiver connected to power grid, a AC/DC 193 | power supply is needed, with 12V or 13.8V output. A couple of options for 194 | the AC/DC power supply: 195 | 196 | 197 | \begin{itemize} 198 | \item At least 30A transformer-based power supply at 12V or 13.8V. 199 | \item Low cost switched power supply at 12V or 13.8V marked as 50A or more. 200 | \end{itemize} 201 | 202 | \subsection{Essential tools} 203 | 204 | Essential tools to have for basic radio and antenna testing: 205 | \begin{itemize} 206 | \item Wattmeter with reflected and forward power readings for HF, which 207 | supports at least 100W 208 | \item Multimeter 209 | \item 50ohm dummy load for at least 100W 210 | \end{itemize} 211 | 212 | 213 | \end{document} 214 | -------------------------------------------------------------------------------- /serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Rhizomatica 3 | * 4 | * This is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3, or (at your option) 7 | * any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this software; see the file COPYING. If not, write to 16 | * the Free Software Foundation, Inc., 51 Franklin Street, 17 | * Boston, MA 02110-1301, USA. 18 | * 19 | * Rhizo-HF-Connector 20 | * 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "uuardopd.h" 39 | #include "serial.h" 40 | 41 | extern controller_conn *radio_conn; 42 | 43 | struct baudrate { 44 | char *name; 45 | int termios_code; 46 | int nonstd_speed; 47 | int bootrom_code; 48 | int xram_records; 49 | }; 50 | 51 | int open_serial_port(char *ttyport) 52 | { 53 | int target_fd = open(ttyport, O_RDWR|O_NONBLOCK); 54 | if (target_fd < 0) 55 | { 56 | fprintf(stderr, "open() serial port error\n"); 57 | perror(ttyport); 58 | exit(EXIT_FAILURE); 59 | } 60 | 61 | ioctl(target_fd, TIOCEXCL); 62 | return target_fd; 63 | } 64 | 65 | 66 | struct baudrate baud_rate_table[] = { 67 | /* the first listed rate will be our default */ 68 | {"115200", B115200, 0, 0, 100}, 69 | {"57600", B57600, 0, 1, 100}, 70 | {"38400", B38400, 0, 2, 100}, 71 | {"19200", B19200, 0, 4, 50}, 72 | /* Non-standard high baud rates */ 73 | {"812500", BOTHER, 812500, -1, 1000}, 74 | {"406250", BOTHER, 406250, -1, 500}, 75 | {"203125", BOTHER, 203125, -1, 250}, 76 | /* table search terminator */ 77 | {NULL, B0, 0, -1, 0}, 78 | }; 79 | 80 | struct baudrate *find_baudrate_by_name(char *srch_name) 81 | { 82 | struct baudrate *br; 83 | 84 | for (br = baud_rate_table; br->name; br++) 85 | if (!strcmp(br->name, srch_name)) 86 | break; 87 | if (br->name) 88 | return(br); 89 | else 90 | { 91 | fprintf(stderr, "error: baud rate \"%s\" not known\n", srch_name); 92 | return(NULL); 93 | } 94 | } 95 | 96 | struct baudrate *set_serial_baudrate(struct baudrate *br, int target_fd) 97 | { 98 | struct termios2 target_termios; 99 | 100 | target_termios.c_iflag = IGNBRK; 101 | target_termios.c_oflag = 0; 102 | target_termios.c_cflag = br->termios_code | CLOCAL|HUPCL|CREAD|CS8; 103 | target_termios.c_lflag = 0; 104 | target_termios.c_cc[VMIN] = 1; 105 | target_termios.c_cc[VTIME] = 0; 106 | target_termios.c_ispeed = br->nonstd_speed; 107 | target_termios.c_ospeed = br->nonstd_speed; 108 | if (ioctl(target_fd, TCSETSF2, &target_termios) < 0) { 109 | fprintf(stderr, "ioctl() TCSETSF2 error\n"); 110 | perror("TCSETSF2"); 111 | exit(1); 112 | } 113 | 114 | return br; 115 | } 116 | 117 | void set_fixed_baudrate(char *baudname, int target_fd) 118 | { 119 | struct baudrate *br; 120 | 121 | br = find_baudrate_by_name(baudname); 122 | if (!br) 123 | exit(1); /* error msg already printed */ 124 | set_serial_baudrate(br, target_fd); 125 | } 126 | 127 | void key_on(int serial_fd, int radio_type) 128 | { 129 | 130 | if (radio_type == RADIO_TYPE_ICOM) 131 | { 132 | int key_on_size = 8; 133 | uint8_t key_on[8]; 134 | key_on[0] = 0xFE; 135 | key_on[1] = 0xFE; 136 | key_on[2] = 0x88; 137 | key_on[3] = 0xE0; 138 | key_on[4] = 0x1C; 139 | key_on[5] = 0x00; 140 | key_on[6] = 0x01; 141 | key_on[7] = 0xFD; 142 | 143 | write(serial_fd, key_on, key_on_size); 144 | } 145 | 146 | if (radio_type == RADIO_TYPE_UBITX) 147 | { 148 | int key_on_size = 5; 149 | uint8_t key_on[5]; 150 | key_on[0] = 0x00; 151 | key_on[1] = 0x00; 152 | key_on[2] = 0x00; 153 | key_on[3] = 0x00; 154 | key_on[4] = 0x08; 155 | write(serial_fd, key_on, key_on_size); 156 | } 157 | 158 | if (radio_type == RADIO_TYPE_SHM) 159 | { 160 | pthread_mutex_lock(&radio_conn->cmd_mutex); 161 | radio_conn->service_command[0] = radio_conn->service_command[1] = radio_conn->service_command[2] = radio_conn->service_command[3] = 0x00; 162 | radio_conn->service_command[4] = 0x08; 163 | pthread_cond_signal(&radio_conn->cmd_condition); 164 | pthread_mutex_unlock(&radio_conn->cmd_mutex); 165 | } 166 | 167 | } 168 | 169 | void key_off(int serial_fd, int radio_type) 170 | { 171 | if (radio_type == RADIO_TYPE_ICOM) 172 | { 173 | int key_off_size = 8; 174 | uint8_t key_off[8]; 175 | key_off[0] = 0xFE; 176 | key_off[1] = 0xFE; 177 | key_off[2] = 0x88; 178 | key_off[3] = 0xE0; 179 | key_off[4] = 0x1C; 180 | key_off[5] = 0x00; 181 | key_off[6] = 0x00; 182 | key_off[7] = 0xFD; 183 | 184 | write(serial_fd, key_off, key_off_size); 185 | } 186 | 187 | if (radio_type == RADIO_TYPE_UBITX) 188 | { 189 | int key_off_size = 5; 190 | uint8_t key_off[5]; 191 | key_off[0] = 0x00; 192 | key_off[1] = 0x00; 193 | key_off[2] = 0x00; 194 | key_off[3] = 0x00; 195 | key_off[4] = 0x88; 196 | write(serial_fd, key_off, key_off_size); 197 | } 198 | 199 | if (radio_type == RADIO_TYPE_SHM) 200 | { 201 | pthread_mutex_lock(&radio_conn->cmd_mutex); 202 | radio_conn->service_command[0] = radio_conn->service_command[1] = radio_conn->service_command[2] = radio_conn->service_command[3] = 0x00; 203 | radio_conn->service_command[4] = 0x88; 204 | pthread_cond_signal(&radio_conn->cmd_condition); 205 | pthread_mutex_unlock(&radio_conn->cmd_mutex); 206 | } 207 | 208 | } 209 | 210 | void connected_led_on(int serial_fd, int radio_type) 211 | { 212 | 213 | if (radio_type == RADIO_TYPE_SHM) 214 | { 215 | pthread_mutex_lock(&radio_conn->cmd_mutex); 216 | radio_conn->service_command[0] = 0x01; // led on 217 | radio_conn->service_command[1] = radio_conn->service_command[2] = radio_conn->service_command[3] = 0x00; 218 | radio_conn->service_command[4] = 0xa0; // CMD_SET_CONNECTED_STATUS 219 | pthread_cond_signal(&radio_conn->cmd_condition); 220 | pthread_mutex_unlock(&radio_conn->cmd_mutex); 221 | // read response... no 222 | } 223 | 224 | } 225 | 226 | 227 | void connected_led_off(int serial_fd, int radio_type) 228 | { 229 | 230 | if (radio_type == RADIO_TYPE_SHM) 231 | { 232 | pthread_mutex_lock(&radio_conn->cmd_mutex); 233 | radio_conn->service_command[0] = 0x00; // led off 234 | radio_conn->service_command[1] = radio_conn->service_command[2] = radio_conn->service_command[3] = 0x00; 235 | radio_conn->service_command[4] = 0xa0; // CMD_SET_CONNECTED_STATUS 236 | pthread_cond_signal(&radio_conn->cmd_condition); 237 | pthread_mutex_unlock(&radio_conn->cmd_mutex); 238 | // read response... no 239 | } 240 | } 241 | 242 | void sys_led_on(int serial_fd, int radio_type) 243 | { 244 | 245 | if (radio_type == RADIO_TYPE_SHM) 246 | { 247 | pthread_mutex_lock(&radio_conn->cmd_mutex); 248 | radio_conn->service_command[0] = 0x01; // led on 249 | radio_conn->service_command[1] = radio_conn->service_command[2] = radio_conn->service_command[3] = 0x00; 250 | radio_conn->service_command[4] = 0x9e; // CMD_SET_LED_STATUS 251 | pthread_cond_signal(&radio_conn->cmd_condition); 252 | pthread_mutex_unlock(&radio_conn->cmd_mutex); 253 | // read response... no 254 | } 255 | 256 | } 257 | 258 | 259 | void sys_led_off(int serial_fd, int radio_type) 260 | { 261 | 262 | if (radio_type == RADIO_TYPE_SHM) 263 | { 264 | pthread_mutex_lock(&radio_conn->cmd_mutex); 265 | radio_conn->service_command[0] = 0x00; // led off 266 | radio_conn->service_command[1] = radio_conn->service_command[2] = radio_conn->service_command[3] = 0x00; 267 | radio_conn->service_command[4] = 0x9e; // CMD_SET_LED_STATUS 268 | pthread_cond_signal(&radio_conn->cmd_condition); 269 | pthread_mutex_unlock(&radio_conn->cmd_mutex); 270 | // read response... no 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /uuport.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop: Tools to integrate Ardop to UUCP 2 | * Copyright (C) 2019 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | */ 21 | 22 | /** 23 | * @file uuport.c 24 | * @author Rafael Diniz 25 | * @date 14 Aug 2019 26 | * @brief UUCP port 27 | * 28 | * UUPORT main C file. 29 | * 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "uuardopd.h" 48 | #include "uuport.h" 49 | #include "shm.h" 50 | #include "circular_buffer.h" 51 | 52 | FILE *log_fd; 53 | atomic_bool running_read; 54 | atomic_bool running_write; 55 | 56 | #define TIMEOUT 15 57 | 58 | void *read_thread(void *conn) 59 | { 60 | rhizo_conn *connector = (rhizo_conn *) conn; 61 | uint8_t buffer[BUFFER_SIZE]; 62 | int bytes_to_read = 0; 63 | int bytes_written = 0; 64 | int timeout_counter = TIMEOUT; 65 | 66 | running_read = true; 67 | while (running_read && (connector->shutdown == false)) 68 | { 69 | if (connector->connected == false) 70 | { 71 | sleep(1); 72 | timeout_counter--; 73 | if (timeout_counter == 0) 74 | { 75 | running_read = false; 76 | } 77 | } 78 | else{ 79 | timeout_counter = TIMEOUT; 80 | } 81 | 82 | bytes_to_read = circular_buf_size(connector->out_buffer_p); 83 | 84 | if (bytes_to_read == 0) 85 | { // we spinlock here 86 | usleep(100000); // 0.1s 87 | continue; 88 | } 89 | 90 | if (bytes_to_read > BUFFER_SIZE) 91 | bytes_to_read = BUFFER_SIZE; 92 | 93 | circular_buf_get_range(connector->out_buffer_p, buffer, bytes_to_read); 94 | 95 | bytes_written = write(1, buffer, bytes_to_read); 96 | 97 | // fprintf(log_fd, "uuport: %d bytes written to uucico\n", bytes_written); 98 | 99 | if (bytes_written != bytes_to_read) 100 | { 101 | fprintf(log_fd, "read_thread: bytes_written: %d != bytes_read: %d.\n", bytes_written, bytes_to_read); 102 | running_read = false; 103 | continue; 104 | } 105 | if (bytes_written == -1) 106 | { 107 | fprintf(log_fd, "read_thread: write() error no: %d\n", errno); 108 | running_read = false; 109 | continue; 110 | } 111 | 112 | } 113 | // connector->session_counter_read++; 114 | 115 | return NULL; 116 | 117 | } 118 | 119 | void *write_thread(void *conn) 120 | { 121 | rhizo_conn *connector = (rhizo_conn *) conn; 122 | uint8_t buffer[BUFFER_SIZE]; 123 | int bytes_to_read = 0; 124 | int bytes_read = 0; 125 | 126 | running_write = true; 127 | while(running_write && (connector->shutdown == false)) 128 | { 129 | if (connector->clean_buffers == true) 130 | { 131 | running_write = false; 132 | continue; 133 | } 134 | // workaround to make protocol 'y' work better 135 | if (circular_buf_size(connector->in_buffer_p) > BUFFER_SIZE / 2) 136 | { 137 | usleep(100000); // 0.1s 138 | bytes_to_read = 1; // slow down... 139 | } 140 | else 141 | { 142 | bytes_to_read = 512; // protocol 'y' packet size 143 | } 144 | 145 | bytes_read = read(0, buffer, bytes_to_read); 146 | 147 | // fprintf(log_fd, "uuport: %d bytes read from uucico\n", bytes_read); 148 | 149 | if (bytes_read == -1) 150 | { 151 | fprintf(log_fd, "write_thread: Error in read(), errno: %d\n", errno); 152 | running_write = false; 153 | continue; 154 | } 155 | if (bytes_read == 0) 156 | { 157 | fprintf(log_fd, "write_thread: read() returned 0\n"); 158 | running_write = false; 159 | continue; 160 | } 161 | 162 | while (circular_buf_free_size(connector->in_buffer_p) < bytes_read) 163 | { 164 | fprintf(log_fd, "Buffer full!\n"); 165 | usleep(100000); 166 | } 167 | circular_buf_put_range(connector->in_buffer_p, buffer, bytes_read); 168 | } 169 | 170 | running_read = false; 171 | // connector->session_counter_write++; 172 | 173 | return NULL; 174 | } 175 | 176 | void finish(int s){ 177 | 178 | if (s == SIGINT) 179 | fprintf(log_fd, "\nSIGINT: Exiting.\n"); 180 | 181 | if (s == SIGTERM) 182 | fprintf(log_fd, "\nSIGTERM: Exiting.\n"); 183 | 184 | if (s == SIGQUIT) 185 | fprintf(log_fd, "\nSIGQUIT: Exiting.\n"); 186 | 187 | if (s == SIGHUP) 188 | { 189 | fprintf(log_fd, "\nSIGHUP: running shutdown...\n"); 190 | running_write = false; 191 | running_read = false; 192 | fflush(log_fd); 193 | sleep(1); 194 | exit(EXIT_SUCCESS); // this is not perfect... but it is what we can do now. 195 | return; 196 | } 197 | if (s == SIGPIPE){ 198 | fprintf(log_fd, "\nSIGPIPE: Doing nothing.\n"); 199 | return; 200 | } 201 | 202 | // some house keeping here? 203 | 204 | fclose(log_fd); 205 | exit(EXIT_SUCCESS); 206 | } 207 | 208 | 209 | int main (int argc, char *argv[]) 210 | { 211 | rhizo_conn *connector = NULL; 212 | 213 | char log_file[BUFFER_SIZE]; 214 | log_file[0] = 0; 215 | 216 | char remote_system[32]; 217 | remote_system[0] = 0; 218 | 219 | signal (SIGINT, finish); 220 | signal (SIGTERM, finish); 221 | signal (SIGQUIT, finish); 222 | signal (SIGHUP, finish); 223 | signal (SIGPIPE, finish); 224 | 225 | if (argc < 1) 226 | { 227 | manual: 228 | fprintf(stderr, "Usage modes: \n%s -l logfile\n", argv[0]); 229 | fprintf(stderr, "%s -h\n", argv[0]); 230 | fprintf(stderr, "\nOptions:\n"); 231 | fprintf(stderr, " -e logfile.txt Log file (default is stderr).\n"); 232 | fprintf(stderr, " -c system_name Name of the remote system (default is don't change).\n"); 233 | fprintf(stderr, " -h Prints this help.\n"); 234 | exit(EXIT_FAILURE); 235 | } 236 | 237 | int opt; 238 | while ((opt = getopt(argc, argv, "hc:e:")) != -1) 239 | { 240 | switch (opt) 241 | { 242 | case 'h': 243 | goto manual; 244 | break; 245 | case 'e': 246 | strcpy(log_file, optarg); 247 | break; 248 | case 'c': 249 | strcpy(remote_system, optarg); 250 | break; 251 | default: 252 | goto manual; 253 | } 254 | } 255 | 256 | if (shm_is_created(SYSV_SHM_KEY_STR, sizeof(rhizo_conn)) == false) 257 | { 258 | fprintf(stderr, "Connector SHM not created. Is uuardopd running?\n"); 259 | return EXIT_FAILURE; 260 | } 261 | connector = shm_attach(SYSV_SHM_KEY_STR, sizeof(rhizo_conn)); 262 | 263 | if (connector->shutdown == true) 264 | { 265 | fprintf(stderr, "uuardopd is in shutdown state. Exiting.\n"); 266 | return EXIT_FAILURE; 267 | } 268 | 269 | connector->in_buffer_p = circular_buf_connect_shm(INTERNAL_BUFFER_SIZE, SYSV_SHM_KEY_IB); 270 | connector->out_buffer_p = circular_buf_connect_shm(INTERNAL_BUFFER_SIZE, SYSV_SHM_KEY_OB); 271 | 272 | if (log_file[0]) 273 | { 274 | log_fd = fopen(log_file, "a"); 275 | if (log_fd == NULL) 276 | { 277 | fprintf(stderr, "Log file could not be opened: %s\n", log_file); 278 | fprintf(stderr, "Reverting to stderr log.\n"); 279 | log_fd = stderr; 280 | } 281 | } 282 | else 283 | { 284 | log_fd = stderr; 285 | } 286 | 287 | if (remote_system[0]) 288 | { 289 | strcpy(connector->remote_call_sign, remote_system); 290 | } 291 | 292 | pthread_t tid; 293 | pthread_create(&tid, NULL, write_thread, (void *) connector); 294 | 295 | read_thread(connector); 296 | 297 | // workaround... as write_thread blocks in fd 0... 298 | fclose(log_fd); 299 | return EXIT_SUCCESS; 300 | 301 | // correct should be this... 302 | pthread_join(tid, NULL); 303 | return EXIT_SUCCESS; 304 | } 305 | -------------------------------------------------------------------------------- /call_uucico.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop: Tools to integrate Ardop to UUCP 2 | * Copyright (C) 2019 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Routines to call uucico when receiving a call 21 | */ 22 | 23 | /** 24 | * @file call_uucico.c 25 | * @author Rafael Diniz 26 | * @date 26 Jul 2019 27 | * @brief Routines to call uucico when receiving a call 28 | * 29 | * Code to call uucico 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #include "call_uucico.h" 53 | 54 | bool call_uucico(rhizo_conn *connector){ 55 | 56 | fprintf(stderr, "call_uucico: Sending signal to start uucico!\n"); 57 | 58 | if (connector->uucico_active == true) 59 | fprintf(stderr, "Warning: trying to activate already active uucico!\n"); 60 | else 61 | connector->uucico_active = true; 62 | 63 | return true; 64 | } 65 | 66 | void *uucico_thread(void *conn){ 67 | rhizo_conn *connector = conn; 68 | pid_t pid; 69 | int st; 70 | 71 | // set some file descriptors as in in.uucpd... 72 | // use 2 pipe() to create the fds for I/O 73 | // fork() 74 | // in the parent, call wait to wait for uucico (in a new thread?) 75 | // in the child, remap the fds to 0, 1 (and 2?) 76 | // in the child, call execl() (or execlp() (uucico -l) 77 | 78 | while (connector->shutdown == false) 79 | { 80 | while (connector->uucico_active == false) 81 | usleep(100000); // 0.1s 82 | 83 | fprintf(stderr, "uucico_thread: session started!\n"); 84 | 85 | // parent write to child 86 | pipe(connector->pipefd1); // pipe[0] is read, pipe[1] is write 87 | // child write to parent 88 | pipe(connector->pipefd2); 89 | 90 | pthread_t tid1; 91 | pthread_create(&tid1, NULL, uucico_read_thread, (void *) connector); 92 | 93 | pthread_t tid2; 94 | pthread_create(&tid2, NULL, uucico_write_thread, (void *) connector); 95 | 96 | // parent 97 | if ((pid = fork()) != 0) 98 | { 99 | if (pid < 0) { 100 | fprintf(stderr, "fork() error.\n"); 101 | return NULL; 102 | } 103 | 104 | close(connector->pipefd1[0]); 105 | close(connector->pipefd2[1]); 106 | 107 | // pthread_create the two threads which does the job of reading / writing from/to buffers and fds... 108 | 109 | while(wait(&st) != pid); 110 | if ( WIFEXITED(st) ){ 111 | fprintf(stderr, "uucico child exec exited with status = %d\n", WEXITSTATUS(st)); 112 | // uucico ended! 113 | // we should disconnect here! 114 | } 115 | 116 | close(connector->pipefd1[1]); 117 | close(connector->pipefd2[0]); 118 | 119 | // fprintf(stderr, "uucico before join\n"); 120 | 121 | pthread_join(tid1, NULL); 122 | pthread_join(tid2, NULL); 123 | 124 | // fprintf(stderr, "uucico after join\n"); 125 | 126 | // usleep(2000000); // 2s for the system to cool down 127 | connector->clean_buffers = true; 128 | 129 | connector->send_break = true; 130 | connector->uucico_active = false; 131 | 132 | fprintf(stderr, "uucico_thread: session ended!\n"); 133 | continue; 134 | } 135 | 136 | // this is the child (uucico) 137 | close(0); 138 | close(1); 139 | close(2); 140 | 141 | dup2(connector->pipefd1[0], 0); 142 | dup2(connector->pipefd2[1], 1); 143 | dup2(connector->pipefd2[1], 2); // is this correct? 144 | 145 | close(connector->pipefd1[0]); 146 | close(connector->pipefd2[1]); 147 | close(connector->pipefd1[1]); // closing write pipefd1 (child reads from parent) 148 | close(connector->pipefd2[0]); // closing read pipefd2 (child writes to parent) 149 | 150 | #if 0 // lets run all as root 151 | char pwd[] = "/var/spool/uucp"; // uucp home 152 | if (chdir(pwd) != 0) { 153 | perror(pwd); 154 | exit(1); 155 | } 156 | gid_t gid = 10; // uucp gid 157 | if (setgid(gid) != 0) { 158 | perror("setgid"); 159 | exit(1); 160 | } 161 | char user[] = "uucp"; 162 | if (initgroups(user, gid) < 0) { 163 | perror("initgroups"); 164 | exit(1); 165 | } 166 | uid_t uid = 10; // uucp uid 167 | if (setuid(uid) != 0) { 168 | perror("setuid"); 169 | exit(1); 170 | } 171 | #endif 172 | char shell[] = "/usr/sbin/uucico"; 173 | 174 | // setenv("LOGNAME", user, 1); 175 | // setenv("USER", user, 1); 176 | setenv("SHELL", shell, 1); 177 | setenv("TERM", "dumb", 1); 178 | 179 | if (connector->ask_login == true) 180 | execl(shell, shell, "-l", NULL); 181 | else 182 | execl(shell, shell, NULL); 183 | 184 | perror(shell); 185 | 186 | _exit(EXIT_SUCCESS); 187 | } 188 | 189 | return NULL; 190 | } 191 | 192 | void *uucico_read_thread(void *conn) 193 | { 194 | bool running = true; 195 | rhizo_conn *connector = (rhizo_conn *) conn; 196 | int num_read = 0; 197 | int bytes_pipe = 0; 198 | uint8_t buffer[BUFFER_SIZE]; 199 | 200 | while(running) 201 | { 202 | ioctl(connector->pipefd2[0], FIONREAD, &bytes_pipe); 203 | // fprintf(stderr, "trying to read from uucico %d bytes!\n", bytes_pipe); 204 | 205 | if (bytes_pipe > BUFFER_SIZE) 206 | bytes_pipe = BUFFER_SIZE; 207 | if (bytes_pipe <= 0) 208 | bytes_pipe = 1; // so we block in read() in case of no data to read 209 | 210 | // workaround to make protocol 'y' work better 211 | while (circular_buf_size(connector->in_buffer) > BUFFER_SIZE/2) 212 | { 213 | bytes_pipe = 1; // slow down... 214 | usleep(100000); // 0.1s 215 | } 216 | 217 | num_read = read(connector->pipefd2[0], buffer, bytes_pipe); 218 | 219 | if (num_read > 0) 220 | { 221 | while (circular_buf_free_size(connector->in_buffer) < num_read) 222 | usleep(20000); 223 | circular_buf_put_range(connector->in_buffer, buffer, num_read); 224 | } 225 | if (num_read == 0) 226 | { 227 | fprintf(stderr, "uucico_read_thread: read == 0\n"); 228 | running = false; 229 | } 230 | if (num_read == -1) 231 | { 232 | fprintf(stderr, "uucico_read_thread: read() error! error no: %d\n",errno); 233 | running = false; 234 | } 235 | } 236 | 237 | connector->session_counter_read++; 238 | 239 | return NULL; 240 | } 241 | 242 | 243 | void *uucico_write_thread(void *conn) { 244 | bool running = true; 245 | rhizo_conn *connector = (rhizo_conn *) conn; 246 | int bytes_to_read = 0; 247 | int num_written = 0; 248 | uint8_t buffer[BUFFER_SIZE]; 249 | 250 | while (running) 251 | { 252 | bytes_to_read = circular_buf_size(connector->out_buffer); 253 | if (bytes_to_read == 0) 254 | { // we spinlock here 255 | usleep(100000); // 0.1s 256 | if (connector->session_counter_read > connector->session_counter_write) 257 | running = false; 258 | continue; 259 | } 260 | 261 | if (bytes_to_read > BUFFER_SIZE) 262 | bytes_to_read = BUFFER_SIZE; 263 | 264 | circular_buf_get_range(connector->out_buffer, buffer, bytes_to_read); 265 | 266 | num_written = write(connector->pipefd1[1], buffer, bytes_to_read); 267 | if (num_written == 0) 268 | { 269 | fprintf(stderr, "pipe_write_thread: write == 0\n"); 270 | running = false; 271 | } 272 | if (num_written == -1) 273 | { 274 | running = false; 275 | if (errno == EPIPE) 276 | { 277 | fprintf(stderr, "uucico_write_thread: write() EPIPE!\n"); 278 | } 279 | else 280 | { 281 | fprintf(stderr, "uucico_write_thread: write() error no: %d\n", errno); 282 | } 283 | } 284 | } 285 | 286 | connector->session_counter_write++; 287 | 288 | return NULL; 289 | } 290 | -------------------------------------------------------------------------------- /manual/hermes-software-en.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt,a4paper]{article} 2 | % \usepackage[brazil]{babel} % carrega portugues brasileiro 3 | \usepackage[utf8]{inputenc} 4 | \usepackage[T1]{fontenc} 5 | \usepackage[top=2cm, bottom=2cm, left=2cm, right=2cm]{geometry} %margens menores! 6 | \usepackage{graphicx} % incluir figuras .eps 7 | \usepackage{tabularx} 8 | \usepackage{color} % colorir texto 9 | \usepackage{indentfirst} 10 | \usepackage{textcomp} 11 | \usepackage[colorlinks=true]{hyperref} 12 | \usepackage{amssymb,amsmath} 13 | \usepackage{float} 14 | % \usepackage{siunitx} 15 | % \usepackage[ampersand]{easylist} 16 | 17 | \title{HERMES - High-frequency Emergency and Rural Multimedia Exchange 18 | System Software Description} 19 | 20 | \author{ 21 | \large 22 | \textsc{Rafael Diniz} 23 | \mbox{}\\ % 24 | rafael@rhizomatica.org\\ 25 | \mbox{Rhizomatica} \\ % 26 | \normalsize 27 | \texttt{Brasília - Brasil}\\ 28 | } 29 | \date{\today} 30 | 31 | 32 | \begin{document} 33 | 34 | \maketitle 35 | 36 | \begin{abstract} 37 | 38 | This document describes the software stack of the HERMES 39 | system - a digital communication system for the HF band which uses the ARDOP 40 | (Amateur Radio Digital Open Protocol) modem and UUCP networking (Unix to Unix 41 | Communication Protocol). Apart from ARDOP and UUCP, HERMES is composed of a 42 | Web Interface for access to the services it provides, which can be run 43 | on almost any mini-computer. 44 | 45 | \end{abstract} 46 | 47 | \newpage 48 | 49 | \tableofcontents 50 | 51 | \section{Reference system} 52 | 53 | \begin{itemize} 54 | \item HF transceiver connected to a computer with audio I/O and PTT control 55 | (see Equipment list document) 56 | \item Mini computer running Linux (and the software stack described by this 57 | document) connected to the HF transceiver and other networks (eg. WiFi, 58 | GSM, LTE) 59 | \end{itemize} 60 | 61 | \section{What HERMES can do} 62 | 63 | HERMES is as a system to be used with HF transceivers for digital 64 | communication. HERMES is meant to work with HF NVIS (near vertical incidence 65 | skywave) and provide hundreds of kilometers of reach for each station. 66 | 67 | The modem we currently use (ARDOP) supports both point-to-point mode (ARQ 68 | mode - Automatic repeat request) and broadcast mode (FEC mode - Forward 69 | Error Correction). We only use ARQ mode, which guarantees error-free 70 | reception of the data. Using broadcast mode would require a protocol 71 | different than UUCP. 72 | 73 | We adopted UUCP as the networking solution to be carried over the ARDOP modem in 74 | ARQ mode. UUCP can carry files and execute remote commands. Specific support 75 | for email exists and can be used out-of-the-box to provide email service over 76 | HF (UUCP was created in the late 1970s and one of the main uses was email). 77 | 78 | In the more basic configuration, HERMES is used for file exchange, with the 79 | option for secure (through cryptography) exchange of files. 80 | 81 | \section{HERMES software configuration} 82 | 83 | HERMES uses ARDOP (Amateur Radio Digital Open Protocol) for a modem. ARDOP is 84 | an SDR (software defined radio) modem made by amateur radio operators which 85 | uses modern modulation techniques (OFDM) and supports standard and 86 | homebrew HF transceivers. 87 | 88 | For the network layer, UUCP is used. UUCP is a system for asynchronous 89 | store-and-forward communication first released in Bell Labs Unix V7 in 90 | the late 1970s, and still used today in niches, like communication over HF. 91 | 92 | The RHIZO-UUArdopD project was developed to provide tools that integrate 93 | the UUCP system with the ARDOP modem. 94 | 95 | A mini computer running Linux is needed. We recommend Debian Buster (10) 96 | arm64 (multilib with armhf if you want to use the pre-compiled ``piardopc '' 97 | by John Wiseman) as the Linux reference system, running on a Raspberry Pi with 98 | Wifi configured in AP (Access Point) mode, for serving the system web 99 | interface. 100 | 101 | The HERMES network stack is made of: 102 | \begin{itemize} 103 | \item Computer to Radio connection 104 | \item ALSA (Audio configuration) 105 | \item ARDOP (Modem configuration) 106 | \item UUCP (Network configuration) 107 | \item Rhizo-UUArdopD (UUCP / ARDOP connection tools) 108 | \item User Web Interface 109 | \end{itemize} 110 | 111 | \subsection{Computer to Radio connection} 112 | 113 | Different setups require different configuration. In the case of using a USB 114 | (Universal Serial Bus) interface (e.g. Signalink), the delay must be set to 115 | 0. In the case of radios with USB connection exposed an embedded sound 116 | card and transmit/receive control (eg. ICOM IC-7100) set the bandpass 117 | filter to at least 2.8kHz (or wider) for digital operation (SSB/Data). 118 | 119 | \subsection{ALSA (Audio configuration)} 120 | 121 | Add to ``/etc/asound.conf'': 122 | \begin{verbatim} 123 | pcm.ARDOP {type rate slave {pcm "hw:1,0" rate 48000}} 124 | \end{verbatim} 125 | 126 | Where ``hw:1,0'' is the HF transceiver's audio device. 127 | 128 | \subsection{ARDOP (Modem configuration)} 129 | 130 | Download link: \url{https://github.com/DigitalHERMES/ardopc} 131 | 132 | The ardop binary should be in /usr/bin/ardop, which can be a 133 | symbolic link to /usr/bin/{ardop1ofdm, ardop2, ardopofdm}. 134 | 135 | ARDOP service file for the ICOM IC-7100 (USB connection, PTT done over serial): 136 | \begin{verbatim} 137 | [Unit] 138 | Description=ARDOP daemon 139 | 140 | [Service] 141 | Type=simple 142 | ExecStart=/usr/bin/ardop 8515 -c /dev/ttyUSB0 ARDOP ARDOP -k FEFE88E01C0001FD -u FEFE88E01C0000FD 143 | ExecStop=/usr/bin/killall -s QUIT ardop 144 | IgnoreSIGPIPE=no 145 | #StandardOutput=null 146 | #StandardError=null 147 | StandardOutput=syslog 148 | StandardError=syslog 149 | 150 | [Install] 151 | WantedBy=multi-user.target 152 | \end{verbatim} 153 | 154 | Service file when using a VOX based setup (eg. when using an interface like 155 | the Signalink): 156 | \begin{verbatim} 157 | [Unit] 158 | Description=ARDOP daemon 159 | 160 | [Service] 161 | Type=simple 162 | ExecStart=/usr/bin/ardop 8515 ARDOP ARDOP 163 | ExecStop=/usr/bin/killall -s QUIT ardop 164 | IgnoreSIGPIPE=no 165 | #StandardOutput=null 166 | #StandardError=null 167 | StandardOutput=syslog 168 | StandardError=syslog 169 | 170 | [Install] 171 | WantedBy=multi-user.target 172 | \end{verbatim} 173 | 174 | 175 | Start/stop ARDOP service: 176 | \begin{verbatim} 177 | systemctl start ardop.service 178 | systemctl stop ardop.service 179 | \end{verbatim} 180 | 181 | 182 | See the log: 183 | \begin{verbatim} 184 | journalctl -f -u ardop 185 | \end{verbatim} 186 | 187 | \subsection{UUCP (Network configuration)} 188 | 189 | UUCP Debian package version 1.07-27 or higher should be used, for example, 190 | the version from Debian Bullseye (11): 191 | \url{https://packages.debian.org/bullseye/uucp}. Packages for Raspberry OS 192 | Buster can be installed using our repository. Example for installation in a 193 | Raspberry Zero or 1 (32bit armv6l Raspberry devices) root terminal: 194 | 195 | \begin{verbatim} 196 | echo deb http://www.telemidia.puc-rio.br/~rafaeldiniz/public_files/hermes-repo/ buster main \ 197 | >> /etc/apt/sources.list 198 | wget http://www.telemidia.puc-rio.br/~rafaeldiniz/public_files/hermes-repo/rafaeldiniz.gpg.key 199 | apt-key add rafaeldiniz.gpg.key 200 | apt-get update 201 | apt-get install uucp 202 | \end{verbatim} 203 | 204 | 205 | UUCP command line examples follow. To copy a file to a remote host, 206 | the following command adds a copy job to the uucp queue (``-r'' is used to 207 | not start transmission after queuing): 208 | \begin{verbatim} 209 | uucp -C -r -d source.xxx AM4AAB\!/var/www/html/arquivos/${nodename}/ 210 | \end{verbatim} 211 | 212 | Trigger the transmission of all queued jobs for host 213 | AM4AAA: 214 | \begin{verbatim} 215 | uucico -S AM4AAA 216 | \end{verbatim} 217 | 218 | List the jobs: 219 | \begin{verbatim} 220 | uustat -a 221 | \end{verbatim} 222 | 223 | Kill a job: 224 | \begin{verbatim} 225 | uustat -k job 226 | uustat -K 227 | \end{verbatim} 228 | 229 | See the log: 230 | \begin{verbatim} 231 | uulog 232 | \end{verbatim} 233 | 234 | 235 | \subsection{Rhizo-uuardopd (UUCP / ARDOP connection tools)} 236 | 237 | %TODO: It seems everything uucico wrote in the end of a reception, don't get 238 | %to the other side - connection closes and buffer get clean fast! 239 | 240 | Two binaries should be installed: uuport (to be called by uucp) and uuardopd 241 | which is the daemon software that connects to the ARDOP modem to start 242 | or stop a connection. Rhizo-uuardopd manages the connection with UUCP 243 | through uucico or uuport. 244 | 245 | Download link: \url{http://github.com/DigitalHERMES/rhizo-uuardop} 246 | 247 | Start/stop UUARDOPD service: 248 | \begin{verbatim} 249 | systemctl start uuardopd.service 250 | systemctl stop uuardopd.service 251 | \end{verbatim} 252 | 253 | 254 | See the log: 255 | \begin{verbatim} 256 | journalctl -f -u uuardopd 257 | \end{verbatim} 258 | 259 | %\subsection{DHCP} 260 | 261 | %\subsection{DNS} 262 | 263 | %\subsection{Apache + PHP} 264 | 265 | 266 | \subsection{User Web Interface} 267 | 268 | HERMES provides a web-based interface for file exchange, using HTML + PHP and 269 | some shell scripts located in the ``gui'' directory of the Rhizo-Uuardopd source 270 | code 271 | (\url{https://github.com/DigitalHERMES/rhizo-uuardop/tree/master/gui}). Current 272 | implementation supports symmetric cryptography and image compression. 273 | 274 | We use ``/var/www/html/arquivos'' as the default UUCP path to send 275 | files through the web interface. 276 | 277 | Requirements of the user interface: 278 | 279 | \begin{itemize} 280 | \item ImageMagic: for image manipulation 281 | \item mozjpeg: Best public JPEG encoder: \url{https://github.com/mozilla/mozjpeg} 282 | % \item opusenc: para comprimir áudio 283 | \item GnuPG: For cryptography 284 | \item hostapd: WiFi AP mode software 285 | \end{itemize} 286 | 287 | The Web interface can be accessed by typing any address in a browser 288 | connected to the WiFi (set the DNS accordingly) or simply 192.168.1.1. 289 | 290 | %\subsection{Email} 291 | 292 | %ps: WORK IN PROGRESS 293 | 294 | %Email server (MTA) can either run locally or only in a central host with 295 | %Internet. Stations can either opt to connect to a central station on demand, 296 | %have some pre-defined schedule, or the central station connects to the 297 | %community stations doing a pooling, delivering and downloading emails and 298 | %files. 299 | 300 | %\subsection{WebPhone} 301 | 302 | %WORK IN PROGRESS 303 | 304 | %\url{https://gitlab.tic-ac.org/keith/webphone/wikis/hermes} 305 | 306 | \end{document} 307 | -------------------------------------------------------------------------------- /manual/report.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt,a4paper]{article} 2 | % \usepackage[brazil]{babel} % carrega portugues brasileiro 3 | \usepackage[utf8]{inputenc} 4 | \usepackage[T1]{fontenc} 5 | \usepackage[top=2cm, bottom=2cm, left=2cm, right=2cm]{geometry} %margens menores! 6 | \usepackage{graphicx} % incluir figuras .eps 7 | \usepackage{tabularx} 8 | \usepackage{color} % colorir texto 9 | \usepackage{indentfirst} 10 | \usepackage{textcomp} 11 | \usepackage[colorlinks=true]{hyperref} 12 | \usepackage{amssymb,amsmath} 13 | \usepackage{float} 14 | % \usepackage{siunitx} 15 | % \usepackage[ampersand]{easylist} 16 | 17 | \title{Manual de uso do sistema de telecomunicação digital para banda de HF} 18 | 19 | \author{ 20 | \large 21 | \textsc{Rafael Diniz} 22 | \mbox{}\\ % 23 | rafael@rhizomatica.org\\ 24 | \mbox{Rhizomatica} \\ % 25 | \normalsize 26 | \texttt{Brasília - Brasil}\\ 27 | } 28 | \date{\today} 29 | 30 | 31 | \begin{document} 32 | 33 | \maketitle 34 | 35 | \begin{abstract} 36 | Este é o manual do sistema de comunicação digital em HF usando o modem ARDOP 37 | (Amateur Radio Digital Open Protocol) e o sistema UUCP (Unix to Unix 38 | Communication Protocol). O manual também abrange a interface gráfica 39 | para usuário baseada em tecnologia Web, assim como os serviços e principais 40 | configurações do sistema de referência baseado na raspberry pi. 41 | 42 | \end{abstract} 43 | 44 | \newpage 45 | 46 | \tableofcontents 47 | 48 | \section{Sistema de referência} 49 | 50 | Um rádio com porta USB como o ICOM IC-7100 ou o ICOM IC-7300, ou opcionalmente o ICOM IC-78, ICOM IC-718 ou Vertex 51 | VX-1700 com a interface Signalink USB, conectado a uma Raspberry Pi 3 52 | rodando Linux. 53 | 54 | Para o sistema de alimentação elétrica com baterias e painel solal: 55 | \begin{itemize} 56 | \item Bateria Estacionária 150Ah. Referência: \url{https://www.neosolar.com.br/loja/bateria-estacionaria-moura-clean-12mf150-150ah.html} 57 | \item Um ou dois paineis solares. O ideal seria que juntos totalizem 250W ou mais de potência. 150W é o mínimo aceitável. Referência: \url{https://www.neosolar.com.br/loja/painel-solar-fotovoltaico-155wp-upsolar-up-m155p.html} 58 | \item Controlador de carga de no mínimo 30A. 40A recomendado. Referência: \url{https://www.neosolar.com.br/loja/controlador-carga-pwm-30a-12-24v-epever-landstar-ls3024eu.html} 59 | \end{itemize} 60 | 61 | Para alimentação com energia da rede 110 ou 220V AC, é necessária uma fonte de 13.8V ou 12V. 62 | \begin{itemize} 63 | \item Fonte linear de trafo 30A 13.8V. Referência: \url{https://www.radiohaus.com.br/produto/8498/fonte-maxtron-30a-estabilizada} OU 64 | \item Fonte chaveada 50A 12V de baixo custo. Referência: 65 | \url{https://produto.mercadolivre.com.br/MLB-1240884918-fonte-chaveada-12v-50a-600w-s600-12-cftv-led-bivolt-_JM?quantity=1#position=1&type=item&tracking_id=9596443b-38ac-416f-ab5c-d1adf108e56d} 66 | \end{itemize} 67 | 68 | Para o rádio transceptor transmitir e receber ondas de rádio, uma antena ajustada para a frequência de transmissão é necessária. 69 | 70 | Antena a ser utilizada: 71 | \begin{itemize} 72 | \item Antena dipolo de fio com BALUN feita sob medida para a frequência escolhida, instalada como V invertida. Referências: Electril: \url{http://www.electril.com/electril/dipolo2.htm} ML: \url{https://produto.mercadolivre.com.br/MLB-1435429491-tw713-antena-dipolo-para-40-e-20-metros-com-balun-11-_JM?quantity=1#position=1&type=item&tracking_id=771c82d5-2426-46d9-8cbb-d34b6e1ec2d1} 73 | \end{itemize} 74 | 75 | Rádios Tranceptores HF. Todos eles precisam ser "desbloqueados" (a exceção do IC-78 - confirmar com o vendedor!) para operar em faixas fora da banda de rádio amador - deve-se verificar antes de comprar qual procedimento deve ser feito para desbloqueador o rádio. 76 | 77 | A seguir estão Rádio transceptores mais fáceis para se adicionar comunicação digital pois já possuem porta USB e placa de som embutida (entre parênteses as bandas suportadas - precisamos somente HF). 78 | 79 | \begin{itemize} 80 | \item ICOM IC-7100 (UHF + VHF + HF) 81 | \item ICOM IC-7300 (HF) 82 | \item Yaesu FT-991A (HF) 83 | \end{itemize} 84 | 85 | Rádios mais em conta, que necessitarão de uma interface especial para comunicação digital (ex: Tigertronics Signalink USB): 86 | \begin{itemize} 87 | \item ICOM IC-78 (HF) 88 | \item ICOM IC-718 (HF) 89 | \item Vertex VX-1700 (HF) 90 | \item Alinco DX-SR8T (HF) 91 | \item Yaesu FT-857D (UHF + VHF + HF) 92 | \item Yaesu FT-891 (HF) 93 | \end{itemize} 94 | 95 | Exemplos de interfaces externas para conexão do rádio a um computador, para rádios sem conexão USB (Universal Serial Bus) e conversores AD/DA internos (conexão com PC entre parênteses): 96 | \begin{itemize} 97 | \item Tigertronics Signalink USB (USB) 98 | \item DigiMaster MiniProSC (USB) 99 | \item West Mountain Radio RIGblaster (USB e Bluetooth) 100 | \end{itemize} 101 | 102 | Cabo coaxial com 50ohm de impedância, podendo ser o RG-58, para curta distância ou RG-213, para mais longa distância. 103 | 104 | Os cabos de energia deve ter bitola grande, suficiente para passar picos de mais de 20A em 12V. 105 | 106 | Equipamentos essenciais para teste: 107 | \begin{itemize} 108 | \item Wattimetro com medição de potência direta e refletida para HF. Referência: \url{https://radiohaus.com.br/produto/4064/mfj-822-wattimetro-hf-vhf-18-200-mhz-300-w-movel} 109 | \item Multímetro. Referência: \url{https://produto.mercadolivre.com.br/MLB-681832769-multimetro-digital-portatil-et-1002-minipa-_JM?quantity=1#position=1&type=item&tracking_id=5bd7e5cd-d473-442f-9203-57b77e67a56b} 110 | \item Carga fantasta (dummy load). Referências: \url{https://www.radiohaus.com.br/produto/2969/mfj-262b-carga-fantasma-200w-0-a-1ghz} ou \url{https://produto.mercadolivre.com.br/MLB-912917571-carga-rf-100w-50r-carga-fantasma-preciso-50-ohms-2ghz-_JM#position=15&type=item&tracking_id=cfa216de-0679-46b0-9f31-480e35dd0981} 111 | \end{itemize} 112 | 113 | 114 | \section{Configuração} 115 | 116 | O ARDOP é um modem SDR feito por rádio amadores que utiliza modernas 117 | técnicas de modulação (OFDM) e UUCP é uma sistema para comunicação 118 | assíncrona da década de 70 para sistemas UNIX, muito utilizado até 119 | hoje em nichos, como comunicação em HF. 120 | 121 | Debian Buster (10) arm64 (multilib com armhf no caso se querer usar os 122 | binários ``piardopc'' do John Wiseman) é o sistema Linux de referência, 123 | rodando numa Raspberry Pi 3 com Wifi configurado no modo AP (Access Point), 124 | para provimento de interface do sistema via Web. 125 | 126 | Para integrar o sistema UUCP ao modem ARDOP, o projeto RHIZO-UUARDOPD 127 | foi desenvolvido para prover ferramentas que integram o UUCP ao ARDOP. 128 | 129 | \subsection{Radio e/ou interface} 130 | 131 | No caso de uso da interface Signalink, o ajuste de ``delay'' deve ser 132 | zerado. 133 | 134 | No caso dos ICOM IC-7100, deixar o filtro maior que 2.8kHz para o mode 135 | SSB/Data em uso com o sistema digital. 136 | 137 | \subsection{Alsa} 138 | 139 | Add to ``/etc/asound.conf'': 140 | \begin{verbatim} 141 | pcm.ARDOP {type rate slave {pcm "hw:1,0" rate 48000}} 142 | \end{verbatim} 143 | 144 | \subsection{ARDOP} 145 | 146 | Link para download: \url{http://www.cantab.net/users/john.wiseman/Downloads/Test/TeensyProjects.zip} 147 | 148 | O binário utilizado do ardop deve ficar em /usr/bin/ardop, que pode ser um 149 | link simbólico para /usr/bin/{ardop1ofdm, ardop2, ardopofdm}, por exemplo. 150 | 151 | Configuração do ardop para uso com o ICOM IC-7100 (via porta USB, com PTT 152 | via porta serial): 153 | \begin{verbatim} 154 | [Unit] 155 | Description=ARDOP daemon 156 | 157 | [Service] 158 | Type=simple 159 | ExecStart=/usr/bin/ardop 8515 -c /dev/ttyUSB0 ARDOP ARDOP -k FEFE88E01C0001FD -u FEFE88E01C0000FD 160 | ExecStop=/usr/bin/killall -s QUIT ardop 161 | IgnoreSIGPIPE=no 162 | #StandardOutput=null 163 | #StandardError=null 164 | StandardOutput=syslog 165 | StandardError=syslog 166 | 167 | [Install] 168 | WantedBy=multi-user.target 169 | \end{verbatim} 170 | 171 | Configuração para uso com a interface Signalink (VOX): 172 | \begin{verbatim} 173 | [Unit] 174 | Description=ARDOP daemon 175 | 176 | [Service] 177 | Type=simple 178 | ExecStart=/usr/bin/ardop 8515 ARDOP ARDOP 179 | ExecStop=/usr/bin/killall -s QUIT ardop 180 | IgnoreSIGPIPE=no 181 | #StandardOutput=null 182 | #StandardError=null 183 | StandardOutput=syslog 184 | StandardError=syslog 185 | 186 | [Install] 187 | WantedBy=multi-user.target 188 | \end{verbatim} 189 | 190 | 191 | Iniciar / Parar serviço: 192 | \begin{verbatim} 193 | systemctl start ardop.service 194 | systemctl stop ardop.service 195 | \end{verbatim} 196 | 197 | 198 | Ver log: 199 | \begin{verbatim} 200 | journalctl -f -u ardop 201 | \end{verbatim} 202 | 203 | \subsection{UUCP} 204 | 205 | Usar versão do pacote do debian 1.07-27 ou superior, por exemplo, a versão 206 | do Debian Bullseye: \url{https://packages.debian.org/bullseye/uucp}. 207 | 208 | ps: baixar o .deb e instalar na mão. 209 | 210 | \subsection{rhizo-uuardopd} 211 | 212 | %TODO: It seems everything uucico wrote in the end of a reception, don't get 213 | %to the other side - connection closes and buffer get clean fast! 214 | 215 | Dois binários, uuport (para ser usado pelo uucp) e uuardopd que é o software 216 | que conecta com o modem Ardop através do uucico ou uuport. 217 | 218 | \url{http://github.com/DigitalHERMES/rhizo-uuardop} 219 | 220 | 221 | Iniciar / Parar serviço: 222 | \begin{verbatim} 223 | systemctl start uuardopd.service 224 | systemctl stop uuardopd.service 225 | \end{verbatim} 226 | 227 | 228 | Ver log: 229 | \begin{verbatim} 230 | journalctl -f -u uuardopd 231 | \end{verbatim} 232 | 233 | %\subsection{DHCP} 234 | 235 | %\subsection{DNS} 236 | 237 | %\subsection{Apache + PHP} 238 | 239 | \section{Interface via UUCP em linha de comando} 240 | 241 | O diretório para arquivos recebidos ``/var/www/html/arquivos''. 242 | 243 | Adicionando a fila de envio (``-r'' não inicia a transmissão de forma 244 | automática): 245 | \begin{verbatim} 246 | uucp -C -r -d source.xxx AM4AAB\!/var/www/html/arquivos/${nodename}/ 247 | \end{verbatim} 248 | 249 | Para disparar o envio de todos os jobs para o destinatário 250 | AM4AAA: 251 | \begin{verbatim} 252 | uucico -S AM4AAA 253 | \end{verbatim} 254 | 255 | Para lista os jobs: 256 | \begin{verbatim} 257 | uustat -a 258 | \end{verbatim} 259 | 260 | Para matar jobs: 261 | \begin{verbatim} 262 | uustat -k job 263 | uustat -K 264 | \end{verbatim} 265 | 266 | Ver log: 267 | \begin{verbatim} 268 | uulog 269 | \end{verbatim} 270 | 271 | \section{Interface com o usuário} 272 | 273 | A interface do sistema é baseada em Web, utilizando HTML+PHP e 274 | alguns shell scripts. 275 | 276 | \section{Pré-requisitos} 277 | 278 | \begin{itemize} 279 | \item ImageMagic: para descomprimir imagens 280 | \item mozjpeg: para comprimir em jpg: https://github.com/mozilla/mozjpeg 281 | % \item opusenc: para comprimir áudio 282 | \item gpg: para criptografia 283 | \item hostapd: para o roteador wifi (com senha ou sem senha) 284 | \end{itemize} 285 | 286 | \subsection{Interface Web} 287 | 288 | Acessível digitando qualquer endereço no navegador, ou 192.168.1.1. 289 | 290 | \subsection{WebPhone} 291 | 292 | WIP! 293 | 294 | \url{https://gitlab.tic-ac.org/keith/webphone/wikis/hermes} 295 | 296 | \end{document} 297 | -------------------------------------------------------------------------------- /circular_buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "circular_buffer.h" 15 | #include "shm.h" 16 | 17 | // Private functions 18 | 19 | static void advance_pointer_n(cbuf_handle_t cbuf, size_t len) 20 | { 21 | assert(cbuf); 22 | 23 | if(cbuf->internal->full) 24 | { 25 | cbuf->internal->tail = (cbuf->internal->tail + len) % cbuf->internal->max; 26 | } 27 | 28 | cbuf->internal->head = (cbuf->internal->head + len) % cbuf->internal->max; 29 | 30 | // We mark full because we will advance tail on the next time around 31 | cbuf->internal->full = (cbuf->internal->head == cbuf->internal->tail); 32 | } 33 | 34 | static void advance_pointer(cbuf_handle_t cbuf) 35 | { 36 | assert(cbuf && cbuf->internal); 37 | 38 | if(cbuf->internal->full) 39 | { 40 | cbuf->internal->tail = (cbuf->internal->tail + 1) % cbuf->internal->max; 41 | } 42 | 43 | cbuf->internal->head = (cbuf->internal->head + 1) % cbuf->internal->max; 44 | 45 | // We mark full because we will advance tail on the next time around 46 | cbuf->internal->full = (cbuf->internal->head == cbuf->internal->tail); 47 | } 48 | 49 | static void retreat_pointer_n(cbuf_handle_t cbuf, size_t len) 50 | { 51 | assert(cbuf && cbuf->internal); 52 | 53 | cbuf->internal->full = false; 54 | cbuf->internal->tail = (cbuf->internal->tail + len) % cbuf->internal->max; 55 | } 56 | 57 | static void retreat_pointer(cbuf_handle_t cbuf) 58 | { 59 | assert(cbuf->internal); 60 | 61 | cbuf->internal->full = false; 62 | cbuf->internal->tail = (cbuf->internal->tail + 1) % cbuf->internal->max; 63 | } 64 | 65 | // User APIs 66 | 67 | cbuf_handle_t circular_buf_init(uint8_t* buffer, size_t size) 68 | { 69 | assert(buffer && size); 70 | 71 | cbuf_handle_t cbuf = memalign(SHMLBA, sizeof(struct circular_buf_t)); 72 | assert(cbuf); 73 | 74 | cbuf->internal = memalign(SHMLBA, sizeof(struct circular_buf_t_aux)); 75 | assert(cbuf->internal); 76 | 77 | cbuf->buffer = buffer; 78 | cbuf->internal->max = size; 79 | circular_buf_reset(cbuf); 80 | atomic_flag_clear(&cbuf->internal->acquire); 81 | 82 | assert(circular_buf_empty(cbuf)); 83 | 84 | return cbuf; 85 | } 86 | 87 | cbuf_handle_t circular_buf_init_shm(size_t size, key_t key) 88 | { 89 | assert(size); 90 | 91 | cbuf_handle_t cbuf = memalign(SHMLBA, sizeof(struct circular_buf_t)); 92 | assert(cbuf); 93 | 94 | if (shm_is_created(key, size)) 95 | { 96 | fprintf(stderr, "shm key %u already created. Re-creating.\n", key); 97 | shm_destroy(key, size); 98 | } 99 | shm_create(key, size); 100 | 101 | cbuf->buffer = shm_attach(key, size); 102 | assert(cbuf->buffer); 103 | 104 | key++; 105 | if (shm_is_created(key, sizeof(struct circular_buf_t_aux))) 106 | { 107 | fprintf(stderr, "shm key %u already created. Re-creating.\n", key); 108 | shm_destroy(key, sizeof(struct circular_buf_t_aux)); 109 | } 110 | shm_create(key, sizeof(struct circular_buf_t_aux)); 111 | 112 | cbuf->internal = shm_attach(key, sizeof(struct circular_buf_t_aux)); 113 | assert(cbuf->internal); 114 | 115 | cbuf->internal->max = size; 116 | atomic_flag_clear(&cbuf->internal->acquire); 117 | 118 | circular_buf_reset(cbuf); 119 | 120 | assert(circular_buf_empty(cbuf)); 121 | 122 | return cbuf; 123 | } 124 | 125 | cbuf_handle_t circular_buf_connect_shm(size_t size, key_t key) 126 | { 127 | assert(size); 128 | 129 | cbuf_handle_t cbuf = memalign(SHMLBA, sizeof(struct circular_buf_t)); 130 | assert(cbuf); 131 | 132 | cbuf->buffer = shm_attach(key, size); 133 | assert(cbuf->buffer); 134 | 135 | cbuf->internal = shm_attach(key+1, sizeof(struct circular_buf_t_aux)); 136 | assert(cbuf->internal); 137 | 138 | assert (cbuf->internal->max == size); 139 | 140 | return cbuf; 141 | } 142 | 143 | 144 | void circular_buf_free(cbuf_handle_t cbuf) 145 | { 146 | assert(cbuf && cbuf->internal); 147 | free(cbuf->internal); 148 | free(cbuf); 149 | } 150 | 151 | void circular_buf_free_shm(cbuf_handle_t cbuf, size_t size, key_t key) 152 | { 153 | assert(cbuf && cbuf->internal && cbuf->buffer); 154 | shm_dettach(key, size, cbuf->buffer); 155 | shm_destroy(key, size); 156 | shm_dettach(key+1, sizeof(struct circular_buf_t_aux), cbuf->internal); 157 | shm_destroy(key+1, sizeof(struct circular_buf_t_aux)); 158 | free(cbuf); 159 | } 160 | 161 | void circular_buf_reset(cbuf_handle_t cbuf) 162 | { 163 | assert(cbuf && cbuf->internal); 164 | 165 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 166 | 167 | cbuf->internal->head = 0; 168 | cbuf->internal->tail = 0; 169 | cbuf->internal->full = false; 170 | 171 | atomic_flag_clear(&cbuf->internal->acquire); 172 | } 173 | 174 | size_t circular_buf_size(cbuf_handle_t cbuf) 175 | { 176 | assert(cbuf && cbuf->internal); 177 | 178 | size_t size = circular_buf_capacity(cbuf); 179 | 180 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 181 | 182 | if(!cbuf->internal->full) 183 | { 184 | if(cbuf->internal->head >= cbuf->internal->tail) 185 | { 186 | size = (cbuf->internal->head - cbuf->internal->tail); 187 | } 188 | else 189 | { 190 | size = (cbuf->internal->max + cbuf->internal->head - cbuf->internal->tail); 191 | } 192 | 193 | } 194 | 195 | atomic_flag_clear(&cbuf->internal->acquire); 196 | 197 | return size; 198 | } 199 | 200 | size_t circular_buf_free_size(cbuf_handle_t cbuf) 201 | { 202 | assert(cbuf->internal); 203 | 204 | size_t size = 0; 205 | 206 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 207 | 208 | if(!cbuf->internal->full) 209 | { 210 | if(cbuf->internal->head >= cbuf->internal->tail) 211 | { 212 | size = cbuf->internal->max - (cbuf->internal->head - cbuf->internal->tail); 213 | } 214 | else 215 | { 216 | size = (cbuf->internal->tail - cbuf->internal->head); 217 | } 218 | 219 | } 220 | 221 | atomic_flag_clear(&cbuf->internal->acquire); 222 | 223 | return size; 224 | } 225 | 226 | size_t circular_buf_capacity(cbuf_handle_t cbuf) 227 | { 228 | assert(cbuf->internal); 229 | 230 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 231 | 232 | size_t capacity = cbuf->internal->max; 233 | 234 | atomic_flag_clear(&cbuf->internal->acquire); 235 | 236 | return capacity; 237 | } 238 | 239 | void circular_buf_put(cbuf_handle_t cbuf, uint8_t data) 240 | { 241 | assert(cbuf && cbuf->internal && cbuf->buffer); 242 | 243 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 244 | 245 | cbuf->buffer[cbuf->internal->head] = data; 246 | advance_pointer(cbuf); 247 | 248 | atomic_flag_clear(&cbuf->internal->acquire); 249 | } 250 | 251 | int circular_buf_put2(cbuf_handle_t cbuf, uint8_t data) 252 | { 253 | assert(cbuf && cbuf->internal && cbuf->buffer); 254 | 255 | int r = -1; 256 | 257 | if(!circular_buf_full(cbuf)) 258 | { 259 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 260 | 261 | cbuf->buffer[cbuf->internal->head] = data; 262 | advance_pointer(cbuf); 263 | 264 | atomic_flag_clear(&cbuf->internal->acquire); 265 | r = 0; 266 | } 267 | 268 | return r; 269 | } 270 | 271 | int circular_buf_get(cbuf_handle_t cbuf, uint8_t * data) 272 | { 273 | assert(cbuf && data && cbuf->internal && cbuf->buffer); 274 | 275 | int r = -1; 276 | 277 | if(!circular_buf_empty(cbuf)) 278 | { 279 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 280 | 281 | *data = cbuf->buffer[cbuf->internal->tail]; 282 | retreat_pointer(cbuf); 283 | 284 | atomic_flag_clear(&cbuf->internal->acquire); 285 | r = 0; 286 | } 287 | return r; 288 | } 289 | 290 | bool circular_buf_empty(cbuf_handle_t cbuf) 291 | { 292 | assert(cbuf && cbuf->internal); 293 | 294 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 295 | 296 | bool is_empty = !cbuf->internal->full && (cbuf->internal->head == cbuf->internal->tail); 297 | 298 | atomic_flag_clear(&cbuf->internal->acquire); 299 | 300 | return is_empty; 301 | } 302 | 303 | bool circular_buf_full(cbuf_handle_t cbuf) 304 | { 305 | assert(cbuf && cbuf->internal); 306 | 307 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 308 | 309 | bool is_full = cbuf->internal->full; 310 | 311 | atomic_flag_clear(&cbuf->internal->acquire); 312 | 313 | return is_full; 314 | } 315 | 316 | 317 | int circular_buf_get_range(cbuf_handle_t cbuf, uint8_t *data, size_t len) 318 | { 319 | assert(cbuf && data && cbuf->internal && cbuf->buffer); 320 | 321 | #if 0 322 | for (int i = 0; i < len; i++) 323 | circular_buf_get(cbuf, &data[i]); 324 | 325 | return 0; 326 | #endif 327 | 328 | #if 1 329 | int r = -1; 330 | size_t size = circular_buf_capacity(cbuf); 331 | 332 | if(circular_buf_size(cbuf) >= len) 333 | { 334 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 335 | 336 | if ( ((cbuf->internal->tail + len) % size) > cbuf->internal->tail) 337 | { 338 | memcpy(data, cbuf->buffer + cbuf->internal->tail, len); 339 | }else 340 | { 341 | memcpy(data, cbuf->buffer + cbuf->internal->tail, size - cbuf->internal->tail); 342 | memcpy(data + (size - cbuf->internal->tail), cbuf->buffer, len - (size - cbuf->internal->tail)); 343 | } 344 | retreat_pointer_n(cbuf, len); 345 | 346 | atomic_flag_clear(&cbuf->internal->acquire); 347 | 348 | r = 0; 349 | } 350 | 351 | return r; 352 | #endif 353 | } 354 | 355 | int circular_buf_put_range(cbuf_handle_t cbuf, uint8_t * data, size_t len) 356 | { 357 | assert(cbuf && cbuf->internal && cbuf->buffer); 358 | 359 | #if 0 360 | for (int i = 0; i < len; i++) 361 | circular_buf_put(cbuf, data[i]); 362 | 363 | return 0; 364 | #endif 365 | 366 | #if 1 367 | int r = -1; 368 | size_t size = circular_buf_capacity(cbuf); 369 | // test if buffer has enough free space 370 | 371 | if(!circular_buf_full(cbuf)) 372 | { 373 | while (atomic_flag_test_and_set(&cbuf->internal->acquire)); 374 | if ( ((cbuf->internal->head + len) % size) > cbuf->internal->head) 375 | { 376 | memcpy(cbuf->buffer + cbuf->internal->head, data, len); 377 | } else 378 | { 379 | memcpy(cbuf->buffer + cbuf->internal->head, data, size - cbuf->internal->head); 380 | memcpy(cbuf->buffer, data + (size - cbuf->internal->head), len - (size - cbuf->internal->head)); 381 | } 382 | 383 | advance_pointer_n(cbuf, len); 384 | atomic_flag_clear(&cbuf->internal->acquire); 385 | r = 0; 386 | } 387 | 388 | return r; 389 | #endif 390 | } 391 | -------------------------------------------------------------------------------- /uuardopd.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuhf: Tools to integrate HF TNCs to UUCP 2 | * Copyright (C) 2019-2021 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | */ 21 | 22 | /** 23 | * @file uuardopd.c 24 | * @author Rafael Diniz 25 | * @date 10 Jul 2019 26 | * @brief UUCP ARDOP daemon 27 | * 28 | * UUARDOPD main C file. 29 | * 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "serial.h" 48 | #include "call_uucico.h" 49 | #include "uuardopd.h" 50 | #include "ardop.h" 51 | #include "vara.h" 52 | #include "shm.h" 53 | #include "circular_buffer.h" 54 | 55 | // temporary global variable to enable sockets closure 56 | rhizo_conn *tmp_conn = NULL; 57 | 58 | // radio shm connector made easy... 59 | controller_conn *radio_conn = NULL; 60 | 61 | void finish(int s){ 62 | fprintf(stderr, "\nExiting...\n"); 63 | 64 | /* Do house cleaning work here */ 65 | if (tmp_conn){ 66 | if (tmp_conn->data_socket){ 67 | shutdown(tmp_conn->data_socket, SHUT_RDWR); 68 | close (tmp_conn->data_socket); 69 | } 70 | if (tmp_conn->control_socket){ 71 | shutdown(tmp_conn->control_socket, SHUT_RDWR); 72 | close (tmp_conn->control_socket); 73 | } 74 | } 75 | 76 | // clean buffers... 77 | circular_buf_reset(tmp_conn->in_buffer); 78 | circular_buf_reset(tmp_conn->out_buffer); 79 | 80 | tmp_conn->shutdown = true; 81 | 82 | connected_led_off(tmp_conn->serial_fd, tmp_conn->radio_type); 83 | sys_led_off(tmp_conn->serial_fd, tmp_conn->radio_type); 84 | 85 | // TODO: close the pipes here 86 | // join all the threads? 87 | 88 | exit(EXIT_SUCCESS); 89 | } 90 | 91 | void *modem_thread(void *conn) 92 | { 93 | rhizo_conn *connector = (rhizo_conn *) conn; 94 | 95 | if (!strcmp("vara", connector->modem_type)){ 96 | initialize_modem_vara(connector); 97 | } 98 | 99 | if (!strcmp("ardop", connector->modem_type)){ 100 | initialize_modem_ardop(connector); 101 | } 102 | 103 | return NULL; 104 | } 105 | 106 | bool initialize_connector(rhizo_conn *connector){ 107 | 108 | connector->in_buffer = circular_buf_init_shm(INTERNAL_BUFFER_SIZE, SYSV_SHM_KEY_IB); 109 | connector->out_buffer = circular_buf_init_shm(INTERNAL_BUFFER_SIZE, SYSV_SHM_KEY_OB); 110 | 111 | connector->connected = false; 112 | connector->waiting_for_connection = false; 113 | connector->serial_keying = false; 114 | connector->serial_fd = -1; 115 | connector->ask_login = false; 116 | connector->clean_buffers = false; // <- this also means -> ask for disconnection! 117 | 118 | connector->shutdown = false; 119 | 120 | connector->radio_type = RADIO_TYPE_SHM; 121 | connector->timeout = TIMEOUT_DEFAULT; 122 | connector->ofdm_mode = true; 123 | connector->vara_mode = 2300; 124 | connector->buffer_size = 0; 125 | connector->session_counter_read = 0; 126 | connector->session_counter_write = 0; 127 | 128 | connector->call_sign[0] = 0; // --> set default to uucp nodename 129 | connector->remote_call_sign[0] = 0; // --> set default to CQ 130 | 131 | connector->uucico_active = false; 132 | connector->send_break = false; 133 | 134 | return true; 135 | } 136 | 137 | bool get_call_sign_from_uucp(rhizo_conn *connector) 138 | { 139 | FILE *uuconf = fopen(UUCP_CONFIG, "r"); 140 | if (uuconf == NULL) 141 | { 142 | fprintf(stderr, "No Call sign specified and UUCP config could not be opened.\n"); 143 | return false; 144 | } 145 | 146 | int j, i; 147 | char buff_call[BUFFER_SIZE]; 148 | char local_buff[BUFFER_SIZE]; 149 | while(fgets(buff_call, BUFFER_SIZE, uuconf)) 150 | { 151 | i = 0; j = 0; 152 | while (buff_call[i] != '\n') 153 | { 154 | if(buff_call[i] == '#' || isblank(buff_call[i])) 155 | { 156 | j = 0; 157 | break; 158 | } 159 | 160 | if(isalnum(buff_call[i])) 161 | { 162 | local_buff[j] = buff_call[i]; 163 | local_buff[++j] = 0; 164 | } 165 | i++; 166 | 167 | if (!strncmp("nodename", local_buff, 8)) 168 | { 169 | while (buff_call[i] == ' ') 170 | i++; 171 | sscanf(&buff_call[i], "%s", connector->call_sign); 172 | goto got_callsign; 173 | } 174 | } 175 | } 176 | 177 | if (connector->call_sign[0] == 0) 178 | { 179 | fprintf(stderr, "No Call sign specified and could not read call sign from UUCP config.\n"); 180 | fclose(uuconf); 181 | return false; 182 | } 183 | 184 | got_callsign: 185 | fclose(uuconf); 186 | return true; 187 | 188 | } 189 | 190 | int main (int argc, char *argv[]) 191 | { 192 | rhizo_conn *connector = NULL; 193 | 194 | if (shm_is_created(SYSV_SHM_KEY_STR, sizeof(rhizo_conn))) 195 | { 196 | fprintf(stderr, "Connector SHM is already created!\nDestroying it and creating again.\n"); 197 | shm_destroy(SYSV_SHM_KEY_STR, sizeof(rhizo_conn)); 198 | } 199 | shm_create(SYSV_SHM_KEY_STR, sizeof(rhizo_conn)); 200 | 201 | connector = shm_attach(SYSV_SHM_KEY_STR, sizeof(rhizo_conn)); 202 | tmp_conn = connector; 203 | 204 | initialize_connector(connector); 205 | 206 | signal (SIGINT, finish); 207 | signal (SIGQUIT, finish); 208 | signal (SIGTERM, finish); 209 | 210 | // Catch SIGPIPE 211 | // signal (SIGPIPE, pipe_fucked); 212 | signal(SIGPIPE, SIG_IGN); // ignores SIGPIPE... 213 | 214 | fprintf(stderr, "Rhizomatica's uuardopd version 0.2 by Rafael Diniz - rafael (AT) rhizomatica (DOT) org\n"); 215 | fprintf(stderr, "License: GNU AGPL version 3+\n\n"); 216 | 217 | 218 | if (argc < 7) 219 | { 220 | manual: 221 | fprintf(stderr, "Usage modes: \n%s -r vara -a tnc_ip_address -p tcp_base_port -s /dev/ttyUSB0 [-l]\n", argv[0]); 222 | fprintf(stderr, "%s -h\n", argv[0]); 223 | fprintf(stderr, "\nOptions:\n"); 224 | fprintf(stderr, " -r [ardop,vara] Choose modem/radio type.\n"); 225 | fprintf(stderr, " -c callsign Station Callsign (Eg: PU2HFF). Not setting it will cause the hostname to be retrieved from uucp config\n"); 226 | fprintf(stderr, " -d remote_callsign Remote Station Callsign.\n"); 227 | fprintf(stderr, " -a tnc_ip_address IP address of the TNC,\n"); 228 | fprintf(stderr, " -p tnc_tcp_base_port TNC's TCP base port of the TNC. ARDOP uses ports tcp_base_port and tcp_base_port+1.\n"); 229 | fprintf(stderr, " -t timeout Time to wait before disconnect when idling (only for ardop).\n"); 230 | fprintf(stderr, " -f features Supported features ARDOP: ofdm, noofdm (default: ofdm).\n"); 231 | fprintf(stderr, " Supported features VARA, BW mode: 500, 2300 or 2750 (default: 2300).\n"); 232 | fprintf(stderr, " -s serial_device Set the serial device file path for keying the radio (VARA ONLY).\n"); 233 | fprintf(stderr, " -l Tell UUCICO to ask login prompt (default: disabled).\n"); 234 | fprintf(stderr, " -o [icom,ubitx,shm] Sets radio type (supported: icom, ubitx or shm)\n"); 235 | fprintf(stderr, " -h Prints this help.\n"); 236 | exit(EXIT_FAILURE); 237 | } 238 | 239 | int opt; 240 | while ((opt = getopt(argc, argv, "hlc:d:p:a:t:f:o:r:s:")) != -1) 241 | { 242 | switch (opt) 243 | { 244 | case 'h': 245 | goto manual; 246 | break; 247 | case 'l': 248 | connector->ask_login = true; 249 | break; 250 | case 'r': 251 | strcpy(connector->modem_type, optarg); 252 | break; 253 | case 'o': 254 | if (!strcmp(optarg,"icom")) 255 | connector->radio_type = RADIO_TYPE_ICOM; 256 | if (!strcmp(optarg,"ubitx")) 257 | connector->radio_type = RADIO_TYPE_UBITX; 258 | if (!strcmp(optarg,"shm")) 259 | connector->radio_type = RADIO_TYPE_SHM; 260 | break; 261 | case 'c': 262 | strcpy(connector->call_sign, optarg); 263 | break; 264 | case 'd': 265 | strcpy(connector->remote_call_sign, optarg); 266 | break; 267 | case 't': 268 | connector->timeout = atoi(optarg); 269 | break; 270 | case 'p': 271 | connector->tcp_base_port = atoi(optarg); 272 | break; 273 | case 'a': 274 | strcpy(connector->ip_address, optarg); 275 | break; 276 | case 's': 277 | connector->serial_keying = true; 278 | strcpy(connector->serial_path, optarg); 279 | break; 280 | case 'f': 281 | if(strstr(optarg, "noofdm")) 282 | connector->ofdm_mode = false; 283 | else if(strstr(optarg, "ofdm")) 284 | connector->ofdm_mode = true; 285 | else 286 | connector->vara_mode = atoi(optarg); 287 | break; 288 | default: 289 | goto manual; 290 | } 291 | } 292 | 293 | if (connector->call_sign[0] == 0) 294 | { 295 | if (get_call_sign_from_uucp(connector) == false) 296 | goto manual; 297 | fprintf(stderr, "Call-sign obtained from uucp config: %s\n", connector->call_sign); 298 | } 299 | 300 | if (connector->remote_call_sign[0] == 0) 301 | { 302 | strcpy(connector->remote_call_sign, "CQ"); 303 | // fprintf(stderr, "Destination call-sign not set. Using CQ.\n"); 304 | } 305 | 306 | if (!strcmp("vara", connector->modem_type) && 307 | (connector->vara_mode != 500) && 308 | (connector->vara_mode != 2300) && 309 | (connector->vara_mode != 2750) ) 310 | { 311 | fprintf(stderr, "Wrong Vara Mode BW %u.\n", connector->vara_mode); 312 | goto manual; 313 | } 314 | 315 | controller_conn *shm_radio_connector = NULL; 316 | if (connector->radio_type == RADIO_TYPE_SHM) 317 | { 318 | if (shm_is_created(SYSV_SHM_CONTROLLER_KEY_STR, sizeof(controller_conn)) == false) 319 | { 320 | fprintf(stderr, "SHM Radio Connector SHM not created. Is ubitx_controller running?\n"); 321 | return EXIT_FAILURE; 322 | } 323 | shm_radio_connector = shm_attach(SYSV_SHM_CONTROLLER_KEY_STR, sizeof(controller_conn)); 324 | radio_conn = shm_radio_connector; 325 | } 326 | 327 | connected_led_off(connector->serial_fd, connector->radio_type); 328 | 329 | pthread_t tid; 330 | pthread_create(&tid, NULL, uucico_thread, (void *) connector); 331 | 332 | modem_thread((void *) connector); 333 | fprintf(stderr, "Modem connection lost.\n"); 334 | 335 | connector->shutdown = true; 336 | // workaround... this was not supposed to be the last line of code... 337 | finish(0); 338 | return EXIT_SUCCESS; 339 | 340 | // should we try to reconnect! 341 | #if 0 342 | if ((connector->shutdown == true) && reconnect) 343 | { 344 | pthread_cancel(everybody); 345 | ring_buffer_clean(all_buffers); 346 | goto start_again; 347 | } 348 | #endif 349 | 350 | pthread_join(tid, NULL); 351 | fprintf(stderr, "uucico recv listener thread finish.\n"); 352 | 353 | return EXIT_SUCCESS; 354 | } 355 | -------------------------------------------------------------------------------- /vara.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop 2 | * Copyright (C) 2018-2021 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Vara support routines 21 | */ 22 | 23 | /** 24 | * @file vara.c 25 | * @author Rafael Diniz 26 | * @date 22 Dec 2020 27 | * @brief VARA modem support functions 28 | * 29 | * All the specific code for supporting VARA. 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "common.h" 46 | #include "net.h" 47 | #include "call_uucico.h" 48 | #include "vara.h" 49 | #include "serial.h" 50 | 51 | void *vara_data_worker_thread_tx(void *conn) 52 | { 53 | rhizo_conn *connector = (rhizo_conn *) conn; 54 | uint8_t buffer[BUFFER_SIZE]; 55 | int bytes_to_read; 56 | 57 | while(connector->shutdown == false){ 58 | 59 | // check if we are connected, otherwise, wait 60 | while (connector->connected == false || circular_buf_size(connector->in_buffer) == 0){ 61 | if (connector->shutdown == true){ 62 | goto exit_local; 63 | } 64 | // fprintf(stderr, "vara_data_worker_thread_tx: sleeping\n"); 65 | sleep(1); 66 | } 67 | 68 | check_data_again: 69 | bytes_to_read = circular_buf_size(connector->in_buffer); 70 | if (bytes_to_read > BUFFER_SIZE) 71 | bytes_to_read = BUFFER_SIZE; 72 | if (bytes_to_read == 0) 73 | { 74 | usleep(50000); // 50ms 75 | goto check_data_again; 76 | } 77 | 78 | circular_buf_get_range(connector->in_buffer, buffer, bytes_to_read); 79 | 80 | // fprintf(stderr, "vara_data_worker_thread_tx: Read %d for sending to VARA\n", bytes_to_read); 81 | 82 | while (connector->buffer_size + bytes_to_read > MAX_VARA_BUFFER) 83 | sleep(1); 84 | 85 | if (tcp_write(connector->data_socket, buffer, bytes_to_read) == false) 86 | { 87 | fprintf(stderr, "Error in tcp_write(data_socket)\n"); 88 | connector->shutdown = true; 89 | goto exit_local; 90 | } 91 | 92 | // buffer management hack 93 | sleep(1); 94 | } 95 | 96 | exit_local: 97 | fprintf(stderr, "vara_data_worker_thread_tx exit.\n"); 98 | return EXIT_SUCCESS; 99 | } 100 | 101 | void *vara_data_worker_thread_rx(void *conn) 102 | { 103 | rhizo_conn *connector = (rhizo_conn *) conn; 104 | uint8_t buffer[MAX_VARA_PACKET_SAFE]; 105 | 106 | while(connector->shutdown == false){ 107 | 108 | while (connector->connected == false){ 109 | if (connector->shutdown == true){ 110 | goto exit_local; 111 | } 112 | sleep(1); 113 | } 114 | 115 | if (tcp_read(connector->data_socket, buffer, 1) == false) 116 | { 117 | connector->shutdown = true; 118 | goto exit_local; 119 | } 120 | 121 | while (circular_buf_free_size(connector->out_buffer) < 1) 122 | { 123 | usleep(100000); // 100ms 124 | } 125 | circular_buf_put_range(connector->out_buffer, buffer, 1); 126 | 127 | } 128 | 129 | exit_local: 130 | fprintf(stderr, "vara_data_worker_thread_rx exit.\n"); 131 | return EXIT_SUCCESS; 132 | } 133 | 134 | void *vara_control_worker_thread_rx(void *conn) 135 | { 136 | rhizo_conn *connector = (rhizo_conn *) conn; 137 | uint8_t rcv_byte; 138 | uint8_t buffer[1024]; 139 | int counter = 0; 140 | bool new_cmd = false; 141 | 142 | while(connector->shutdown == false){ 143 | 144 | if (tcp_read(connector->control_socket, &rcv_byte, 1) == false) 145 | { 146 | fprintf(stderr, "Error in tcp_read(control_socket)\n"); 147 | connector->shutdown = true; 148 | goto exit_local; 149 | } 150 | 151 | if (rcv_byte == '\r'){ 152 | buffer[counter] = 0; 153 | counter = 0; 154 | new_cmd = true; 155 | } 156 | else{ 157 | buffer[counter] = rcv_byte; 158 | counter++; 159 | new_cmd = false; 160 | } 161 | 162 | if (new_cmd){ 163 | if (!strcmp((char *) buffer, "DISCONNECTED")){ 164 | fprintf(stderr, "TNC: %s\n", buffer); 165 | connector->clean_buffers = true; 166 | connector->connected = false; 167 | connected_led_off(connector->serial_fd, connector->radio_type); 168 | connector->waiting_for_connection = false; 169 | } else 170 | // other commands here 171 | if (!memcmp(buffer, "CONNECTED", strlen("CONNECTED"))){ 172 | fprintf(stderr, "TNC: %s\n", buffer); 173 | connector->connected = true; 174 | connected_led_on(connector->serial_fd, connector->radio_type); 175 | if (connector->waiting_for_connection == false) 176 | { // we are receiving a connection... call uucico! 177 | bool retval = call_uucico(connector); 178 | if (retval == false) 179 | fprintf(stderr, "Error calling call_uucico()!\n"); 180 | } 181 | connector->waiting_for_connection = false; 182 | } else 183 | if (!memcmp(buffer, "BUFFER", strlen("BUFFER"))) 184 | { 185 | sscanf( (char *) buffer, "BUFFER %d", &connector->buffer_size); 186 | fprintf(stderr, "BUFFER: %d\n", connector->buffer_size); 187 | } else 188 | { 189 | if (connector->serial_keying == true || connector->radio_type == RADIO_TYPE_SHM) 190 | { 191 | if (!memcmp(buffer, "PTT ON", strlen("PTT ON"))) 192 | { 193 | key_on(connector->serial_fd, connector->radio_type); 194 | } 195 | if (!memcmp(buffer, "PTT OFF", strlen("PTT OFF"))){ 196 | key_off(connector->serial_fd, connector->radio_type); 197 | } 198 | } 199 | // lets not print IMALIVE watchdog 200 | if (memcmp(buffer, "IAMALIVE", strlen("IAMALIVE"))) 201 | fprintf(stderr, "%s\n", buffer); 202 | } 203 | } 204 | } 205 | 206 | exit_local: 207 | fprintf(stderr, "vara_control_worker_thread_rx exit.\n"); 208 | return EXIT_SUCCESS; 209 | } 210 | 211 | void *vara_control_worker_thread_tx(void *conn) 212 | { 213 | rhizo_conn *connector = (rhizo_conn *) conn; 214 | char buffer[1024]; 215 | bool ret = true; 216 | 217 | // We set a call sign 218 | memset(buffer,0,sizeof(buffer)); 219 | sprintf(buffer, "MYCALL %s\r", connector->call_sign); 220 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 221 | 222 | memset(buffer,0,sizeof(buffer)); 223 | strcpy(buffer,"LISTEN ON\r"); 224 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 225 | 226 | memset(buffer,0,sizeof(buffer)); 227 | strcpy(buffer,"PUBLIC OFF\r"); 228 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 229 | 230 | memset(buffer,0,sizeof(buffer)); 231 | sprintf(buffer,"BW%u\r", connector->vara_mode); 232 | // strcpy(buffer,"BW2300\r"); 233 | // strcpy(buffer,"BW500\r"); 234 | // strcpy(buffer,"BW2750\r"); 235 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 236 | 237 | memset(buffer,0,sizeof(buffer)); 238 | sprintf(buffer,"COMPRESSION FILES\r"); 239 | // sprintf(buffer,"COMPRESSION TEXT\r"); 240 | // sprintf(buffer,"COMPRESSION OFF\r"); 241 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 242 | 243 | // check lost tcp connection 244 | if (ret == false) 245 | { 246 | fprintf(stderr, "Error in tcp_write(control_socket)\n"); 247 | connector->shutdown = true; 248 | goto exit_local; 249 | } 250 | 251 | // 1Hz function 252 | while(connector->shutdown == false){ 253 | 254 | ret = true; 255 | 256 | if (connector->clean_buffers == true) 257 | { 258 | if (connector->connected == true) 259 | { 260 | connector->send_break = false; 261 | sleep(1); 262 | memset(buffer,0,sizeof(buffer)); 263 | sprintf(buffer,"DISCONNECT\r"); 264 | // sprintf(buffer,"ABORT\r"); // shouldn't we use abort here? 265 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 266 | } 267 | usleep(1200000); // sleep for threads finish their jobs (more than 1s here) 268 | 269 | fprintf(stderr, "Killing uucico.\n"); 270 | system("killall uucico"); 271 | 272 | fprintf(stderr, "Killing uuport.\n"); 273 | system("killall uuport"); 274 | 275 | fprintf(stderr, "Connection closed - Cleaning internal buffers.\n"); 276 | circular_buf_reset(connector->in_buffer); 277 | circular_buf_reset(connector->out_buffer); 278 | 279 | while (connector->connected == true) 280 | usleep(100000); 281 | 282 | usleep(1200000); // sleep for threads finish their jobs (more than 1s here) 283 | 284 | fprintf(stderr, "Connection closed - Cleaning internal buffers 2.\n"); 285 | circular_buf_reset(connector->in_buffer); 286 | circular_buf_reset(connector->out_buffer); 287 | 288 | connector->clean_buffers = false; 289 | } 290 | 291 | // Logic to start a connection 292 | // condition for connection: no connection AND something to transmitt AND we did not issue a CONNECT recently 293 | if (connector->connected == false && 294 | circular_buf_size(connector->in_buffer) > 0 && 295 | !connector->waiting_for_connection){ 296 | 297 | memset(buffer,0,sizeof(buffer)); 298 | sprintf(buffer,"CONNECT %s %s\r", connector->call_sign, 299 | connector->remote_call_sign); 300 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 301 | 302 | fprintf(stderr, "CONNECTING... %s\n", buffer); 303 | 304 | connector->waiting_for_connection = true; 305 | } 306 | 307 | // check lost tcp connection 308 | if (ret == false) 309 | { 310 | fprintf(stderr, "Error in tcp_write(control_socket)\n"); 311 | connector->shutdown = true; 312 | goto exit_local; 313 | } 314 | 315 | sleep(1); // 1Hz function 316 | } 317 | 318 | exit_local: 319 | fprintf(stderr, "vara_control_worker_thread_tx exit.\n"); 320 | return EXIT_SUCCESS; 321 | } 322 | 323 | bool initialize_modem_vara(rhizo_conn *connector) 324 | { 325 | bool ret = true; 326 | 327 | ret &= tcp_connect(connector->ip_address, connector->tcp_base_port, &connector->control_socket); 328 | ret &= tcp_connect(connector->ip_address, connector->tcp_base_port+1, &connector->data_socket); 329 | 330 | if (ret == false) 331 | { 332 | fprintf(stderr, "Connection to TNC failure.\n"); 333 | connector->shutdown = true; 334 | return false; 335 | } 336 | 337 | if (connector->serial_keying == true) 338 | { 339 | connector->serial_fd = open_serial_port(connector->serial_path); 340 | 341 | if (connector->serial_fd == -1) 342 | { 343 | fprintf(stderr, "Could not open serial device.\n"); 344 | return false; 345 | } 346 | 347 | if (connector->radio_type == RADIO_TYPE_ICOM) 348 | set_fixed_baudrate("19200", connector->serial_fd); 349 | 350 | if (connector->radio_type == RADIO_TYPE_UBITX) 351 | set_fixed_baudrate("38400", connector->serial_fd); 352 | } 353 | 354 | sys_led_on(connector->serial_fd, connector->radio_type); 355 | 356 | // we start our control thread 357 | pthread_t tid1; 358 | pthread_create(&tid1, NULL, vara_control_worker_thread_rx, (void *) connector); 359 | 360 | // we start our control tx thread 361 | pthread_t tid2; 362 | pthread_create(&tid2, NULL, vara_control_worker_thread_tx, (void *) connector); 363 | 364 | pthread_t tid3; 365 | pthread_create(&tid3, NULL, vara_data_worker_thread_tx, (void *) connector); 366 | 367 | pthread_t tid4; 368 | pthread_create(&tid4, NULL, vara_data_worker_thread_rx, (void *) connector); 369 | 370 | // pthread_t tid5; 371 | // pthread_create(&tid5, NULL, connection_timeout_thread, (void *) connector); 372 | 373 | pthread_join(tid1, NULL); 374 | pthread_join(tid2, NULL); 375 | pthread_join(tid3, NULL); 376 | pthread_join(tid4, NULL); 377 | // pthread_join(tid5, NULL); 378 | 379 | return true; 380 | } 381 | -------------------------------------------------------------------------------- /ardop.c: -------------------------------------------------------------------------------- 1 | /* Rhizo-uuardop: Tools to integrate Ardop to UUCP 2 | * Copyright (C) 2019 Rhizomatica 3 | * Author: Rafael Diniz 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Ardop support routines 21 | */ 22 | 23 | /** 24 | * @file ardop.c 25 | * @author Rafael Diniz 26 | * @date 12 Apr 2018 27 | * @brief Ardop modem support functions 28 | * 29 | * All the specific code for supporting Ardop. 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | 45 | #include "common.h" 46 | #include "ardop.h" 47 | #include "net.h" 48 | #include "call_uucico.h" 49 | #include "serial.h" 50 | 51 | void *ardop_data_worker_thread_tx(void *conn) 52 | { 53 | rhizo_conn *connector = (rhizo_conn *) conn; 54 | uint8_t buffer[BUFFER_SIZE]; 55 | int bytes_to_read; 56 | uint8_t ardop_size[2]; 57 | uint32_t packet_size; 58 | 59 | while(connector->shutdown == false){ 60 | 61 | // check if we are connected, otherwise, wait 62 | while (connector->connected == false || circular_buf_size(connector->in_buffer) == 0){ 63 | if (connector->shutdown == true){ 64 | goto exit_local; 65 | } 66 | sleep(1); 67 | } 68 | 69 | check_data_again: 70 | bytes_to_read = circular_buf_size(connector->in_buffer); 71 | if (bytes_to_read > BUFFER_SIZE) 72 | bytes_to_read = BUFFER_SIZE; 73 | if (bytes_to_read == 0) 74 | { 75 | usleep(50000); // 50ms 76 | goto check_data_again; 77 | } 78 | 79 | circular_buf_get_range(connector->in_buffer, buffer, bytes_to_read); 80 | 81 | fprintf(stderr, "ardop_data_worker_thread_tx: After read buffer\n"); 82 | 83 | packet_size = bytes_to_read; 84 | 85 | uint32_t counter = 0; 86 | uint32_t tx_size = packet_size; 87 | while (tx_size != 0){ 88 | 89 | while (connector->buffer_size + packet_size > MAX_ARDOP_BUFFER) 90 | sleep(1); 91 | 92 | if (tx_size > MAX_ARDOP_PACKET){ 93 | ardop_size[0] = (uint8_t) (MAX_ARDOP_PACKET >> 8); 94 | ardop_size[1] = (uint8_t) (MAX_ARDOP_PACKET & 255); 95 | } 96 | else{ 97 | ardop_size[0] = (uint8_t) (tx_size >> 8); 98 | ardop_size[1] = (uint8_t) (tx_size & 255); 99 | } 100 | 101 | // ardop header 102 | if (tcp_write(connector->data_socket, ardop_size, sizeof(ardop_size)) 103 | == false) 104 | { 105 | fprintf(stderr, "Error in tcp_write(data_socket)\n"); 106 | connector->shutdown = true; 107 | goto exit_local; 108 | } 109 | 110 | // fprintf(stderr, "ardop_data_worker_thread_tx: After ardop header tcp_write\n"); 111 | bool ret; 112 | if (tx_size > MAX_ARDOP_PACKET) 113 | { 114 | fprintf(stderr,"Sent to ARDOP: %u bytes.\n", MAX_ARDOP_PACKET); 115 | ret = tcp_write(connector->data_socket, &buffer[counter] , MAX_ARDOP_PACKET); 116 | counter += MAX_ARDOP_PACKET; 117 | tx_size -= MAX_ARDOP_PACKET; 118 | } 119 | else{ 120 | fprintf(stderr,"Sent to ARDOP: %u bytes.\n", tx_size); 121 | ret = tcp_write(connector->data_socket, &buffer[counter], tx_size); 122 | counter += tx_size; 123 | tx_size -= tx_size; 124 | } 125 | 126 | // check lost tcp connection 127 | if (ret == false) 128 | { 129 | fprintf(stderr, "Error in tcp_write(data_socket)\n"); 130 | connector->shutdown = true; 131 | goto exit_local; 132 | } 133 | 134 | // buffer management hack 135 | sleep(2); 136 | } 137 | 138 | } 139 | 140 | exit_local: 141 | 142 | fprintf(stderr, "ardop_data_worker_thread_tx exit.\n"); 143 | return EXIT_SUCCESS; 144 | } 145 | 146 | void *ardop_data_worker_thread_rx(void *conn) 147 | { 148 | rhizo_conn *connector = (rhizo_conn *) conn; 149 | uint8_t buffer[MAX_ARDOP_PACKET_SAFE]; 150 | uint32_t buf_size; // our header is 4 bytes long 151 | uint8_t ardop_size[2]; 152 | 153 | while(connector->shutdown == false){ 154 | 155 | while (connector->connected == false){ 156 | if (connector->shutdown == true){ 157 | goto exit_local; 158 | } 159 | sleep(1); 160 | } 161 | 162 | // fprintf(stderr,"Before tcp_read.\n"); 163 | ardop_size[0] = 0; ardop_size[1] = 0; 164 | if (tcp_read(connector->data_socket, ardop_size, 2) == false) 165 | { 166 | fprintf(stderr, "Error in tcp_read(data_socket)\n"); 167 | connector->shutdown = true; 168 | goto exit_local; 169 | } 170 | 171 | // ARDOP TNC data format: length 2 bytes | payload 172 | buf_size = 0; 173 | buf_size = ardop_size[0]; 174 | buf_size <<= 8; 175 | buf_size |= ardop_size[1]; 176 | 177 | fprintf(stderr,"Ardop Receive: %u bytes.\n", buf_size); 178 | 179 | if (tcp_read(connector->data_socket, buffer, buf_size) == false) 180 | { 181 | fprintf(stderr, "Error in tcp_read(data_socket)\n"); 182 | connector->shutdown = true; 183 | goto exit_local; 184 | } 185 | 186 | if (buf_size > 3 && !memcmp("ARQ", buffer, 3)){ 187 | buf_size -= 3; 188 | while (circular_buf_free_size(connector->out_buffer) < buf_size) 189 | { 190 | usleep(10000); // 10ms 191 | } 192 | circular_buf_put_range(connector->out_buffer, buffer + 3, buf_size); 193 | // fprintf(stderr,"Buffer write: %u received.\n", buf_size); 194 | // fwrite(buffer+3, buf_size, 1, stdout); 195 | } 196 | else{ 197 | buffer[buf_size] = 0; 198 | fprintf(stderr, "Ardop non-payload data rx: %s\n", buffer); 199 | } 200 | 201 | } 202 | 203 | exit_local: 204 | fprintf(stderr, "ardop_data_worker_thread_rx exit.\n"); 205 | return EXIT_SUCCESS; 206 | } 207 | 208 | void *ardop_control_worker_thread_rx(void *conn) 209 | { 210 | rhizo_conn *connector = (rhizo_conn *) conn; 211 | uint8_t rcv_byte; 212 | uint8_t buffer[1024]; 213 | int counter = 0; 214 | bool new_cmd = false; 215 | 216 | while(connector->shutdown == false){ 217 | 218 | if (tcp_read(connector->control_socket, &rcv_byte, 1) == false) 219 | { 220 | fprintf(stderr, "Error in tcp_read(control_socket)\n"); 221 | connector->shutdown = true; 222 | goto exit_local; 223 | } 224 | 225 | if (rcv_byte == '\r'){ 226 | buffer[counter] = 0; 227 | counter = 0; 228 | new_cmd = true; 229 | } 230 | else{ 231 | buffer[counter] = rcv_byte; 232 | counter++; 233 | new_cmd = false; 234 | } 235 | 236 | // treat "STATUS CONNECT TO PP2UIT FAILED!" ? 237 | // and reset waiting for connection! 238 | 239 | if (new_cmd){ 240 | if (!memcmp(buffer, "DISCONNECTED", strlen("DISCONNECTED"))){ 241 | fprintf(stderr, "TNC: %s\n", buffer); 242 | connector->clean_buffers = true; 243 | connector->connected = false; 244 | connector->waiting_for_connection = false; 245 | } else 246 | if (!memcmp(buffer, "NEWSTATE DISC", strlen("NEWSTATE DISC"))){ 247 | fprintf(stderr, "TNC: %s\n", buffer); 248 | connector->clean_buffers = true; 249 | connector->connected = false; 250 | connector->waiting_for_connection = false; 251 | } else 252 | if (!memcmp(buffer, "CONNECTED", strlen("CONNECTED"))){ 253 | fprintf(stderr, "TNC: %s\n", buffer); 254 | connector->connected = true; 255 | if (connector->waiting_for_connection == false) 256 | { // we are receiving a connection... call uucico! 257 | bool retval = call_uucico(connector); 258 | if (retval == false) 259 | fprintf(stderr, "Error calling call_uucico()!\n"); 260 | } 261 | connector->waiting_for_connection = false; 262 | } else 263 | if (!memcmp(buffer, "PTT", strlen("PTT"))){ 264 | // supressed output 265 | // fprintf(stderr, "%s -- CMD NOT CONSIDERED!!\n", buffer); 266 | } else 267 | if (!memcmp(buffer, "BUFFER", strlen("BUFFER"))){ 268 | sscanf( (char *) buffer, "BUFFER %d", & connector->buffer_size); 269 | fprintf(stderr, "BUFFER: %d\n", connector->buffer_size); 270 | 271 | } else 272 | if (!memcmp(buffer, "INPUTPEAKS", strlen("INPUTPEAKS"))){ 273 | // suppressed output 274 | } else { 275 | fprintf(stderr, "%s\n", buffer); 276 | } 277 | } 278 | } 279 | 280 | exit_local: 281 | fprintf(stderr, "ardop_control_worker_thread_rx exit.\n"); 282 | return EXIT_SUCCESS; 283 | } 284 | 285 | void *ardop_control_worker_thread_tx(void *conn) 286 | { 287 | rhizo_conn *connector = (rhizo_conn *) conn; 288 | char buffer[1024]; 289 | bool ret = true; 290 | 291 | // initialize 292 | memset(buffer,0,sizeof(buffer)); 293 | sprintf(buffer, "INITIALIZE\r"); 294 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 295 | 296 | // We set a call sign 297 | memset(buffer,0,sizeof(buffer)); 298 | sprintf(buffer, "MYCALL %s\r", connector->call_sign); 299 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 300 | 301 | // we take care of timeout, here we just set the wanted timeout + 5 302 | memset(buffer,0,sizeof(buffer)); 303 | if (connector->timeout < 240) 304 | sprintf(buffer, "ARQTIMEOUT %d\r", connector->timeout); 305 | else 306 | sprintf(buffer, "ARQTIMEOUT %d\r", MAX_TIMEOUT); 307 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 308 | 309 | // memset(buffer,0,sizeof(buffer)); 310 | // sprintf(buffer, "ARQBW 2000MAX\r"); 311 | // ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 312 | 313 | memset(buffer,0,sizeof(buffer)); 314 | strcpy(buffer,"LISTEN True\r"); 315 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 316 | 317 | memset(buffer,0,sizeof(buffer)); 318 | strcpy(buffer,"BUSYDET 0\r"); // disabling busy detection 319 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 320 | 321 | memset(buffer,0,sizeof(buffer)); 322 | if (connector->ofdm_mode == true) 323 | strcpy(buffer,"ENABLEOFDM True\r"); 324 | else 325 | strcpy(buffer,"ENABLEOFDM False\r"); 326 | ret &= tcp_write(connector->control_socket, (uint8_t *) buffer, strlen(buffer)); 327 | 328 | // check lost tcp connection 329 | if (ret == false) 330 | { 331 | fprintf(stderr, "Error in tcp_write(control_socket)\n"); 332 | connector->shutdown = true; 333 | goto exit_local; 334 | } 335 | 336 | 337 | // 1Hz function 338 | uint32_t counter = 0; 339 | while(connector->shutdown == false){ 340 | 341 | ret = true; 342 | // Logic to start a connection 343 | 344 | // lets issue buffer commands.... but not much! 345 | if (connector->connected == true && counter % 2) { 346 | memset(buffer,0,sizeof(buffer)); 347 | sprintf(buffer,"BUFFER\r"); 348 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 349 | } 350 | 351 | if (connector->clean_buffers == true) 352 | { 353 | if (connector->connected == true) 354 | { 355 | if (connector->send_break == true) // we might have some data from uucico 356 | { 357 | fprintf(stderr, "Sending BREAK to ardop.\n"); 358 | memset(buffer,0,sizeof(buffer)); 359 | sprintf(buffer,"BREAK\r"); 360 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 361 | } 362 | 363 | connector->send_break = false; 364 | sleep(1); 365 | memset(buffer,0,sizeof(buffer)); 366 | sprintf(buffer,"DISCONNECT\r"); 367 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 368 | } 369 | 370 | usleep(1200000); // sleep for threads finish their jobs (more than 1s here) 371 | 372 | fprintf(stderr, "Killing uucico.\n"); 373 | system("killall uucico"); 374 | 375 | fprintf(stderr, "Killing uuport.\n"); 376 | system("killall uuport"); 377 | 378 | fprintf(stderr, "Connection closed - Cleaning internal buffers.\n"); 379 | circular_buf_reset(connector->in_buffer); 380 | circular_buf_reset(connector->out_buffer); 381 | 382 | while (connector->connected == true) 383 | usleep(100000); 384 | 385 | usleep(1200000); // sleep for threads finish their jobs (more than 1s here) 386 | 387 | fprintf(stderr, "Connection closed - Cleaning internal buffers 2.\n"); 388 | circular_buf_reset(connector->in_buffer); 389 | circular_buf_reset(connector->out_buffer); 390 | 391 | fprintf(stderr, "Cleaning ardop buffer.\n"); 392 | memset(buffer,0,sizeof(buffer)); 393 | sprintf(buffer,"PURGEBUFFER\r"); 394 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 395 | 396 | connector->clean_buffers = false; 397 | } 398 | 399 | if (connector->connected == false && 400 | circular_buf_size(connector->in_buffer) > 0 && 401 | !connector->waiting_for_connection){ 402 | 403 | memset(buffer,0,sizeof(buffer)); 404 | sprintf(buffer,"ARQCALL %s 4\r", connector->remote_call_sign); 405 | ret &= tcp_write(connector->control_socket, (uint8_t *)buffer, strlen(buffer)); 406 | 407 | fprintf(stderr, "CONNECTING... %s\n", buffer); 408 | connector->waiting_for_connection = true; 409 | } 410 | 411 | 412 | // check lost tcp connection 413 | if (ret == false) 414 | { 415 | fprintf(stderr, "Error in tcp_write(control_socket)\n"); 416 | connector->shutdown = true; 417 | goto exit_local; 418 | } 419 | 420 | sleep(1); // 1Hz function 421 | counter++; 422 | } 423 | 424 | exit_local: 425 | fprintf(stderr, "ardop_control_worker_thread_tx exit.\n"); 426 | return EXIT_SUCCESS; 427 | } 428 | 429 | bool initialize_modem_ardop(rhizo_conn *connector){ 430 | 431 | if (tcp_connect(connector->ip_address, connector->tcp_base_port, &connector->control_socket) 432 | == false) 433 | { 434 | fprintf(stderr, "Connection to TNC's control socket failed.\n"); 435 | connector->shutdown = true; 436 | return false; 437 | } 438 | 439 | if (tcp_connect(connector->ip_address, connector->tcp_base_port+1, &connector->data_socket) 440 | == false) 441 | { 442 | fprintf(stderr, "Connection to TNC's data socket failed.\n"); 443 | connector->shutdown = true; 444 | return false; 445 | } 446 | 447 | sys_led_on(connector->serial_fd, connector->radio_type); 448 | 449 | // we start our control rx thread 450 | pthread_t tid1; 451 | pthread_create(&tid1, NULL, ardop_control_worker_thread_rx, (void *) connector); 452 | 453 | // we start our control tx thread 454 | pthread_t tid2; 455 | pthread_create(&tid2, NULL, ardop_control_worker_thread_tx, (void *) connector); 456 | 457 | // and run the two workers for the data channel 458 | pthread_t tid3; 459 | pthread_create(&tid3, NULL, ardop_data_worker_thread_tx, (void *) connector); 460 | 461 | pthread_t tid4; 462 | pthread_create(&tid4, NULL, ardop_data_worker_thread_rx, (void *) connector); 463 | 464 | pthread_join(tid1, NULL); 465 | pthread_join(tid2, NULL); 466 | pthread_join(tid3, NULL); 467 | pthread_join(tid4, NULL); 468 | 469 | // should we write a tcp_write (CLOSE) to the TNC? 470 | 471 | return true; 472 | } 473 | --------------------------------------------------------------------------------