AX.25 support at the UPSAT FSK encoder/decoder

This commit is contained in:
Manolis Surligas 2016-04-29 21:43:09 +03:00
parent 652c257b01
commit e79e98fbd8
7 changed files with 386 additions and 36 deletions

View File

@ -4,7 +4,7 @@
<key>satnogs_upsat_fsk_frame_encoder</key>
<category>satnogs</category>
<import>import satnogs</import>
<make>satnogs.upsat_fsk_frame_encoder($preamble, $sync_word, $append_crc, $whitening, $manchester, $msb_first, $ax_25, $settling_samples)</make>
<make>satnogs.upsat_fsk_frame_encoder($preamble, $sync_word, $append_crc, $whitening, $manchester, $msb_first, $ax_25, $src_addr, $src_ssid, $dest_addr, $dest_ssid, $settling_samples)</make>
<param>
<name>Frame Preamble</name>
@ -88,6 +88,30 @@
</option>
</param>
<param>
<name>Destination Callsign</name>
<key>dest_addr</key>
<type>string</type>
</param>
<param>
<name>Destination SSID</name>
<key>dest_ssid</key>
<type>int</type>
</param>
<param>
<name>Source Callsign</name>
<key>src_addr</key>
<type>string</type>
</param>
<param>
<name>Source SSID</name>
<key>src_ssid</key>
<type>int</type>
</param>
<param>
<name>Number of zero settling samples</name>
<key>settling_samples</key>

View File

