411 lines
13 KiB
C++
411 lines
13 KiB
C++
/* -*- c++ -*- */
|
|
/*
|
|
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
|
|
*
|
|
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gnuradio/io_signature.h>
|
|
#include "upsat_fsk_frame_encoder_impl.h"
|
|
#include <satnogs/log.h>
|
|
#include <satnogs/utils.h>
|
|
#include <satnogs/crc.h>
|
|
#include <arpa/inet.h>
|
|
|
|
namespace gr {
|
|
namespace satnogs {
|
|
|
|
upsat_fsk_frame_encoder::sptr
|
|
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 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(
|
|
new upsat_fsk_frame_encoder_impl(preamble, sync_word,
|
|
append_crc,
|
|
whitening, manchester,
|
|
msb_first, ax25_format,
|
|
ax25_src_addr, ax25_src_ssid,
|
|
ax25_dest_addr, ax25_dest_ssid,
|
|
settling_samples));
|
|
}
|
|
|
|
/*
|
|
* The private constructor
|
|
*/
|
|
upsat_fsk_frame_encoder_impl::upsat_fsk_frame_encoder_impl(
|
|
const std::vector<uint8_t> &preamble,
|
|
const std::vector<uint8_t> &sync_word,
|
|
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),
|
|
gr::io_signature::make(1, 1, sizeof(float))),
|
|
d_preamble(preamble),
|
|
d_preamble_len(preamble.size()),
|
|
d_sync_word(sync_word),
|
|
d_sync_word_len(sync_word.size()),
|
|
d_append_crc(append_crc),
|
|
d_whitening(whitening),
|
|
d_manchester(manchester),
|
|
d_msb_first(msb_first),
|
|
d_is_ax25(ax25_format),
|
|
d_max_pdu_len(d_preamble_len + d_sync_word_len + sizeof(uint8_t)
|
|
+ UPSAT_MAX_FRAME_LEN + sizeof(uint16_t)),
|
|
d_settling_samples(settling_samples),
|
|
d_encoded(0),
|
|
d_pdu_len(0),
|
|
d_scrambler(0x1001, 0x1FF, 17)
|
|
{
|
|
/* Simplify the logic of the output samples handling */
|
|
set_output_multiple(8);
|
|
message_port_register_in(pmt::mp("pdu"));
|
|
/*
|
|
* Allocate memory for the maximum possible frame WITH all headers and
|
|
* tail fields
|
|
* +-------------+----------+--------+--------------------------+------+
|
|
* | Preamble | SFD | LENGTH | PDU | CRC |
|
|
* +-------------+----------+--------+--------------------------+------+
|
|
* 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 */
|
|
memcpy(d_pdu, d_preamble.data(), d_preamble_len);
|
|
memcpy(d_pdu + d_preamble_len, d_sync_word.data(), d_sync_word_len);
|
|
}
|
|
|
|
/*
|
|
* Our virtual destructor.
|
|
*/
|
|
upsat_fsk_frame_encoder_impl::~upsat_fsk_frame_encoder_impl()
|
|
{
|
|
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
|
|
upsat_fsk_frame_encoder_impl::map_msb_first(float *out, size_t nsamples_out)
|
|
{
|
|
size_t i;
|
|
register uint8_t b;
|
|
for (i = 0; i < nsamples_out; i += 8) {
|
|
b = d_pdu[d_encoded + i / 8];
|
|
out[i] = ((b >> 7) * 2.0f) - 1.0f;
|
|
out[i + 1] = (((b >> 6) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 2] = (((b >> 5) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 3] = (((b >> 4) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 4] = (((b >> 3) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 5] = (((b >> 2) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 6] = (((b >> 1) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 7] = ((b & 0x1) * 2.0f) - 1.0f;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
upsat_fsk_frame_encoder_impl::map_lsb_first(float *out, size_t nsamples_out)
|
|
{
|
|
size_t i;
|
|
register uint8_t b;
|
|
for (i = 0; i < nsamples_out; i += 8) {
|
|
b = d_pdu[d_encoded + i / 8];
|
|
out[i + 7] = ((b >> 7) * 2.0f) - 1.0f;
|
|
out[i + 6] = (((b >> 6) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 5] = (((b >> 5) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 4] = (((b >> 4) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 3] = (((b >> 3) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 2] = (((b >> 2) & 0x1) * 2.0f) - 1.0f;
|
|
out[i + 1] = (((b >> 1) & 0x1) * 2.0f) - 1.0f;
|
|
out[i] = ((b & 0x1) * 2.0f) - 1.0f;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
upsat_fsk_frame_encoder_impl::add_sob(uint64_t item)
|
|
{
|
|
static const pmt::pmt_t sob_key = pmt::string_to_symbol("tx_sob");
|
|
static const pmt::pmt_t value = pmt::PMT_T;
|
|
static const pmt::pmt_t srcid = pmt::string_to_symbol(alias());
|
|
add_item_tag(0, item, sob_key, value, srcid);
|
|
}
|
|
|
|
inline void
|
|
upsat_fsk_frame_encoder_impl::add_eob(uint64_t item)
|
|
{
|
|
static const pmt::pmt_t eob_key = pmt::string_to_symbol("tx_eob");
|
|
static const pmt::pmt_t value = pmt::PMT_T;
|
|
static const pmt::pmt_t srcid = pmt::string_to_symbol(alias());
|
|
add_item_tag(0, item, eob_key, value, srcid);
|
|
}
|
|
|
|
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;
|
|
|
|
/*
|
|
* 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"));
|
|
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;
|
|
}
|
|
/*
|
|
* 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) d_pdu_len;
|
|
|
|
/* Append the variable length PDU */
|
|
memcpy(d_pdu + d_preamble_len + d_sync_word_len + 1,
|
|
pmt::blob_data(pdu), d_pdu_len);
|
|
|
|
/* If it is necessary calculate and append the CRC */
|
|
if (d_append_crc) {
|
|
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));
|
|
d_pdu_len += sizeof(uint16_t);
|
|
}
|
|
|
|
/*
|
|
* Whitening is performed on all bytes except preamble and SYNC fields
|
|
*/
|
|
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);
|
|
}
|
|
|
|
d_pdu_len += d_preamble_len + d_sync_word_len + 1;
|
|
|
|
/*
|
|
* NRZ Encode the whole frame
|
|
*/
|
|
if (d_msb_first) {
|
|
map_msb_first(d_pdu_encoded, d_pdu_len * 8);
|
|
}
|
|
else {
|
|
map_lsb_first(d_pdu_encoded, d_pdu_len * 8);
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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;
|
|
uint8_t len_field;
|
|
|
|
/*
|
|
* 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);
|
|
memset(d_pdu_encoded, 0, d_max_pdu_len * 8 * sizeof(float));
|
|
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, 1, 1);
|
|
|
|
status = ax25_bit_stuffing(d_ax25_pdu, &encoded_len,
|
|
d_ax25_tmp_buf, len, 1, 1);
|
|
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.
|
|
*/
|
|
len_field = (uint8_t)(encoded_len / 8);
|
|
|
|
/* Apply whitening */
|
|
if (d_whitening) {
|
|
d_scrambler.reset();
|
|
d_scrambler.scramble(&len_field, &len_field, 1);
|
|
d_scrambler.scramble_one_bit_per_byte(d_ax25_pdu, d_ax25_pdu,
|
|
encoded_len);
|
|
}
|
|
|
|
d_pdu[d_preamble_len + d_sync_word_len] = len_field;
|
|
|
|
/* If it is necessary calculate and append the CRC */
|
|
if (d_append_crc) {
|
|
LOG_WARN("AX.25 has its own CRC-16 field. Skipping...");
|
|
}
|
|
|
|
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 - extra_bits; 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 */
|
|
|