GNU Radio's SATNOGS Package
ax25.h
Go to the documentation of this file.
1 /* -*- c++ -*- */
2 /*
3  * gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
4  *
5  * Copyright (C) 2016,2018 Libre Space Foundation <http://librespacefoundation.org/>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifndef INCLUDE_SATNOGS_AX25_H_
22 #define INCLUDE_SATNOGS_AX25_H_
23 
24 #include <satnogs/crc.h>
25 #include <satnogs/log.h>
26 #include <limits.h>
27 #include <stdint.h>
28 #include <string>
29 
30 namespace gr {
31 
32 namespace satnogs {
33 const size_t AX25_MIN_ADDR_LEN = 14;
34 const size_t AX25_MAX_ADDR_LEN = (2 * 7 + 8 * 7);
35 const size_t AX25_MIN_CTRL_LEN = 1;
36 const size_t AX25_MAX_CTRL_LEN = 2;
37 const size_t AX25_MAX_FRAME_LEN = 256;
38 const uint8_t AX25_SYNC_FLAG = 0x7E;
39 const uint8_t AX25_CALLSIGN_MAX_LEN = 6;
40 const float AX25_SYNC_FLAG_MAP[8] =
41 { -1, 1, 1, 1, 1, 1, 1, -1 };
42 const uint8_t AX25_SYNC_FLAG_MAP_BIN[8] =
43 { 0, 1, 1, 1, 1, 1, 1, 0 };
44 /**
45  * AX.25 Frame types
46  */
47 typedef enum {
48  AX25_I_FRAME, //!< AX25_I_FRAME Information frame
49  AX25_S_FRAME, //!< AX25_S_FRAME Supervisory frame
50  AX25_U_FRAME, //!< AX25_U_FRAME Unnumbered frame
51  AX25_UI_FRAME /**!< AX25_UI_FRAME Unnumbered information frame */
53 
54 typedef enum {
57 
58 typedef enum {
61 
62 typedef struct {
63  uint8_t address[AX25_MAX_ADDR_LEN];
64  size_t address_len;
65  uint16_t ctrl;
66  size_t ctrl_len;
67  uint8_t pid;
68  uint8_t *info;
69  size_t info_len;
70  ax25_frame_type_t type;
71 } ax25_frame_t;
72 
73 /**
74  * Calculates the FCS of the AX25 frame
75  * @param buffer data buffer
76  * @param len size of the buffer
77  * @return the FCS of the buffer
78  */
79 static inline uint16_t
80 ax25_fcs(uint8_t *buffer, size_t len)
81 {
82  return crc::crc16_ax25(buffer, len);
83 }
84 
85 /**
86  * Creates the header field of the AX.25 frame
87  * @param out the output buffer with enough memory to hold the address field
88  * @param dest_addr the destination callsign address
89  * @param dest_ssid the destination SSID
90  * @param src_addr the callsign of the source
91  * @param src_ssid the source SSID
92  */
93 static inline size_t
94 ax25_create_addr_field(uint8_t *out, std::string dest_addr,
95  uint8_t dest_ssid, std::string src_addr,
96  uint8_t src_ssid)
97 {
98  size_t i;
99 
100  for (i = 0; i < dest_addr.length(); i++) {
101  *out++ = dest_addr[i] << 1;
102  }
103  /*
104  * Perhaps the destination callsign was smaller that the maximum allowed.
105  * In this case the leftover bytes should be filled with space
106  */
107  for (; i < AX25_CALLSIGN_MAX_LEN; i++) {
108  *out++ = ' ' << 1;
109  }
110  /* Apply SSID, reserved and C bit */
111  /* FIXME: C bit is set to 0 implicitly */
112  *out++ = ((0b1111 & dest_ssid) << 1) | 0b01100000;
113 
114  for (i = 0; i < src_addr.length(); i++) {
115  *out++ = src_addr[i] << 1;
116  }
117  for (; i < AX25_CALLSIGN_MAX_LEN; i++) {
118  *out++ = ' ' << 1;
119  }
120  /* Apply SSID, reserved and C bit. As this is the last address field
121  * the trailing bit is set to 1.
122  * /
123  /* FIXME: C bit is set to 0 implicitly */
124  *out++ = ((0b1111 & src_ssid) << 1) | 0b01100001;
125  return AX25_MIN_ADDR_LEN;
126 }
127 
128 /**
129  * Gets the destination SSID of an AX.25 frame
130  * @param in the AX.25 frame buffer
131  * @return the destination SSID
132  */
133 static inline uint8_t
134 ax25_get_dest_ssid(const uint8_t *in)
135 {
136  uint8_t ret;
137  ret = in[AX25_CALLSIGN_MAX_LEN];
138  return (ret >> 1) & 0b1111;
139 }
140 
141 static inline size_t
142 ax25_prepare_frame(uint8_t *out, const uint8_t *info, size_t info_len,
143  ax25_frame_type_t type, uint8_t *addr, size_t addr_len,
144  uint16_t ctrl, size_t ctrl_len, size_t preamble_len,
145  size_t postamble_len)
146 {
147  uint16_t fcs;
148  size_t i;
149  if (info_len > AX25_MAX_FRAME_LEN) {
150  return 0;
151  }
152 
153  memset(out, AX25_SYNC_FLAG, preamble_len);
154  i = preamble_len;
155 
156  /* Insert address and control fields */
157  if (addr_len == AX25_MIN_ADDR_LEN || addr_len == AX25_MAX_ADDR_LEN) {
158  memcpy(out + i, addr, addr_len);
159  i += addr_len;
160  }
161  else {
162  return 0;
163  }
164 
165  if (ctrl_len == AX25_MIN_CTRL_LEN || ctrl_len == AX25_MAX_CTRL_LEN) {
166  memcpy(out + i, &ctrl, ctrl_len);
167  i += ctrl_len;
168  }
169  else {
170  return 0;
171  }
172 
173  /*
174  * Set the PID depending the frame type.
175  * FIXME: For now, only the "No layer 3 is implemented" information is
176  * inserted
177  */
178  if (type == AX25_I_FRAME || type == AX25_UI_FRAME) {
179  out[i++] = 0xF0;
180  }
181  memcpy(out + i, info, info_len);
182  i += info_len;
183 
184  /* Compute the FCS. Ignore the first flag byte */
185  fcs = ax25_fcs(out + preamble_len, i - preamble_len);
186  /* The MS bits are sent first ONLY at the FCS field */
187  out[i++] = fcs & 0xFF;
188  out[i++] = (fcs >> 8) & 0xFF;
189  memset(out + i, AX25_SYNC_FLAG, postamble_len);
190  for (size_t j = preamble_len; j < i; j++) {
191  printf("0x%02x ", out[j]);
192  }
193  printf("\n");
194  return i + postamble_len;
195 }
196 
197 /**
198  * Constructs an AX.25 by performing NRZ encoding and bit stuffing
199  * @param out the output buffer to hold the frame. Note that due to
200  * the NRZ encoding the output would be [-1, 1]. Also the size of the
201  * buffer should be enough, such that the extra stuffed bits are fitting
202  * on the allocated space.
203  *
204  * @param out_len due to bit stuffing the output size can vary. This
205  * pointer will hold the resulting frame size after bit stuffing.
206  *
207  * @param buffer buffer holding the data that should be encoded.
208  * Note that this buffer SHOULD contain the leading and trailing
209  * synchronization flag, all necessary headers and the CRC.
210  *
211  * @param buffer_len the length of the input buffer.
212  *
213  * @param preamble_len the number of consecutive AX.25 flags that will
214  * be placed in the preamble. This preamble will be NOT bit-stuffed.
215  *
216  * @param postamble_len the number of consecutive AX.25 flags that will
217  * be placed in the postamble. This postamble will be NOT bit-stuffed.
218  *
219  * @return the resulting status of the encoding
220  */
221 static inline ax25_encode_status_t
222 ax25_nrz_bit_stuffing(float *out, size_t *out_len, const uint8_t *buffer,
223  size_t buffer_len, size_t preamble_len,
224  size_t postamble_len)
225 {
226  uint8_t bit;
227  uint8_t prev_bit = 0;
228  size_t out_idx = 0;
229  size_t bit_idx;
230  size_t cont_1 = 0;
231  size_t total_cont_1 = 0;
232  size_t i;
233 
234  /* Leading FLAG field does not need bit stuffing */
235  for (i = 0; i < preamble_len; i++) {
236  memcpy(out + out_idx, AX25_SYNC_FLAG_MAP, 8 * sizeof(float));
237  out_idx += 8;
238  }
239 
240  /* Skip the leading and trailing FLAG field */
241  buffer += preamble_len;
242  for (i = 0; i < 8 * (buffer_len - preamble_len - postamble_len); i++) {
243  bit = (buffer[i / 8] >> (i % 8)) & 0x1;
244  out[out_idx++] = bit ? 1.0 : -1.0;
245 
246  /* Check if bit stuffing should be applied */
247  if (bit & prev_bit) {
248  cont_1++;
249  total_cont_1++;
250  if (cont_1 == 4) {
251  out[out_idx++] = -1.0;
252  cont_1 = 0;
253  }
254  }
255  else {
256  cont_1 = total_cont_1 = 0;
257  }
258  prev_bit = bit;
259 
260  /*
261  * If the total number of continuous 1's is 15 the the frame should be
262  * dropped
263  */
264  if (total_cont_1 >= 14) {
265  return AX25_ENC_FAIL;
266  }
267  }
268 
269  /* Trailing FLAG field does not need bit stuffing */
270  for (i = 0; i < postamble_len; i++) {
271  memcpy(out + out_idx, AX25_SYNC_FLAG_MAP, 8 * sizeof(float));
272  out_idx += 8;
273  }
274 
275  *out_len = out_idx;
276  return AX25_ENC_OK;
277 }
278 
279 /**
280  * Constructs an AX.25 by performing bit stuffing.
281  * @param out the output buffer to hold the frame. To keep it simple,
282  * each byte of the buffer holds only one bit. Also the size of the
283  * buffer should be enough, such that the extra stuffed bits are fitting
284  * on the allocated space.
285  *
286  * @param out_len due to bit stuffing the output size can vary. This
287  * pointer will hold the resulting frame size after bit stuffing.
288  *
289  * @param buffer buffer holding the data that should be encoded.
290  * Note that this buffer SHOULD contain the leading and trailing
291  * synchronization flag, all necessary headers and the CRC.
292  *
293  * @param buffer_len the length of the input buffer.
294  *
295  * @param preamble_len the number of consecutive AX.25 flags that will
296  * be placed in the preamble. This preamble will be NOT bit-stuffed.
297  *
298  * @param postamble_len the number of consecutive AX.25 flags that will
299  * be placed in the postamble. This postamble will be NOT bit-stuffed.
300  *
301  * @return the resulting status of the encoding
302  */
303 static inline ax25_encode_status_t
304 ax25_bit_stuffing(uint8_t *out, size_t *out_len, const uint8_t *buffer,
305  const size_t buffer_len, size_t preamble_len,
306  size_t postamble_len)
307 {
308  uint8_t bit;
309  uint8_t shift_reg = 0x0;
310  size_t out_idx = 0;
311  size_t bit_idx;
312  size_t i;
313 
314  /* Leading FLAG field does not need bit stuffing */
315  for (i = 0; i < preamble_len; i++) {
316  memcpy(out + out_idx, AX25_SYNC_FLAG_MAP_BIN, 8 * sizeof(uint8_t));
317  out_idx += 8;
318  }
319 
320  /* Skip the leading and trailing FLAG field */
321  buffer += preamble_len;
322  for (i = 0; i < 8 * (buffer_len - preamble_len - postamble_len); i++) {
323  bit = (buffer[i / 8] >> (i % 8)) & 0x1;
324  shift_reg = (shift_reg << 1) | bit;
325  out[out_idx++] = bit;
326 
327  /* Check if bit stuffing should be applied */
328  if ((shift_reg & 0x1F) == 0x1F) {
329  out[out_idx++] = 0x0;
330  shift_reg <<= 1;
331  }
332  }
333 
334  /* Trailing FLAG field does not need bit stuffing */
335  for (i = 0; i < postamble_len; i++) {
336  memcpy(out + out_idx, AX25_SYNC_FLAG_MAP_BIN, 8 * sizeof(uint8_t));
337  out_idx += 8;
338  }
339 
340  *out_len = out_idx;
341  return AX25_ENC_OK;
342 }
343 
344 static inline ax25_decode_status_t
345 ax25_decode(uint8_t *out, size_t *out_len, const uint8_t *ax25_frame,
346  size_t len)
347 {
348  size_t i;
349  size_t frame_start = UINT_MAX;
350  size_t frame_stop = UINT_MAX;
351  uint8_t res;
352  size_t cont_1 = 0;
353  size_t received_bytes = 0;
354  size_t bit_cnt = 0;
355  uint8_t decoded_byte = 0x0;
356  uint16_t fcs;
357  uint16_t recv_fcs;
358 
359  /* Start searching for the SYNC flag */
360  for (i = 0; i < len - sizeof(AX25_SYNC_FLAG_MAP_BIN); i++) {
361  res = (AX25_SYNC_FLAG_MAP_BIN[0] ^ ax25_frame[i])
362  | (AX25_SYNC_FLAG_MAP_BIN[1] ^ ax25_frame[i + 1])
363  | (AX25_SYNC_FLAG_MAP_BIN[2] ^ ax25_frame[i + 2])
364  | (AX25_SYNC_FLAG_MAP_BIN[3] ^ ax25_frame[i + 3])
365  | (AX25_SYNC_FLAG_MAP_BIN[4] ^ ax25_frame[i + 4])
366  | (AX25_SYNC_FLAG_MAP_BIN[5] ^ ax25_frame[i + 5])
367  | (AX25_SYNC_FLAG_MAP_BIN[6] ^ ax25_frame[i + 6])
368  | (AX25_SYNC_FLAG_MAP_BIN[7] ^ ax25_frame[i + 7]);
369  /* Found it! */
370  if (res == 0) {
371  frame_start = i;
372  break;
373  }
374  }
375 
376  /* We failed to find the SYNC flag */
377  if (frame_start == UINT_MAX) {
378  return AX25_DEC_FAIL;
379  }
380 
381  for (i = frame_start + sizeof(AX25_SYNC_FLAG_MAP_BIN);
382  i < len - sizeof(AX25_SYNC_FLAG_MAP_BIN) + 1; i++) {
383  /* Check if we reached the frame end */
384  res = (AX25_SYNC_FLAG_MAP_BIN[0] ^ ax25_frame[i])
385  | (AX25_SYNC_FLAG_MAP_BIN[1] ^ ax25_frame[i + 1])
386  | (AX25_SYNC_FLAG_MAP_BIN[2] ^ ax25_frame[i + 2])
387  | (AX25_SYNC_FLAG_MAP_BIN[3] ^ ax25_frame[i + 3])
388  | (AX25_SYNC_FLAG_MAP_BIN[4] ^ ax25_frame[i + 4])
389  | (AX25_SYNC_FLAG_MAP_BIN[5] ^ ax25_frame[i + 5])
390  | (AX25_SYNC_FLAG_MAP_BIN[6] ^ ax25_frame[i + 6])
391  | (AX25_SYNC_FLAG_MAP_BIN[7] ^ ax25_frame[i + 7]);
392  /* Found it! */
393  if (res == 0) {
394  frame_stop = i;
395  break;
396  }
397 
398  if (ax25_frame[i]) {
399  cont_1++;
400  decoded_byte |= 1 << bit_cnt;
401  bit_cnt++;
402  }
403  else {
404  /* If 5 consecutive 1's drop the extra zero*/
405  if (cont_1 >= 5) {
406  cont_1 = 0;
407  }
408  else {
409  bit_cnt++;
410  cont_1 = 0;
411  }
412  }
413 
414  /* Fill the fully constructed byte */
415  if (bit_cnt == 8) {
416  out[received_bytes++] = decoded_byte;
417  bit_cnt = 0;
418  decoded_byte = 0x0;
419  }
420  }
421 
422  if (frame_stop == UINT_MAX || received_bytes < AX25_MIN_ADDR_LEN) {
423  return AX25_DEC_FAIL;
424  }
425 
426  /* Now check the CRC */
427  fcs = ax25_fcs(out, received_bytes - sizeof(uint16_t));
428  recv_fcs = (((uint16_t) out[received_bytes - 2]) << 8)
429  | out[received_bytes - 1];
430 
431  if (fcs != recv_fcs) {
432  LOG_WARN("AX.25 CRC-16 failed");
433  return AX25_DEC_FAIL;
434  }
435 
436  *out_len = received_bytes - sizeof(uint16_t);
437  return AX25_DEC_OK;
438 
439 }
440 
441 } // namespace satnogs
442 
443 } // namespace gr
444 
445 #endif /* INCLUDE_SATNOGS_AX25_H_ */
static uint8_t ax25_get_dest_ssid(const uint8_t *in)
Definition: ax25.h:134
int i
Definition: decode_rs.h:71
static ax25_decode_status_t ax25_decode(uint8_t *out, size_t *out_len, const uint8_t *ax25_frame, size_t len)
Definition: ax25.h:345
const size_t AX25_MAX_FRAME_LEN
Definition: ax25.h:37
int j
Definition: decode_rs.h:71
uint8_t pid
Definition: ax25.h:67
ax25_decode_status_t
Definition: ax25.h:58
ax25_frame_type_t
Definition: ax25.h:47
const uint8_t AX25_CALLSIGN_MAX_LEN
Definition: ax25.h:39
memset(parity, 0, NROOTS *sizeof(data_t))
static size_t ax25_prepare_frame(uint8_t *out, const uint8_t *info, size_t info_len, ax25_frame_type_t type, uint8_t *addr, size_t addr_len, uint16_t ctrl, size_t ctrl_len, size_t preamble_len, size_t postamble_len)
Definition: ax25.h:142
const uint8_t AX25_SYNC_FLAG
Definition: ax25.h:38
uint8_t * info
Definition: ax25.h:68
AX25_U_FRAME Unnumbered frame.
Definition: ax25.h:50
Implements a bit shift register.
Definition: shift_reg.h:35
ax25_encode_status_t
Definition: ax25.h:54
static size_t ax25_create_addr_field(uint8_t *out, std::string dest_addr, uint8_t dest_ssid, std::string src_addr, uint8_t src_ssid)
Definition: ax25.h:94
const float AX25_SYNC_FLAG_MAP[8]
Definition: ax25.h:40
size_t address_len
Definition: ax25.h:64
const size_t AX25_MIN_CTRL_LEN
Definition: ax25.h:35
const size_t AX25_MAX_ADDR_LEN
Definition: ax25.h:34
AX25_I_FRAME Information frame.
Definition: ax25.h:48
ax25_frame_type_t type
Definition: ax25.h:70
Definition: ax25.h:55
Definition: amsat_duv_decoder.h:29
AX25_S_FRAME Supervisory frame.
Definition: ax25.h:49
const uint8_t AX25_SYNC_FLAG_MAP_BIN[8]
Definition: ax25.h:42
Definition: ax25.h:59
const size_t AX25_MIN_ADDR_LEN
Definition: ax25.h:33
size_t ctrl_len
Definition: ax25.h:66
Definition: ax25.h:59
static uint16_t crc16_ax25(const uint8_t *data, size_t len)
uint16_t ctrl
Definition: ax25.h:65
const size_t AX25_MAX_CTRL_LEN
Definition: ax25.h:36
#define LOG_WARN(M,...)
Definition: log.h:43
static ax25_encode_status_t ax25_nrz_bit_stuffing(float *out, size_t *out_len, const uint8_t *buffer, size_t buffer_len, size_t preamble_len, size_t postamble_len)
Definition: ax25.h:222
static uint16_t ax25_fcs(uint8_t *buffer, size_t len)
Definition: ax25.h:80
size_t info_len
Definition: ax25.h:69
Definition: ax25.h:51
Definition: ax25.h:62
Definition: ax25.h:55
static ax25_encode_status_t ax25_bit_stuffing(uint8_t *out, size_t *out_len, const uint8_t *buffer, const size_t buffer_len, size_t preamble_len, size_t postamble_len)
Definition: ax25.h:304