@ -22,6 +22,8 @@
#define INCLUDE_SATNOGS_AX25_H_
#include <satnogs/utils.h>
#include <satnogs/log.h>
#include <limits.h>
namespace gr
{
@ -44,7 +46,8 @@ namespace gr
{
AX25_I_FRAME, //!< AX25_I_FRAME Information frame
AX25_S_FRAME, //!< AX25_S_FRAME Supervisory frame
AX25_U_FRAME //!< AX25_U_FRAME Unnumbered frame
AX25_U_FRAME, //!< AX25_U_FRAME Unnumbered frame
AX25_UI_FRAME /**!< AX25_UI_FRAME Unnumbered information frame */
} ax25_frame_type_t;
typedef enum
@ -53,6 +56,12 @@ namespace gr
AX25_ENC_OK
} ax25_encode_status_t;
typedef enum
{
AX25_DEC_FAIL,
AX25_DEC_OK
} ax25_decode_status_t;
typedef struct
{
uint8_t address[AX25_MAX_ADDR_LEN];
@ -60,7 +69,8 @@ namespace gr
uint16_t ctrl;
size_t ctrl_len;
uint8_t pid;
uint8_t info[AX25_MAX_FRAME_LEN];
uint8_t *info;
size_t info_len;
ax25_frame_type_t type;
} ax25_frame_t;
@ -75,7 +85,7 @@ namespace gr
{
uint16_t fcs = 0xFFFF;
while (len--) {
fcs = (fcs >> 8) ^ crc16_ccitt_table_reverse[(fcs ^ *buffer++) & 0XFF];
fcs = (fcs >> 8) ^ crc16_ccitt_table_reverse[(fcs ^ *buffer++) & 0xFF];
}
return fcs ^ 0xFFFF;
}
@ -146,7 +156,7 @@ namespace gr
if( ctrl_len == AX25_MIN_CTRL_LEN || ctrl_len == AX25_MAX_CTRL_LEN){
memcpy(out + i, &ctrl, ctrl_len);
i += addr_len;
i += ctrl_len;
}
else{
return 0;
@ -157,7 +167,7 @@ namespace gr
* FIXME: For now, only the "No layer 3 is implemented" information is
* inserted
*/
if(type == AX25_I_FRAME){
if (type == AX25_I_FRAME || type == AX25_UI_FRAME) {
out[i++] = 0xF0;
}
memcpy(out + i, info, info_len);
@ -315,6 +325,104 @@ namespace gr
return AX25_ENC_OK;
}
static inline ax25_decode_status_t
ax25_decode (uint8_t *out, size_t *out_len,
const uint8_t *ax25_frame, size_t len)
{
size_t i;
size_t frame_start = UINT_MAX;
size_t frame_stop = UINT_MAX;
uint8_t res;
size_t cont_1 = 0;
size_t received_bytes = 0;
size_t bit_cnt = 0;
uint8_t decoded_byte = 0x0;
uint16_t fcs;
uint16_t recv_fcs;
/* Start searching for the SYNC flag */
for(i = 0; i < len - sizeof(AX25_SYNC_FLAG_MAP_BIN); i++) {
res = (AX25_SYNC_FLAG_MAP_BIN[0] ^ ax25_frame[i]) |
(AX25_SYNC_FLAG_MAP_BIN[1] ^ ax25_frame[i + 1]) |
(AX25_SYNC_FLAG_MAP_BIN[2] ^ ax25_frame[i + 2]) |
(AX25_SYNC_FLAG_MAP_BIN[3] ^ ax25_frame[i + 3]) |
(AX25_SYNC_FLAG_MAP_BIN[4] ^ ax25_frame[i + 4]) |
(AX25_SYNC_FLAG_MAP_BIN[5] ^ ax25_frame[i + 5]) |
(AX25_SYNC_FLAG_MAP_BIN[6] ^ ax25_frame[i + 6]) |
(AX25_SYNC_FLAG_MAP_BIN[7] ^ ax25_frame[i + 7]);
/* Found it! */
if(res == 0){
frame_start = i;
break;
}
}
/* We failed to find the SYNC flag */
if(frame_start == UINT_MAX){
return AX25_DEC_FAIL;
}
for(i = frame_start + sizeof(AX25_SYNC_FLAG_MAP_BIN);
i < len - sizeof(AX25_SYNC_FLAG_MAP_BIN); i++) {
/* Check if we reached the frame end */
res = (AX25_SYNC_FLAG_MAP_BIN[0] ^ ax25_frame[i]) |
(AX25_SYNC_FLAG_MAP_BIN[1] ^ ax25_frame[i + 1]) |
(AX25_SYNC_FLAG_MAP_BIN[2] ^ ax25_frame[i + 2]) |
(AX25_SYNC_FLAG_MAP_BIN[3] ^ ax25_frame[i + 3]) |
(AX25_SYNC_FLAG_MAP_BIN[4] ^ ax25_frame[i + 4]) |
(AX25_SYNC_FLAG_MAP_BIN[5] ^ ax25_frame[i + 5]) |
(AX25_SYNC_FLAG_MAP_BIN[6] ^ ax25_frame[i + 6]) |
(AX25_SYNC_FLAG_MAP_BIN[7] ^ ax25_frame[i + 7]);
/* Found it! */
if(res == 0){
frame_stop = i;
break;
}
if (ax25_frame[i]) {
cont_1++;
decoded_byte |= 1 << bit_cnt;
bit_cnt++;
}
else {
/* If 5 consecutive 1's drop the extra zero*/
if (cont_1 >= 5) {
cont_1 = 0;
}
else{
bit_cnt++;
cont_1 = 0;
}
}
/* Fill the fully constructed byte */
if(bit_cnt == 8){
out[received_bytes++] = decoded_byte;
bit_cnt = 0;
decoded_byte = 0x0;
}
}
if(frame_stop == UINT_MAX || received_bytes < AX25_MIN_ADDR_LEN){
return AX25_DEC_FAIL;
}
/* Now check the CRC */
fcs = ax25_fcs (out, received_bytes - sizeof(uint16_t));
recv_fcs = (((uint16_t) out[received_bytes - 2]) << 8)
| out[received_bytes - 1];
if(fcs != recv_fcs) {
LOG_WARN("AX.25 CRC-16 failed");
return AX25_DEC_FAIL;
}
*out_len = received_bytes - sizeof(uint16_t);
return AX25_DEC_OK;
}
} // namespace satnogs
} // namespace gr

View File

