";
32 | $class="body";
33 | } else {
34 | echo "
";
35 | $class="bodywt";
36 | }
37 | if ($file_ext=="gpg") {
38 | echo $value_st;
39 | 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 |
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 |
180 |
188 |
189 |
190 |
191 |
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 |
--------------------------------------------------------------------------------