@ -63,7 +63,10 @@ namespace gr
* the Manchester algorithm of the CC1120 chip. False otherwise.
*
* @param msb_first if set to true, the the treansmission starts from the
* MSB of each byte.
* MSB of each byte. In case the AX.25 encapuslation is selected, this
* parameter is NOT taken into consideration for the AX.25 part,
* as the AX.25 dictates that the LS bit should be sent first.
*
* @param ax25_format if set to true the frame payload will be encoded
* using AX.25 encapsulation.
*
@ -78,11 +81,16 @@ namespace gr
static sptr
make (const std::vector<uint8_t>& preamble,
const std::vector<uint8_t>& sync_word,
bool append_crc = true,
bool whitening = false, bool manchester = false,
bool msb_first = true,
bool ax25_format = false,
size_t settling_samples = 64);
bool append_crc,
bool whitening,
bool manchester,
bool msb_first,
bool ax25_format,
const std::string& ax25_dest_addr,
uint8_t ax25_dest_ssid,
const std::string& ax25_src_addr,
uint8_t ax25_src_ssid,
size_t settling_samples);
};
} // namespace satnogs

View File

@ -25,6 +25,7 @@
#include <gnuradio/io_signature.h>
#include "upsat_fsk_frame_acquisition_impl.h"
#include <satnogs/log.h>
#include <satnogs/ax25.h>
#include <satnogs/utils.h>
#include <arpa/inet.h>
@ -97,6 +98,8 @@ namespace gr
}
d_pdu = new uint8_t[UPSAT_MAX_FRAME_LEN];
d_ax25_tmp_buf = new uint8_t[2 * UPSAT_MAX_FRAME_LEN * 8];
d_ax25_buf = new uint8_t[2 * UPSAT_MAX_FRAME_LEN];
}
/*
@ -105,6 +108,8 @@ namespace gr
upsat_fsk_frame_acquisition_impl::~upsat_fsk_frame_acquisition_impl ()
{
delete[] d_pdu;
delete[] d_ax25_tmp_buf;
delete[] d_ax25_buf;
}
inline void
@ -173,6 +178,23 @@ namespace gr
d_decoded_bits = 0;
}
inline void
upsat_fsk_frame_acquisition_impl::unpack_ax25_bytes (size_t len_bytes)
{
size_t i;
uint8_t *in = d_pdu + 1;
for(i = 0; i < len_bytes; i++){
d_ax25_tmp_buf[8*i] = (in[i] >> 7) & 0x1;
d_ax25_tmp_buf[8*i + 1] = (in[i] >> 6) & 0x1;
d_ax25_tmp_buf[8*i + 2] = (in[i] >> 5) & 0x1;
d_ax25_tmp_buf[8*i + 3] = (in[i] >> 4) & 0x1;
d_ax25_tmp_buf[8*i + 4] = (in[i] >> 3) & 0x1;
d_ax25_tmp_buf[8*i + 5] = (in[i] >> 2) & 0x1;
d_ax25_tmp_buf[8*i + 6] = (in[i] >> 1) & 0x1;
d_ax25_tmp_buf[8*i + 7] = in[i] & 0x1;
}
}
int
upsat_fsk_frame_acquisition_impl::work (
int noutput_items, gr_vector_const_void_star &input_items,
@ -181,6 +203,8 @@ namespace gr
int i;
uint16_t crc_received;
uint16_t crc_calc;
size_t ax25_frame_len = 0;
ax25_decode_status_t status;
const float *in = (const float *) input_items[0];
for (i = 0; i < noutput_items; i++) {
slice_and_shift (in[i]);
@ -273,9 +297,30 @@ namespace gr
d_decoded_bytes++;
if (d_decoded_bytes == d_frame_len) {
if(d_is_ax25) {
unpack_ax25_bytes(d_frame_len - 1);
status = ax25_decode(d_ax25_buf, &ax25_frame_len,
d_ax25_tmp_buf, (d_frame_len - 1)*8);
if(status == AX25_DEC_OK){
message_port_pub (pmt::mp ("pdu"),
pmt::make_blob(d_ax25_buf, ax25_frame_len));
}
else{
LOG_WARN("AX.25 decoding failed.");
}
/*
* We are done here. Whitening and FSK CRC is not supported
* when transmitting/receiving AX.25 frames
*/
reset_state ();
break;
}
if(d_whitening){
d_descrambler.descramble(d_pdu+1, d_pdu+1, d_frame_len - 1);
}
if(!d_check_crc){
message_port_pub (
pmt::mp ("pdu"),

View File

@ -63,6 +63,8 @@ namespace gr
size_t d_frame_len;
whitening d_descrambler;
uint8_t *d_pdu;
uint8_t *d_ax25_tmp_buf;
uint8_t *d_ax25_buf;
inline void
slice_and_shift (float in);
@ -79,6 +81,8 @@ namespace gr
have_frame_len ();
inline void
have_payload ();
inline void
unpack_ax25_bytes(size_t len_bytes);
public:
upsat_fsk_frame_acquisition_impl (const std::vector<uint8_t> &preamble,

View File

@ -37,9 +37,12 @@ namespace gr
upsat_fsk_frame_encoder::make (const std::vector<uint8_t>& preamble,
const std::vector<uint8_t>& sync_word,
bool append_crc, bool whitening,
bool manchester,
bool msb_first,
bool manchester, bool msb_first,
bool ax25_format,
const std::string& ax25_dest_addr,
uint8_t ax25_dest_ssid,
const std::string& ax25_src_addr,
uint8_t ax25_src_ssid,
size_t settling_samples)
{
return gnuradio::get_initial_sptr (
@ -47,6 +50,8 @@ namespace gr
append_crc,
whitening, manchester,
msb_first, ax25_format,
ax25_src_addr, ax25_src_ssid,
ax25_dest_addr, ax25_dest_ssid,
settling_samples));
}
@ -59,6 +64,10 @@ namespace gr
bool append_crc, bool whitening,
bool manchester, bool msb_first,
bool ax25_format,
const std::string& ax25_dest_addr,
uint8_t ax25_dest_ssid,
const std::string& ax25_src_addr,
uint8_t ax25_src_ssid,
size_t settling_samples) :
gr::sync_block ("upsat_fsk_frame_encoder",
gr::io_signature::make (0, 0, 0),
@ -91,6 +100,18 @@ namespace gr
* User def. User def. 1B 1-255 B 2 B
*/
d_pdu = new uint8_t[d_max_pdu_len];
if(d_is_ax25){
d_ax25_addr = new uint8_t[AX25_MAX_ADDR_LEN];
/* One useful bit per byte for the AX.25 buffer */
d_ax25_pdu = new uint8_t[2 * d_max_pdu_len * 8];
d_ax25_tmp_buf = new uint8_t[d_max_pdu_len * 2];
d_ax25_addr_len = ax25_create_addr_field (d_ax25_addr, ax25_dest_addr,
ax25_dest_ssid, ax25_src_addr,
ax25_src_ssid);
}
d_pdu_encoded = new float[d_max_pdu_len*8 + d_settling_samples];
/* Copy the preamble at the start of the pdu */
@ -105,6 +126,11 @@ namespace gr
{
delete [] d_pdu;
delete [] d_pdu_encoded;
if(d_is_ax25) {
delete [] d_ax25_addr;
delete [] d_ax25_pdu;
delete [] d_ax25_tmp_buf;
}
}
inline void
@ -163,25 +189,23 @@ namespace gr
add_item_tag (0, item, eob_key, value, srcid);
}
int
upsat_fsk_frame_encoder_impl::work (int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
inline int
upsat_fsk_frame_encoder_impl::raw_frame_handling (float* out,
int noutput_items)
{
uint16_t crc;
size_t min_available;
pmt::pmt_t pdu;
float *out = (float *) output_items[0];
/*
* If the whole previous frame has been successfully sent, block waiting
* for a new one
*/
if(d_encoded == 0){
pdu = delete_head_blocking(pmt::mp("pdu"));
if (d_encoded == 0) {
pdu = delete_head_blocking (pmt::mp ("pdu"));
d_pdu_len = pmt::blob_length (pdu);
if(d_pdu_len > UPSAT_MAX_FRAME_LEN){
if (d_pdu_len > UPSAT_MAX_FRAME_LEN) {
LOG_ERROR("PDU is greater than the supported. Dropping the PDU");
return 0;
}
@ -198,21 +222,21 @@ namespace gr
pmt::blob_data (pdu), d_pdu_len);
/* If it is necessary calculate and append the CRC */
if(d_append_crc) {
crc = crc16_ccitt(d_pdu + d_preamble_len + d_sync_word_len,
d_pdu_len + 1);
if (d_append_crc) {
crc = crc16_ccitt (d_pdu + d_preamble_len + d_sync_word_len,
d_pdu_len + 1);
/* CRC must be transmitted MSB first */
crc = htons(crc);
memcpy(d_pdu + d_preamble_len + d_sync_word_len + 1 + d_pdu_len,
&crc, sizeof(uint16_t));
crc = htons (crc);
memcpy (d_pdu + d_preamble_len + d_sync_word_len + 1 + d_pdu_len,
&crc, sizeof(uint16_t));
d_pdu_len += sizeof(uint16_t);
}
/*
* Whitening is performed on all bytes except preamble and SYNC fields
*/
if(d_whitening){
d_scrambler.reset();
if (d_whitening) {
d_scrambler.reset ();
d_scrambler.scramble (d_pdu + d_preamble_len + d_sync_word_len,
d_pdu + d_preamble_len + d_sync_word_len,
d_pdu_len + 1);
@ -235,28 +259,151 @@ namespace gr
d_settling_samples * sizeof(float));
/* The new frame now has a bigger size of course*/
d_pdu_len += d_settling_samples/8;
d_pdu_len += d_settling_samples / 8;
/* Start of burst */
add_sob(nitems_written(0));
add_sob (nitems_written (0));
}
noutput_items = std::max (0, noutput_items);
min_available = std::min ((size_t) noutput_items,
(d_pdu_len - d_encoded) * 8);
memcpy(out, d_pdu_encoded + d_encoded*8, min_available * sizeof(float));
d_encoded += min_available/8;
memcpy (out, d_pdu_encoded + d_encoded * 8,
min_available * sizeof(float));
d_encoded += min_available / 8;
/* End of the frame reached */
if(d_encoded == d_pdu_len){
add_eob(nitems_written(0) + min_available - 1);
if (d_encoded == d_pdu_len) {
add_eob (nitems_written (0) + min_available - 1);
d_encoded = 0;
}
return min_available;
}
inline int
upsat_fsk_frame_encoder_impl::ax25_frame_handling (float* out,
int noutput_items)
{
uint16_t crc;
size_t min_available;
pmt::pmt_t pdu;
size_t len;
size_t encoded_len;
ax25_encode_status_t status;
size_t extra_bits;
size_t i;
/*
* If the whole previous frame has been successfully sent, block waiting
* for a new one
*/
if (d_encoded == 0) {
/* Reset the buffer */
memset(d_ax25_pdu, 0, 2 * d_max_pdu_len * 8);
pdu = delete_head_blocking (pmt::mp ("pdu"));
d_pdu_len = pmt::blob_length (pdu);
if (d_pdu_len > UPSAT_MAX_FRAME_LEN) {
LOG_ERROR("PDU is greater than the supported. Dropping the PDU");
return 0;
}
len = ax25_prepare_frame(d_ax25_tmp_buf, (uint8_t *) pmt::blob_data (pdu),
d_pdu_len, AX25_UI_FRAME, d_ax25_addr,
d_ax25_addr_len, 0x03, 1);
status = ax25_bit_stuffing(d_ax25_pdu, &encoded_len,
d_ax25_tmp_buf, len);
if(status != AX25_ENC_OK) {
LOG_WARN("Failed to properly encode into AX.25 frame");
return 0;
}
/* Due to bit stuffing the resulting bit number may not be
* a multiple of 8. Thus we append proper number of zeros
* after the SYNC flag. So neither the AX.25 or the FSK
* framing are affected
*/
extra_bits = 8 - (encoded_len % 8);
encoded_len += extra_bits;
/*
* Set the frame length at the corresponding field.
* NOTE: The frame length is calculated taking consideration only
* the address field (if exists) and the payload. Length and CRC fields
* are NOT included.
*/
d_pdu[d_preamble_len + d_sync_word_len] = (uint8_t) encoded_len;
/* If it is necessary calculate and append the CRC */
if (d_append_crc) {
LOG_WARN("AX.25 has its own CRC-16 field. Skipping...");
}
/*
* Whitening can not be applied in the AX.25 because it will alter
* the SYNC flag of the stack
*/
if (d_whitening) {
LOG_WARN("AX.25 and whitening are not compatible."
" No whitening will be performed");
}
d_pdu_len = d_preamble_len + d_sync_word_len + 1 + encoded_len/8;
/* NRZ encoding the FSK preamble with the standard method */
map_msb_first (d_pdu_encoded, (d_preamble_len + d_sync_word_len + 1) * 8);
/* NRZ encode the AX.25 part of the FSK frame */
for(i = 0; i < encoded_len; i++){
d_pdu_encoded[i + (d_preamble_len + d_sync_word_len + 1)*8]
= (d_ax25_pdu[i] * 2.0f) - 1.0f;
}
/* Reset the settling trailing samples */
memset (d_pdu_encoded + d_pdu_len * 8, 0,
d_settling_samples * sizeof(float));
/* The new frame now has a bigger size of course*/
d_pdu_len += d_settling_samples / 8;
/* Start of burst */
add_sob (nitems_written (0));
}
noutput_items = std::max (0, noutput_items);
min_available = std::min ((size_t) noutput_items,
(d_pdu_len - d_encoded) * 8);
memcpy (out, d_pdu_encoded + d_encoded * 8,
min_available * sizeof(float));
d_encoded += min_available / 8;
/* End of the frame reached */
if (d_encoded == d_pdu_len) {
add_eob (nitems_written (0) + min_available - 1);
d_encoded = 0;
}
return min_available;
}
int
upsat_fsk_frame_encoder_impl::work (int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
float *out = (float *) output_items[0];
if(d_is_ax25) {
return ax25_frame_handling(out, noutput_items);
}
else{
return raw_frame_handling(out, noutput_items);
}
}
} /* namespace satnogs */
} /* namespace gr */

View File

@ -23,6 +23,7 @@
#include <satnogs/upsat_fsk_frame_encoder.h>
#include <satnogs/whitening.h>
#include <satnogs/ax25.h>
namespace gr
{
@ -47,6 +48,10 @@ namespace gr
size_t d_pdu_len;
whitening d_scrambler;
uint8_t *d_pdu;
uint8_t *d_ax25_pdu;
uint8_t *d_ax25_tmp_buf;
uint8_t *d_ax25_addr;
size_t d_ax25_addr_len;
float *d_pdu_encoded;
inline void
@ -58,6 +63,11 @@ namespace gr
inline void
add_eob (uint64_t item);
inline int
raw_frame_handling(float *out, int noutput_items);
inline int
ax25_frame_handling(float *out, int noutput_items);
public:
upsat_fsk_frame_encoder_impl (const std::vector<uint8_t>& preamble,
const std::vector<uint8_t>& sync_word,
@ -65,6 +75,10 @@ namespace gr
bool manchester,
bool msb_first,
bool ax25_format,
const std::string& ax25_dest_addr,
uint8_t ax25_dest_ssid,
const std::string& ax25_src_addr,
uint8_t ax25_src_ssid,
size_t settling_samples);
~upsat_fsk_frame_encoder_impl ();