Formatted code (#1007)

* Updated style

* Updated files

* fixed new line

* Updated spacing

* File fix WIP

* Updated to clang 13

* updated comment style

* Removed old comment code
This commit is contained in:
jLynx
2023-05-19 08:16:05 +12:00
committed by GitHub
parent 7aca7ce74d
commit 033c4e9a5b
599 changed files with 70746 additions and 66896 deletions

View File

@@ -22,31 +22,31 @@
#include "audio_compressor.hpp"
float GainComputer::operator()(const float x) const {
const auto abs_x = std::abs(x);
const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x);
const auto overshoot_db = db - threshold_db;
if( knee_width_db > 0.0f ) {
const auto w2 = knee_width_db / 2.0f;
const auto a = w2 / (knee_width_db * knee_width_db);
const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2);
const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
} else {
const auto rectified_overshoot = std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
}
const auto abs_x = std::abs(x);
const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x);
const auto overshoot_db = db - threshold_db;
if (knee_width_db > 0.0f) {
const auto w2 = knee_width_db / 2.0f;
const auto a = w2 / (knee_width_db * knee_width_db);
const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2);
const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
} else {
const auto rectified_overshoot = std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
}
}
void FeedForwardCompressor::execute_in_place(const buffer_f32_t& buffer) {
constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f);
for(size_t i=0; i<buffer.count; i++) {
buffer.p[i] = execute_once(buffer.p[i]) * makeup_gain;
}
constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f);
for (size_t i = 0; i < buffer.count; i++) {
buffer.p[i] = execute_once(buffer.p[i]) * makeup_gain;
}
}
float FeedForwardCompressor::execute_once(const float x) {
const auto gain_db = gain_computer(x);
const auto peak_db = -peak_detector(-gain_db);
const auto gain = fast_pow2(peak_db * (3.321928094887362f / 20.0f));
return x * gain;
const auto gain_db = gain_computer(x);
const auto peak_db = -peak_detector(-gain_db);
const auto gain = fast_pow2(peak_db * (3.321928094887362f / 20.0f));
return x * gain;
}

View File

@@ -34,69 +34,67 @@
*/
class GainComputer {
public:
constexpr GainComputer(
float ratio,
float threshold
) : ratio { ratio },
slope { 1.0f / ratio - 1.0f },
threshold_db { threshold }
{
}
public:
constexpr GainComputer(
float ratio,
float threshold)
: ratio{ratio},
slope{1.0f / ratio - 1.0f},
threshold_db{threshold} {
}
float operator()(const float x) const;
float operator()(const float x) const;
private:
const float ratio;
const float slope;
const float threshold_db;
private:
const float ratio;
const float slope;
const float threshold_db;
static constexpr float knee_width_db = 0.0f;
static constexpr float knee_width_db = 0.0f;
static constexpr float db_floor = -120.0f;
static constexpr float lin_floor = std::pow(10.0f, db_floor / 20.0f);
static constexpr float log2_db_k = 20.0f * std::log10(2.0f);
static constexpr float db_floor = -120.0f;
static constexpr float lin_floor = std::pow(10.0f, db_floor / 20.0f);
static constexpr float log2_db_k = 20.0f * std::log10(2.0f);
};
class PeakDetectorBranchingSmooth {
public:
constexpr PeakDetectorBranchingSmooth(
float att_a,
float rel_a
) : att_a { att_a },
rel_a { rel_a }
{
}
public:
constexpr PeakDetectorBranchingSmooth(
float att_a,
float rel_a)
: att_a{att_a},
rel_a{rel_a} {
}
float operator()(const float db) {
const auto a = (db > state) ? att_a : rel_a;
state = db + a * (state - db);
return state;
}
float operator()(const float db) {
const auto a = (db > state) ? att_a : rel_a;
state = db + a * (state - db);
return state;
}
private:
float state { 0.0f };
const float att_a;
const float rel_a;
private:
float state{0.0f};
const float att_a;
const float rel_a;
};
class FeedForwardCompressor {
public:
void execute_in_place(const buffer_f32_t& buffer);
public:
void execute_in_place(const buffer_f32_t& buffer);
private:
static constexpr float fs = 12000.0f;
static constexpr float ratio = 10.0f;
static constexpr float threshold = -30.0f;
private:
static constexpr float fs = 12000.0f;
static constexpr float ratio = 10.0f;
static constexpr float threshold = -30.0f;
GainComputer gain_computer { ratio, threshold };
PeakDetectorBranchingSmooth peak_detector { tau_alpha(0.010f, fs), tau_alpha(0.300f, fs) };
GainComputer gain_computer{ratio, threshold};
PeakDetectorBranchingSmooth peak_detector{tau_alpha(0.010f, fs), tau_alpha(0.300f, fs)};
float execute_once(const float x);
float execute_once(const float x);
static constexpr float tau_alpha(const float tau, const float fs) {
return std::exp(-1.0f / (tau * fs));
}
static constexpr float tau_alpha(const float tau, const float fs) {
return std::exp(-1.0f / (tau * fs));
}
};
#endif/*__AUDIO_COMPRESSOR_H__*/
#endif /*__AUDIO_COMPRESSOR_H__*/

View File

@@ -40,84 +40,84 @@ constexpr uint32_t gpdma_ahb_master_peripheral = 1;
constexpr uint32_t gpdma_ahb_master_memory = 0;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */
constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */
constexpr uint32_t gpdma_rx_src_peripheral = gpdma_rx_peripheral;
constexpr uint32_t gpdma_rx_dest_peripheral = gpdma_rx_peripheral;
constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */
constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */
constexpr uint32_t gpdma_tx_src_peripheral = gpdma_tx_peripheral;
constexpr uint32_t gpdma_tx_dest_peripheral = gpdma_tx_peripheral;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control_tx(const size_t transfer_bytes) {
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_memory,
.d = gpdma_ahb_master_peripheral,
.si = 1,
.di = 0,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_memory,
.d = gpdma_ahb_master_peripheral,
.si = 1,
.di = 0,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config_tx() {
return {
.e = 0,
.srcperipheral = gpdma_tx_src_peripheral,
.destperipheral = gpdma_tx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
return {
.e = 0,
.srcperipheral = gpdma_tx_src_peripheral,
.destperipheral = gpdma_tx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
constexpr gpdma::channel::Control control_rx(const size_t transfer_bytes) {
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_peripheral,
.d = gpdma_ahb_master_memory,
.si = 0,
.di = 1,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_peripheral,
.d = gpdma_ahb_master_memory,
.si = 0,
.di = 1,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config_rx() {
return {
.e = 0,
.srcperipheral = gpdma_rx_src_peripheral,
.destperipheral = gpdma_rx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
return {
.e = 0,
.srcperipheral = gpdma_rx_src_peripheral,
.destperipheral = gpdma_rx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
@@ -145,94 +145,94 @@ static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
static void tx_transfer_complete() {
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
}
static void tx_error() {
disable();
disable();
}
static void rx_transfer_complete() {
rx_next_lli = gpdma_channel_i2s0_rx.next_lli();
rx_next_lli = gpdma_channel_i2s0_rx.next_lli();
}
static void rx_error() {
disable();
disable();
}
void init() {
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
}
static void configure_tx() {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
const auto control_value = control_tx(transfer_bytes);
for(size_t i=0; i<lli_tx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_tx[i * transfer_samples]);
lli_tx_loop[i].srcaddr = memory;
lli_tx_loop[i].destaddr = peripheral;
lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]);
lli_tx_loop[i].control = control_value;
}
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
const auto control_value = control_tx(transfer_bytes);
for (size_t i = 0; i < lli_tx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_tx[i * transfer_samples]);
lli_tx_loop[i].srcaddr = memory;
lli_tx_loop[i].destaddr = peripheral;
lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]);
lli_tx_loop[i].control = control_value;
}
}
static void configure_rx() {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->RXFIFO);
const auto control_value = control_rx(transfer_bytes);
for(size_t i=0; i<lli_rx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_rx[i * transfer_samples]);
lli_rx_loop[i].srcaddr = peripheral;
lli_rx_loop[i].destaddr = memory;
lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]);
lli_rx_loop[i].control = control_value;
}
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->RXFIFO);
const auto control_value = control_rx(transfer_bytes);
for (size_t i = 0; i < lli_rx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_rx[i * transfer_samples]);
lli_rx_loop[i].srcaddr = peripheral;
lli_rx_loop[i].destaddr = memory;
lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]);
lli_rx_loop[i].control = control_value;
}
}
void configure() {
configure_tx();
configure_rx();
configure_tx();
configure_rx();
}
void enable() {
const auto gpdma_config_tx = config_tx();
const auto gpdma_config_rx = config_rx();
const auto gpdma_config_tx = config_tx();
const auto gpdma_config_rx = config_rx();
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
gpdma_channel_i2s0_tx.enable();
gpdma_channel_i2s0_rx.enable();
gpdma_channel_i2s0_tx.enable();
gpdma_channel_i2s0_rx.enable();
}
void disable() {
gpdma_channel_i2s0_tx.disable();
gpdma_channel_i2s0_rx.disable();
gpdma_channel_i2s0_tx.disable();
gpdma_channel_i2s0_rx.disable();
}
buffer_t tx_empty_buffer() {
const auto next_lli = tx_next_lli;
if( next_lli ) {
const size_t next_index = next_lli - &lli_tx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
const auto next_lli = tx_next_lli;
if (next_lli) {
const size_t next_index = next_lli - &lli_tx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return {reinterpret_cast<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples};
} else {
return {nullptr, 0};
}
}
buffer_t rx_empty_buffer() {
const auto next_lli = rx_next_lli;
if( next_lli ) {
const size_t next_index = next_lli - &lli_rx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_rx_loop[free_index].destaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
const auto next_lli = rx_next_lli;
if (next_lli) {
const size_t next_index = next_lli - &lli_rx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return {reinterpret_cast<sample_t*>(lli_rx_loop[free_index].destaddr), transfer_samples};
} else {
return {nullptr, 0};
}
}
} /* namespace dma */

View File

@@ -30,13 +30,13 @@
namespace audio {
struct sample_t {
union {
struct {
int16_t left;
int16_t right;
};
uint32_t raw;
};
union {
struct {
int16_t left;
int16_t right;
};
uint32_t raw;
};
};
using buffer_t = buffer_t<sample_t>;
@@ -54,4 +54,4 @@ audio::buffer_t rx_empty_buffer();
} /* namespace dma */
} /* namespace audio */
#endif/*__AUDIO_DMA_H__*/
#endif /*__AUDIO_DMA_H__*/

View File

@@ -33,8 +33,8 @@
#include <array>
void AudioInput::read_audio_buffer(buffer_s16_t& audio) {
auto audio_buffer = audio::dma::rx_empty_buffer();
for (size_t i=0; i<audio_buffer.count; i++)
audio.p[i] = audio_buffer.p[i].right;
auto audio_buffer = audio::dma::rx_empty_buffer();
for (size_t i = 0; i < audio_buffer.count; i++)
audio.p[i] = audio_buffer.p[i].right;
}

View File

@@ -34,14 +34,14 @@
#include <memory>
class AudioInput {
public:
void read_audio_buffer(buffer_s16_t& audio);
public:
void read_audio_buffer(buffer_s16_t& audio);
private:
/*static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
private:
/*static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
IIRBiquadFilter hpf { };*/
IIRBiquadFilter hpf { };*/
};
#endif/*__AUDIO_INPUT_H__*/
#endif /*__AUDIO_INPUT_H__*/

View File

@@ -33,96 +33,88 @@
#include <array>
void AudioOutput::configure(
const bool do_proc
) {
do_processing = do_proc;
const bool do_proc) {
do_processing = do_proc;
}
void AudioOutput::configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config,
const float squelch_threshold
) {
hpf.configure(hpf_config);
deemph.configure(deemph_config);
squelch.set_threshold(squelch_threshold);
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config,
const float squelch_threshold) {
hpf.configure(hpf_config);
deemph.configure(deemph_config);
squelch.set_threshold(squelch_threshold);
}
void AudioOutput::write(
const buffer_s16_t& audio
) {
std::array<float, 32> audio_f;
for(size_t i=0; i<audio.count; i++) {
audio_f[i] = audio.p[i] * ki;
}
write(buffer_f32_t {
audio_f.data(),
audio.count,
audio.sampling_rate
});
const buffer_s16_t& audio) {
std::array<float, 32> audio_f;
for (size_t i = 0; i < audio.count; i++) {
audio_f[i] = audio.p[i] * ki;
}
write(buffer_f32_t{
audio_f.data(),
audio.count,
audio.sampling_rate});
}
void AudioOutput::write(
const buffer_f32_t& audio
) {
block_buffer.feed(
audio,
[this](const buffer_f32_t& buffer) {
this->on_block(buffer);
}
);
const buffer_f32_t& audio) {
block_buffer.feed(
audio,
[this](const buffer_f32_t& buffer) {
this->on_block(buffer);
});
}
void AudioOutput::on_block(
const buffer_f32_t& audio
) {
if (do_processing) {
const auto audio_present_now = squelch.execute(audio);
const buffer_f32_t& audio) {
if (do_processing) {
const auto audio_present_now = squelch.execute(audio);
hpf.execute_in_place(audio);
deemph.execute_in_place(audio);
hpf.execute_in_place(audio);
deemph.execute_in_place(audio);
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
audio_present = (audio_present_history != 0);
if( !audio_present ) {
for(size_t i=0; i<audio.count; i++) {
audio.p[i] = 0;
}
}
} else
audio_present = true;
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
audio_present = (audio_present_history != 0);
fill_audio_buffer(audio, audio_present);
if (!audio_present) {
for (size_t i = 0; i < audio.count; i++) {
audio.p[i] = 0;
}
}
} else
audio_present = true;
fill_audio_buffer(audio, audio_present);
}
bool AudioOutput::is_squelched() {
return !audio_present;
return !audio_present;
}
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo) {
std::array<int16_t, 32> audio_int;
std::array<int16_t, 32> audio_int;
auto audio_buffer = audio::dma::tx_empty_buffer();
for(size_t i=0; i<audio_buffer.count; i++) {
const int32_t sample_int = audio.p[i] * k;
const int32_t sample_saturated = __SSAT(sample_int, 16);
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
audio_int[i] = sample_saturated;
}
if( stream && send_to_fifo ) {
stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
}
auto audio_buffer = audio::dma::tx_empty_buffer();
for (size_t i = 0; i < audio_buffer.count; i++) {
const int32_t sample_int = audio.p[i] * k;
const int32_t sample_saturated = __SSAT(sample_int, 16);
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
audio_int[i] = sample_saturated;
}
if (stream && send_to_fifo) {
stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
}
feed_audio_stats(audio);
feed_audio_stats(audio);
}
void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) {
audio_stats.feed(
audio,
[](const AudioStatistics& statistics) {
const AudioStatisticsMessage audio_stats_message { statistics };
shared_memory.application_queue.push(audio_stats_message);
}
);
audio_stats.feed(
audio,
[](const AudioStatistics& statistics) {
const AudioStatisticsMessage audio_stats_message{statistics};
shared_memory.application_queue.push(audio_stats_message);
});
}

View File

@@ -36,46 +36,45 @@
#include <memory>
class AudioOutput {
public:
void configure(const bool do_proc);
void configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
const float squelch_threshold = 0.0f
);
public:
void configure(const bool do_proc);
void write(const buffer_s16_t& audio);
void write(const buffer_f32_t& audio);
void configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
const float squelch_threshold = 0.0f);
void set_stream(std::unique_ptr<StreamInput> new_stream) {
stream = std::move(new_stream);
}
bool is_squelched();
void write(const buffer_s16_t& audio);
void write(const buffer_f32_t& audio);
private:
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
void set_stream(std::unique_ptr<StreamInput> new_stream) {
stream = std::move(new_stream);
}
BlockDecimator<float, 32> block_buffer { 1 };
bool is_squelched();
IIRBiquadFilter hpf { };
IIRBiquadFilter deemph { };
FMSquelch squelch { };
private:
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
std::unique_ptr<StreamInput> stream { };
BlockDecimator<float, 32> block_buffer{1};
AudioStatsCollector audio_stats { };
IIRBiquadFilter hpf{};
IIRBiquadFilter deemph{};
FMSquelch squelch{};
uint64_t audio_present_history = 0;
bool audio_present = false;
bool do_processing = true;
std::unique_ptr<StreamInput> stream{};
void on_block(const buffer_f32_t& audio);
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
void feed_audio_stats(const buffer_f32_t& audio);
AudioStatsCollector audio_stats{};
uint64_t audio_present_history = 0;
bool audio_present = false;
bool do_processing = true;
void on_block(const buffer_f32_t& audio);
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
void feed_audio_stats(const buffer_f32_t& audio);
};
#endif/*__AUDIO_OUTPUT_H__*/
#endif /*__AUDIO_OUTPUT_H__*/

View File

@@ -24,44 +24,44 @@
#include "utility.hpp"
void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) {
auto src_p = src.p;
const auto src_end = &src.p[src.count];
while(src_p < src_end) {
const auto sample = *(src_p++);
const auto sample_squared = sample * sample;
squared_sum += sample_squared;
if( sample_squared > max_squared ) {
max_squared = sample_squared;
}
}
auto src_p = src.p;
const auto src_end = &src.p[src.count];
while (src_p < src_end) {
const auto sample = *(src_p++);
const auto sample_squared = sample * sample;
squared_sum += sample_squared;
if (sample_squared > max_squared) {
max_squared = sample_squared;
}
}
}
bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t sampling_rate) {
count += sample_count;
count += sample_count;
const size_t samples_per_update = sampling_rate * update_interval;
const size_t samples_per_update = sampling_rate * update_interval;
if( count >= samples_per_update ) {
statistics.rms_db = mag2_to_dbv_norm(squared_sum / count);
statistics.max_db = mag2_to_dbv_norm(max_squared);
statistics.count = count;
if (count >= samples_per_update) {
statistics.rms_db = mag2_to_dbv_norm(squared_sum / count);
statistics.max_db = mag2_to_dbv_norm(max_squared);
statistics.count = count;
squared_sum = 0;
max_squared = 0;
count = 0;
squared_sum = 0;
max_squared = 0;
count = 0;
return true;
} else {
return false;
}
return true;
} else {
return false;
}
}
bool AudioStatsCollector::feed(const buffer_f32_t& src) {
consume_audio_buffer(src);
consume_audio_buffer(src);
return update_stats(src.count, src.sampling_rate);
return update_stats(src.count, src.sampling_rate);
}
bool AudioStatsCollector::mute(const size_t sample_count, const size_t sampling_rate) {
return update_stats(sample_count, sampling_rate);
return update_stats(sample_count, sampling_rate);
}

View File

@@ -29,35 +29,35 @@
#include <cstddef>
class AudioStatsCollector {
public:
template<typename Callback>
void feed(const buffer_f32_t& src, Callback callback) {
if( feed(src) ) {
callback(statistics);
}
}
public:
template <typename Callback>
void feed(const buffer_f32_t& src, Callback callback) {
if (feed(src)) {
callback(statistics);
}
}
template<typename Callback>
void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) {
if( mute(sample_count, sampling_rate) ) {
callback(statistics);
}
}
template <typename Callback>
void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) {
if (mute(sample_count, sampling_rate)) {
callback(statistics);
}
}
private:
static constexpr float update_interval { 0.1f };
float squared_sum { 0 };
float max_squared { 0 };
size_t count { 0 };
private:
static constexpr float update_interval{0.1f};
float squared_sum{0};
float max_squared{0};
size_t count{0};
AudioStatistics statistics { };
AudioStatistics statistics{};
void consume_audio_buffer(const buffer_f32_t& src);
void consume_audio_buffer(const buffer_f32_t& src);
bool update_stats(const size_t sample_count, const size_t sampling_rate);
bool update_stats(const size_t sample_count, const size_t sampling_rate);
bool feed(const buffer_f32_t& src);
bool mute(const size_t sample_count, const size_t sampling_rate);
bool feed(const buffer_f32_t& src);
bool mute(const size_t sample_count, const size_t sampling_rate);
};
#endif/*__AUDIO_STATS_COLLECTOR_H__*/
#endif /*__AUDIO_STATS_COLLECTOR_H__*/

View File

@@ -31,57 +31,56 @@
#include "audio_dma.hpp"
static void init() {
audio::dma::init();
audio::dma::configure();
audio::dma::enable();
audio::dma::init();
audio::dma::configure();
audio::dma::enable();
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
}
static void halt() {
port_disable();
while(true) {
port_wait_for_interrupt();
}
port_disable();
while (true) {
port_wait_for_interrupt();
}
}
extern "C" {
void __late_init(void) {
/*
* System initializations.
* - HAL initialization, this also initializes the configured device drivers
* and performs the board-specific initializations.
* - Kernel initialization, the main() function becomes a thread and the
* RTOS is active.
*/
halInit();
/*
* System initializations.
* - HAL initialization, this also initializes the configured device drivers
* and performs the board-specific initializations.
* - Kernel initialization, the main() function becomes a thread and the
* RTOS is active.
*/
halInit();
/* After this call, scheduler, systick, heap, etc. are available. */
/* By doing chSysInit() here, it runs before C++ constructors, which may
* require the heap.
*/
chSysInit();
/* After this call, scheduler, systick, heap, etc. are available. */
/* By doing chSysInit() here, it runs before C++ constructors, which may
* require the heap.
*/
chSysInit();
/* Baseband initialization */
init();
/* Baseband initialization */
init();
}
void _default_exit(void) {
// TODO: Is this complete?
nvicDisableVector(DMA_IRQn);
chSysDisable();
// TODO: Is this complete?
systick_stop();
nvicDisableVector(DMA_IRQn);
ShutdownMessage shutdown_message;
shared_memory.application_queue.push(shutdown_message);
chSysDisable();
shared_memory.baseband_message = nullptr;
systick_stop();
halt();
ShutdownMessage shutdown_message;
shared_memory.application_queue.push(shutdown_message);
shared_memory.baseband_message = nullptr;
halt();
}
}

View File

@@ -46,45 +46,45 @@ constexpr uint32_t gpdma_src_peripheral = 0x0;
constexpr uint32_t gpdma_dest_peripheral = 0x0;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control(const baseband::Direction direction, const size_t buffer_words) {
return {
.transfersize = buffer_words,
.sbsize = 0, /* Burst size: 1 */
.dbsize = 0, /* Burst size: 1 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio,
.d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory,
.si = (direction == baseband::Direction::Transmit) ? 1U : 0U,
.di = (direction == baseband::Direction::Transmit) ? 0U : 1U,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
return {
.transfersize = buffer_words,
.sbsize = 0, /* Burst size: 1 */
.dbsize = 0, /* Burst size: 1 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio,
.d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory,
.si = (direction == baseband::Direction::Transmit) ? 1U : 0U,
.di = (direction == baseband::Direction::Transmit) ? 0U : 1U,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config(const baseband::Direction direction) {
return {
.e = 0,
.srcperipheral = gpdma_src_peripheral,
.destperipheral = gpdma_dest_peripheral,
.flowcntrl = (direction == baseband::Direction::Transmit)
? gpdma::FlowControl::MemoryToPeripheral_DMAControl
: gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
return {
.e = 0,
.srcperipheral = gpdma_src_peripheral,
.destperipheral = gpdma_dest_peripheral,
.flowcntrl = (direction == baseband::Direction::Transmit)
? gpdma::FlowControl::MemoryToPeripheral_DMAControl
: gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
constexpr size_t buffer_samples_log2n = 13;
@@ -108,75 +108,74 @@ volatile uint32_t buffer_transfered = 0;
volatile uint32_t buffer_handled = 0;
static void transfer_complete() {
const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0];
buffer_transfered++;
thread_wait.wake_from_interrupt(next_lli_index);
const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0];
buffer_transfered++;
thread_wait.wake_from_interrupt(next_lli_index);
}
static void dma_error() {
thread_wait.wake_from_interrupt(-1);
disable();
thread_wait.wake_from_interrupt(-1);
disable();
}
void init() {
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
#if defined(PORTAPACK_BASEBAND_DMA_NO_SYNC)
/* Disable synchronization logic to improve(?) DMA response time.
* SGPIO (peripheral) must be on same clock as GPDMA peripheral.
* SGPIO runs from BASE_PERIPH_CLK, which is set to PLL1 in normal
* operation, same as the M4 and M0 cores. Memory, of course, is
* running from the same clock as the cores.
*/
LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
/* Disable synchronization logic to improve(?) DMA response time.
* SGPIO (peripheral) must be on same clock as GPDMA peripheral.
* SGPIO runs from BASE_PERIPH_CLK, which is set to PLL1 in normal
* operation, same as the M4 and M0 cores. Memory, of course, is
* running from the same clock as the cores.
*/
LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
#endif
}
void configure(
baseband::sample_t* const buffer_base,
const baseband::Direction direction
) {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0]);
const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4));
for(size_t i=0; i<lli_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_base[i * transfer_samples]);
lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral;
lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory;
lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]);
lli_loop[i].control = control_value;
}
baseband::sample_t* const buffer_base,
const baseband::Direction direction) {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0]);
const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4));
for (size_t i = 0; i < lli_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_base[i * transfer_samples]);
lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral;
lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory;
lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]);
lli_loop[i].control = control_value;
}
}
void enable(const baseband::Direction direction) {
const auto gpdma_config = config(direction);
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
gpdma_channel_sgpio.enable();
const auto gpdma_config = config(direction);
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
gpdma_channel_sgpio.enable();
}
bool is_enabled() {
return gpdma_channel_sgpio.is_enabled();
return gpdma_channel_sgpio.is_enabled();
}
void disable() {
gpdma_channel_sgpio.disable();
gpdma_channel_sgpio.disable();
}
baseband::buffer_t wait_for_buffer() {
const auto next_index = thread_wait.sleep();
buffer_handled++;
const auto next_index = thread_wait.sleep();
buffer_handled++;
auto buffer_missed = buffer_transfered - buffer_handled;
shared_memory.m4_buffer_missed = buffer_missed;
if( next_index >= 0 ) {
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
const auto src = lli_loop[free_index].srcaddr;
const auto dst = lli_loop[free_index].destaddr;
const auto p = (src == reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0])) ? dst : src;
return { reinterpret_cast<sample_t*>(p), transfer_samples };
} else {
return { };
}
auto buffer_missed = buffer_transfered - buffer_handled;
shared_memory.m4_buffer_missed = buffer_missed;
if (next_index >= 0) {
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
const auto src = lli_loop[free_index].srcaddr;
const auto dst = lli_loop[free_index].destaddr;
const auto p = (src == reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0])) ? dst : src;
return {reinterpret_cast<sample_t*>(p), transfer_samples};
} else {
return {};
}
}
} /* namespace dma */

View File

@@ -33,9 +33,8 @@ namespace dma {
void init();
void configure(
baseband::sample_t* const buffer_base,
const baseband::Direction direction
);
baseband::sample_t* const buffer_base,
const baseband::Direction direction);
void enable(const baseband::Direction direction);
bool is_enabled();
@@ -47,4 +46,4 @@ baseband::buffer_t wait_for_buffer();
} /* namespace dma */
} /* namespace baseband */
#endif/*__BASEBAND_DMA_H__*/
#endif /*__BASEBAND_DMA_H__*/

View File

@@ -26,11 +26,10 @@
#include "message.hpp"
void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) {
channel_stats.feed(
channel,
[](const ChannelStatistics& statistics) {
const ChannelStatisticsMessage channel_stats_message { statistics };
shared_memory.application_queue.push(channel_stats_message);
}
);
channel_stats.feed(
channel,
[](const ChannelStatistics& statistics) {
const ChannelStatisticsMessage channel_stats_message{statistics};
shared_memory.application_queue.push(channel_stats_message);
});
}

View File

@@ -29,18 +29,18 @@
#include "message.hpp"
class BasebandProcessor {
public:
virtual ~BasebandProcessor() = default;
public:
virtual ~BasebandProcessor() = default;
virtual void execute(const buffer_c8_t& buffer) = 0;
virtual void execute(const buffer_c8_t& buffer) = 0;
virtual void on_message(const Message* const) { };
virtual void on_message(const Message* const){};
protected:
void feed_channel_stats(const buffer_c16_t& channel);
protected:
void feed_channel_stats(const buffer_c16_t& channel);
private:
ChannelStatsCollector channel_stats { };
private:
ChannelStatsCollector channel_stats{};
};
#endif/*__BASEBAND_PROCESSOR_H__*/
#endif /*__BASEBAND_PROCESSOR_H__*/

View File

@@ -24,36 +24,36 @@
#include "lpc43xx_cpp.hpp"
bool BasebandStatsCollector::process(const buffer_c8_t& buffer) {
samples += buffer.count;
samples += buffer.count;
const size_t report_samples = buffer.sampling_rate * report_interval;
const auto report_delta = samples - samples_last_report;
return report_delta >= report_samples;
const size_t report_samples = buffer.sampling_rate * report_interval;
const auto report_delta = samples - samples_last_report;
return report_delta >= report_samples;
}
BasebandStatistics BasebandStatsCollector::capture_statistics() {
BasebandStatistics statistics;
BasebandStatistics statistics;
const auto idle_ticks = thread_idle->total_ticks;
statistics.idle_ticks = (idle_ticks - last_idle_ticks);
last_idle_ticks = idle_ticks;
const auto idle_ticks = thread_idle->total_ticks;
statistics.idle_ticks = (idle_ticks - last_idle_ticks);
last_idle_ticks = idle_ticks;
const auto main_ticks = thread_main->total_ticks;
statistics.main_ticks = (main_ticks - last_main_ticks);
last_main_ticks = main_ticks;
const auto main_ticks = thread_main->total_ticks;
statistics.main_ticks = (main_ticks - last_main_ticks);
last_main_ticks = main_ticks;
const auto rssi_ticks = thread_rssi->total_ticks;
statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks);
last_rssi_ticks = rssi_ticks;
const auto rssi_ticks = thread_rssi->total_ticks;
statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks);
last_rssi_ticks = rssi_ticks;
const auto baseband_ticks = thread_baseband->total_ticks;
statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks);
last_baseband_ticks = baseband_ticks;
const auto baseband_ticks = thread_baseband->total_ticks;
statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks);
last_baseband_ticks = baseband_ticks;
statistics.saturation = lpc43xx::m4::flag_saturation();
lpc43xx::m4::clear_flag_saturation();
statistics.saturation = lpc43xx::m4::flag_saturation();
lpc43xx::m4::clear_flag_saturation();
samples_last_report = samples;
samples_last_report = samples;
return statistics;
return statistics;
}

View File

@@ -31,41 +31,40 @@
#include <cstddef>
class BasebandStatsCollector {
public:
BasebandStatsCollector(
const Thread* const thread_idle,
const Thread* const thread_main,
const Thread* const thread_rssi,
const Thread* const thread_baseband
) : thread_idle { thread_idle },
thread_main { thread_main },
thread_rssi { thread_rssi },
thread_baseband { thread_baseband }
{
}
public:
BasebandStatsCollector(
const Thread* const thread_idle,
const Thread* const thread_main,
const Thread* const thread_rssi,
const Thread* const thread_baseband)
: thread_idle{thread_idle},
thread_main{thread_main},
thread_rssi{thread_rssi},
thread_baseband{thread_baseband} {
}
template<typename Callback>
void process(const buffer_c8_t& buffer, Callback callback) {
if( process(buffer) ) {
callback(capture_statistics());
}
}
template <typename Callback>
void process(const buffer_c8_t& buffer, Callback callback) {
if (process(buffer)) {
callback(capture_statistics());
}
}
private:
static constexpr float report_interval { 1.0f };
size_t samples { 0 };
size_t samples_last_report { 0 };
const Thread* const thread_idle;
uint32_t last_idle_ticks { 0 };
const Thread* const thread_main;
uint32_t last_main_ticks { 0 };
const Thread* const thread_rssi;
uint32_t last_rssi_ticks { 0 };
const Thread* const thread_baseband;
uint32_t last_baseband_ticks { 0 };
private:
static constexpr float report_interval{1.0f};
size_t samples{0};
size_t samples_last_report{0};
const Thread* const thread_idle;
uint32_t last_idle_ticks{0};
const Thread* const thread_main;
uint32_t last_main_ticks{0};
const Thread* const thread_rssi;
uint32_t last_rssi_ticks{0};
const Thread* const thread_baseband;
uint32_t last_baseband_ticks{0};
bool process(const buffer_c8_t& buffer);
BasebandStatistics capture_statistics();
bool process(const buffer_c8_t& buffer);
BasebandStatistics capture_statistics();
};
#endif/*__BASEBAND_STATS_COLLECTOR_H__*/
#endif /*__BASEBAND_STATS_COLLECTOR_H__*/

View File

@@ -44,60 +44,56 @@ WORKING_AREA(baseband_thread_wa, 4096);
Thread* BasebandThread::thread = nullptr;
BasebandThread::BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority,
baseband::Direction direction
) : baseband_processor { baseband_processor },
_direction { direction },
sampling_rate { sampling_rate }
{
thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
priority, ThreadBase::fn,
this
);
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority,
baseband::Direction direction)
: baseband_processor{baseband_processor},
_direction{direction},
sampling_rate{sampling_rate} {
thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
priority, ThreadBase::fn,
this);
}
BasebandThread::~BasebandThread() {
chThdTerminate(thread);
chThdWait(thread);
thread = nullptr;
chThdTerminate(thread);
chThdWait(thread);
thread = nullptr;
}
void BasebandThread::set_sampling_rate(uint32_t new_sampling_rate) {
sampling_rate = new_sampling_rate;
sampling_rate = new_sampling_rate;
}
void BasebandThread::run() {
baseband_sgpio.init();
baseband::dma::init();
baseband_sgpio.init();
baseband::dma::init();
const auto baseband_buffer = std::make_unique<std::array<baseband::sample_t, 8192>>();
baseband::dma::configure(
baseband_buffer->data(),
direction()
);
//baseband::dma::allocate(4, 2048);
const auto baseband_buffer = std::make_unique<std::array<baseband::sample_t, 8192>>();
baseband::dma::configure(
baseband_buffer->data(),
direction());
// baseband::dma::allocate(4, 2048);
baseband_sgpio.configure(direction());
baseband::dma::enable(direction());
baseband_sgpio.streaming_enable();
baseband_sgpio.configure(direction());
baseband::dma::enable(direction());
baseband_sgpio.streaming_enable();
while( !chThdShouldTerminate() ) {
// TODO: Place correct sampling rate into buffer returned here:
const auto buffer_tmp = baseband::dma::wait_for_buffer();
if( buffer_tmp ) {
buffer_c8_t buffer {
buffer_tmp.p, buffer_tmp.count, sampling_rate
};
while (!chThdShouldTerminate()) {
// TODO: Place correct sampling rate into buffer returned here:
const auto buffer_tmp = baseband::dma::wait_for_buffer();
if (buffer_tmp) {
buffer_c8_t buffer{
buffer_tmp.p, buffer_tmp.count, sampling_rate};
if( baseband_processor ) {
baseband_processor->execute(buffer);
}
}
}
if (baseband_processor) {
baseband_processor->execute(buffer);
}
}
}
i2s::i2s0::tx_mute();
baseband::dma::disable();
baseband_sgpio.streaming_disable();
i2s::i2s0::tx_mute();
baseband::dma::disable();
baseband_sgpio.streaming_disable();
}

View File

@@ -29,36 +29,35 @@
#include <ch.h>
class BasebandThread : public ThreadBase {
public:
BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority,
const baseband::Direction direction = baseband::Direction::Receive
);
~BasebandThread();
public:
BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority,
const baseband::Direction direction = baseband::Direction::Receive);
~BasebandThread();
BasebandThread(const BasebandThread&) = delete;
BasebandThread(BasebandThread&&) = delete;
BasebandThread& operator=(const BasebandThread&) = delete;
BasebandThread& operator=(BasebandThread&&) = delete;
// This getter should die, it's just here to leak information to code that
// isn't in the right place to begin with.
baseband::Direction direction() const {
return _direction;
}
void set_sampling_rate(uint32_t new_sampling_rate);
BasebandThread(const BasebandThread&) = delete;
BasebandThread(BasebandThread&&) = delete;
BasebandThread& operator=(const BasebandThread&) = delete;
BasebandThread& operator=(BasebandThread&&) = delete;
private:
static Thread* thread;
// This getter should die, it's just here to leak information to code that
// isn't in the right place to begin with.
baseband::Direction direction() const {
return _direction;
}
BasebandProcessor* baseband_processor { nullptr };
baseband::Direction _direction { baseband::Direction::Receive };
uint32_t sampling_rate { 0 };
void set_sampling_rate(uint32_t new_sampling_rate);
void run() override;
private:
static Thread* thread;
BasebandProcessor* baseband_processor{nullptr};
baseband::Direction _direction{baseband::Direction::Receive};
uint32_t sampling_rate{0};
void run() override;
};
#endif/*__BASEBAND_THREAD_H__*/
#endif /*__BASEBAND_THREAD_H__*/

View File

@@ -29,72 +29,71 @@
#include "dsp_types.hpp"
#include "complex.hpp"
template<typename T, size_t N>
template <typename T, size_t N>
class BlockDecimator {
public:
constexpr BlockDecimator(
const size_t factor
) : factor_ { factor }
{
}
public:
constexpr BlockDecimator(
const size_t factor)
: factor_{factor} {
}
void set_input_sampling_rate(const uint32_t new_sampling_rate) {
if( new_sampling_rate != input_sampling_rate() ) {
input_sampling_rate_ = new_sampling_rate;
reset_state();
}
}
void set_input_sampling_rate(const uint32_t new_sampling_rate) {
if (new_sampling_rate != input_sampling_rate()) {
input_sampling_rate_ = new_sampling_rate;
reset_state();
}
}
uint32_t input_sampling_rate() const {
return input_sampling_rate_;
}
uint32_t input_sampling_rate() const {
return input_sampling_rate_;
}
void set_factor(const size_t new_factor) {
if( new_factor != factor() ) {
factor_ = new_factor;
reset_state();
}
}
void set_factor(const size_t new_factor) {
if (new_factor != factor()) {
factor_ = new_factor;
reset_state();
}
}
size_t factor() const {
return factor_;
}
size_t factor() const {
return factor_;
}
uint32_t output_sampling_rate() const {
return input_sampling_rate() / factor();
}
uint32_t output_sampling_rate() const {
return input_sampling_rate() / factor();
}
template<typename BlockCallback>
void feed(const buffer_t<T>& src, BlockCallback callback) {
/* NOTE: Input block size must be >= factor */
template <typename BlockCallback>
void feed(const buffer_t<T>& src, BlockCallback callback) {
/* NOTE: Input block size must be >= factor */
set_input_sampling_rate(src.sampling_rate);
set_input_sampling_rate(src.sampling_rate);
while( src_i < src.count ) {
buffer[dst_i++] = src.p[src_i];
if( dst_i == buffer.size() ) {
callback({ buffer.data(), buffer.size(), output_sampling_rate() });
reset_state();
dst_i = 0;
}
while (src_i < src.count) {
buffer[dst_i++] = src.p[src_i];
if (dst_i == buffer.size()) {
callback({buffer.data(), buffer.size(), output_sampling_rate()});
reset_state();
dst_i = 0;
}
src_i += factor();
}
src_i += factor();
}
src_i -= src.count;
}
src_i -= src.count;
}
private:
std::array<T, N> buffer { };
uint32_t input_sampling_rate_ { 0 };
size_t factor_ { 1 };
size_t src_i { 0 };
size_t dst_i { 0 };
private:
std::array<T, N> buffer{};
uint32_t input_sampling_rate_{0};
size_t factor_{1};
size_t src_i{0};
size_t dst_i{0};
void reset_state() {
src_i = 0;
dst_i = 0;
}
void reset_state() {
src_i = 0;
dst_i = 0;
}
};
#endif/*__BLOCK_DECIMATOR_H__*/
#endif /*__BLOCK_DECIMATOR_H__*/

View File

@@ -22,70 +22,67 @@
#include "channel_decimator.hpp"
buffer_c16_t ChannelDecimator::execute_decimation(const buffer_c8_t& buffer) {
const buffer_c16_t work_baseband_buffer {
work_baseband.data(),
work_baseband.size()
};
const buffer_c16_t work_baseband_buffer{
work_baseband.data(),
work_baseband.size()};
const buffer_s16_t work_audio_buffer {
(int16_t*)work_baseband.data(),
sizeof(work_baseband) / sizeof(int16_t)
};
const buffer_s16_t work_audio_buffer{
(int16_t*)work_baseband.data(),
sizeof(work_baseband) / sizeof(int16_t)};
/* 3.072MHz complex<int8_t>[2048], [-128, 127]
* -> Shift by -fs/4
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
* -0.1dB @ 86kHz, -1dB @ 270kHz, -60dB @ 1.44MHz
* -> gain of 256
* -> decimation by 2
* -> 1.544MHz complex<int16_t>[1024], [-32768, 32512] */
auto stage_0_out = execute_stage_0(buffer, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By2 ) {
return stage_0_out;
}
/* 3.072MHz complex<int8_t>[2048], [-128, 127]
* -> Shift by -fs/4
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
* -0.1dB @ 86kHz, -1dB @ 270kHz, -60dB @ 1.44MHz
* -> gain of 256
* -> decimation by 2
* -> 1.544MHz complex<int16_t>[1024], [-32768, 32512] */
auto stage_0_out = execute_stage_0(buffer, work_baseband_buffer);
if (decimation_factor == DecimationFactor::By2) {
return stage_0_out;
}
/* 1.536MHz complex<int16_t>[1024], [-32768, 32512]
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
* -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz
* -> gain of 1
* -> decimation by 2
* -> 768kHz complex<int16_t>[512], [-8192, 8128] */
auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By4 ) {
return cic_1_out;
}
/* 1.536MHz complex<int16_t>[1024], [-32768, 32512]
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
* -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz
* -> gain of 1
* -> decimation by 2
* -> 768kHz complex<int16_t>[512], [-8192, 8128] */
auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer);
if (decimation_factor == DecimationFactor::By4) {
return cic_1_out;
}
/* 768kHz complex<int16_t>[512], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 384kHz complex<int16_t>[256], [-32768, 32512] */
auto cic_2_out = cic_2.execute(cic_1_out, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By8 ) {
return cic_2_out;
}
/* 768kHz complex<int16_t>[512], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 384kHz complex<int16_t>[256], [-32768, 32512] */
auto cic_2_out = cic_2.execute(cic_1_out, work_baseband_buffer);
if (decimation_factor == DecimationFactor::By8) {
return cic_2_out;
}
/* 384kHz complex<int16_t>[256], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 192kHz complex<int16_t>[128], [-32768, 32512] */
auto cic_3_out = cic_3.execute(cic_2_out, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By16 ) {
return cic_3_out;
}
/* 384kHz complex<int16_t>[256], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 192kHz complex<int16_t>[128], [-32768, 32512] */
auto cic_3_out = cic_3.execute(cic_2_out, work_baseband_buffer);
if (decimation_factor == DecimationFactor::By16) {
return cic_3_out;
}
/* 192kHz complex<int16_t>[128], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 96kHz complex<int16_t>[64], [-32768, 32512] */
auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer);
/* 192kHz complex<int16_t>[128], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 96kHz complex<int16_t>[64], [-32768, 32512] */
auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer);
return cic_4_out;
return cic_4_out;
}
buffer_c16_t ChannelDecimator::execute_stage_0(
const buffer_c8_t& buffer,
const buffer_c16_t& work_baseband_buffer
) {
if( fs_over_4_downconvert ) {
return translate.execute(buffer, work_baseband_buffer);
} else {
return cic_0.execute(buffer, work_baseband_buffer);
}
const buffer_c8_t& buffer,
const buffer_c16_t& work_baseband_buffer) {
if (fs_over_4_downconvert) {
return translate.execute(buffer, work_baseband_buffer);
} else {
return cic_0.execute(buffer, work_baseband_buffer);
}
}

View File

@@ -30,52 +30,50 @@
#include <array>
class ChannelDecimator {
public:
enum class DecimationFactor {
By2,
By4,
By8,
By16,
By32,
};
constexpr ChannelDecimator(
const DecimationFactor decimation_factor,
const bool fs_over_4_downconvert = true
) : decimation_factor { decimation_factor },
fs_over_4_downconvert { fs_over_4_downconvert }
{
}
public:
enum class DecimationFactor {
By2,
By4,
By8,
By16,
By32,
};
void set_decimation_factor(const DecimationFactor f) {
decimation_factor = f;
}
constexpr ChannelDecimator(
const DecimationFactor decimation_factor,
const bool fs_over_4_downconvert = true)
: decimation_factor{decimation_factor},
fs_over_4_downconvert{fs_over_4_downconvert} {
}
buffer_c16_t execute(const buffer_c8_t& buffer) {
auto decimated = execute_decimation(buffer);
void set_decimation_factor(const DecimationFactor f) {
decimation_factor = f;
}
return decimated;
}
buffer_c16_t execute(const buffer_c8_t& buffer) {
auto decimated = execute_decimation(buffer);
private:
std::array<complex16_t, 1024> work_baseband { };
return decimated;
}
dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate { };
dsp::decimate::Complex8DecimateBy2CIC3 cic_0 { };
dsp::decimate::DecimateBy2CIC3 cic_1 { };
dsp::decimate::DecimateBy2CIC3 cic_2 { };
dsp::decimate::DecimateBy2CIC3 cic_3 { };
dsp::decimate::DecimateBy2CIC3 cic_4 { };
private:
std::array<complex16_t, 1024> work_baseband{};
DecimationFactor decimation_factor { DecimationFactor::By32 };
const bool fs_over_4_downconvert { true };
dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate{};
dsp::decimate::Complex8DecimateBy2CIC3 cic_0{};
dsp::decimate::DecimateBy2CIC3 cic_1{};
dsp::decimate::DecimateBy2CIC3 cic_2{};
dsp::decimate::DecimateBy2CIC3 cic_3{};
dsp::decimate::DecimateBy2CIC3 cic_4{};
buffer_c16_t execute_decimation(const buffer_c8_t& buffer);
DecimationFactor decimation_factor{DecimationFactor::By32};
const bool fs_over_4_downconvert{true};
buffer_c16_t execute_stage_0(
const buffer_c8_t& buffer,
const buffer_c16_t& work_baseband_buffer
);
buffer_c16_t execute_decimation(const buffer_c8_t& buffer);
buffer_c16_t execute_stage_0(
const buffer_c8_t& buffer,
const buffer_c16_t& work_baseband_buffer);
};
#endif/*__CHANNEL_DECIMATOR_H__*/
#endif /*__CHANNEL_DECIMATOR_H__*/

View File

@@ -32,35 +32,35 @@
#include <hal.h>
class ChannelStatsCollector {
public:
template<typename Callback>
void feed(const buffer_c16_t& src, Callback callback) {
void *src_p = src.p;
while(src_p < &src.p[src.count]) {
const uint32_t sample = *__SIMD32(src_p)++;
const uint32_t mag_sq = __SMUAD(sample, sample);
if( mag_sq > max_squared ) {
max_squared = mag_sq;
}
}
count += src.count;
public:
template <typename Callback>
void feed(const buffer_c16_t& src, Callback callback) {
void* src_p = src.p;
while (src_p < &src.p[src.count]) {
const uint32_t sample = *__SIMD32(src_p)++;
const uint32_t mag_sq = __SMUAD(sample, sample);
if (mag_sq > max_squared) {
max_squared = mag_sq;
}
}
count += src.count;
const size_t samples_per_update = src.sampling_rate * update_interval;
const size_t samples_per_update = src.sampling_rate * update_interval;
if( count >= samples_per_update ) {
const float max_squared_f = max_squared;
const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f)));
callback({ max_db, count });
if (count >= samples_per_update) {
const float max_squared_f = max_squared;
const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f)));
callback({max_db, count});
max_squared = 0;
count = 0;
}
}
max_squared = 0;
count = 0;
}
}
private:
static constexpr float update_interval { 0.1f };
uint32_t max_squared { 0 };
size_t count { 0 };
private:
static constexpr float update_interval{0.1f};
uint32_t max_squared{0};
size_t count{0};
};
#endif/*__CHANNEL_STATS_COLLECTOR_H__*/
#endif /*__CHANNEL_STATS_COLLECTOR_H__*/

View File

@@ -42,7 +42,7 @@
* setting also defines the system tick time unit.
*/
#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__)
#define CH_FREQUENCY 1000
#define CH_FREQUENCY 1000
#endif
/**
@@ -57,7 +57,7 @@
* and generally faster.
*/
#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__)
#define CH_TIME_QUANTUM 0
#define CH_TIME_QUANTUM 0
#endif
/**
@@ -72,7 +72,7 @@
* @note Requires @p CH_USE_MEMCORE.
*/
#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__)
#define CH_MEMCORE_SIZE 0
#define CH_MEMCORE_SIZE 0
#endif
/**
@@ -89,7 +89,7 @@
* enter a sleep state.
*/
#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__)
#define CH_NO_IDLE_THREAD FALSE
#define CH_NO_IDLE_THREAD FALSE
#endif
/** @} */
@@ -110,7 +110,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__)
#define CH_OPTIMIZE_SPEED TRUE
#define CH_OPTIMIZE_SPEED TRUE
#endif
/** @} */
@@ -129,7 +129,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
#define CH_USE_REGISTRY FALSE
#define CH_USE_REGISTRY FALSE
#endif
/**
@@ -140,7 +140,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__)
#define CH_USE_WAITEXIT TRUE
#define CH_USE_WAITEXIT TRUE
#endif
/**
@@ -150,7 +150,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES TRUE
#define CH_USE_SEMAPHORES TRUE
#endif
/**
@@ -162,7 +162,7 @@
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES_PRIORITY FALSE
#define CH_USE_SEMAPHORES_PRIORITY FALSE
#endif
/**
@@ -174,7 +174,7 @@
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__)
#define CH_USE_SEMSW TRUE
#define CH_USE_SEMSW TRUE
#endif
/**
@@ -184,7 +184,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__)
#define CH_USE_MUTEXES TRUE
#define CH_USE_MUTEXES TRUE
#endif
/**
@@ -196,7 +196,7 @@
* @note Requires @p CH_USE_MUTEXES.
*/
#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS TRUE
#define CH_USE_CONDVARS TRUE
#endif
/**
@@ -208,7 +208,7 @@
* @note Requires @p CH_USE_CONDVARS.
*/
#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS_TIMEOUT TRUE
#define CH_USE_CONDVARS_TIMEOUT TRUE
#endif
/**
@@ -218,7 +218,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__)
#define CH_USE_EVENTS TRUE
#define CH_USE_EVENTS TRUE
#endif
/**
@@ -230,7 +230,7 @@
* @note Requires @p CH_USE_EVENTS.
*/
#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_EVENTS_TIMEOUT TRUE
#define CH_USE_EVENTS_TIMEOUT TRUE
#endif
/**
@@ -241,7 +241,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES TRUE
#define CH_USE_MESSAGES TRUE
#endif
/**
@@ -253,7 +253,7 @@
* @note Requires @p CH_USE_MESSAGES.
*/
#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES_PRIORITY FALSE
#define CH_USE_MESSAGES_PRIORITY FALSE
#endif
/**
@@ -265,7 +265,7 @@
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__)
#define CH_USE_MAILBOXES TRUE
#define CH_USE_MAILBOXES TRUE
#endif
/**
@@ -275,7 +275,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__)
#define CH_USE_QUEUES TRUE
#define CH_USE_QUEUES TRUE
#endif
/**
@@ -286,7 +286,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__)
#define CH_USE_MEMCORE TRUE
#define CH_USE_MEMCORE TRUE
#endif
/**
@@ -300,7 +300,7 @@
* @note Mutexes are recommended.
*/
#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__)
#define CH_USE_HEAP TRUE
#define CH_USE_HEAP TRUE
#endif
/**
@@ -314,7 +314,7 @@
* appropriate documentation.
*/
#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__)
#define CH_USE_MALLOC_HEAP FALSE
#define CH_USE_MALLOC_HEAP FALSE
#endif
/**
@@ -325,7 +325,7 @@
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__)
#define CH_USE_MEMPOOLS TRUE
#define CH_USE_MEMPOOLS TRUE
#endif
/**
@@ -338,7 +338,7 @@
* @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS.
*/
#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__)
#define CH_USE_DYNAMIC TRUE
#define CH_USE_DYNAMIC TRUE
#endif
/** @} */
@@ -358,7 +358,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_SYSTEM_STATE_CHECK TRUE
#define CH_DBG_SYSTEM_STATE_CHECK TRUE
#endif
/**
@@ -369,7 +369,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_CHECKS TRUE
#define CH_DBG_ENABLE_CHECKS TRUE
#endif
/**
@@ -381,7 +381,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_ASSERTS TRUE
#define CH_DBG_ENABLE_ASSERTS TRUE
#endif
/**
@@ -392,7 +392,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_TRACE FALSE
#define CH_DBG_ENABLE_TRACE FALSE
#endif
/**
@@ -406,7 +406,7 @@
* @p panic_msg variable set to @p NULL.
*/
#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_STACK_CHECK TRUE
#define CH_DBG_ENABLE_STACK_CHECK TRUE
#endif
/**
@@ -418,7 +418,7 @@
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__)
#define CH_DBG_FILL_THREADS TRUE
#define CH_DBG_FILL_THREADS TRUE
#endif
/**
@@ -431,7 +431,7 @@
* some test cases into the test suite.
*/
#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
#define CH_DBG_THREADS_PROFILING TRUE
#define CH_DBG_THREADS_PROFILING TRUE
#endif
/** @} */
@@ -448,11 +448,11 @@
* @details User fields added to the end of the @p Thread structure.
*/
#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
#define THREAD_EXT_FIELDS \
/* Add threads custom fields here.*/ \
uint32_t switches; \
uint32_t start_ticks; \
uint32_t total_ticks;
#define THREAD_EXT_FIELDS \
/* Add threads custom fields here.*/ \
uint32_t switches; \
uint32_t start_ticks; \
uint32_t total_ticks;
#endif
/**
@@ -463,12 +463,13 @@
* the threads creation APIs.
*/
#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_INIT_HOOK(tp) { \
/* Add threads initialization code here.*/ \
tp->switches = 0; \
tp->start_ticks = 0; \
tp->total_ticks = 0; \
}
#define THREAD_EXT_INIT_HOOK(tp) \
{ \
/* Add threads initialization code here.*/ \
tp->switches = 0; \
tp->start_ticks = 0; \
tp->total_ticks = 0; \
}
#endif
/**
@@ -480,9 +481,10 @@
* terminate.
*/
#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_EXIT_HOOK(tp) { \
/* Add threads finalization code here.*/ \
}
#define THREAD_EXT_EXIT_HOOK(tp) \
{ \
/* Add threads finalization code here.*/ \
}
#endif
/**
@@ -490,12 +492,13 @@
* @details This hook is invoked just before switching between threads.
*/
#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__)
#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \
/* System halt code here.*/ \
otp->switches++; \
ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \
otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \
}
#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) \
{ \
/* System halt code here.*/ \
otp->switches++; \
ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \
otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \
}
#endif
/**
@@ -503,24 +506,24 @@
* @details This hook is continuously invoked by the idle thread loop.
*/
#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__)
#define IDLE_LOOP_HOOK() { \
/* Idle loop code here.*/ \
}
#define IDLE_LOOP_HOOK() \
{ \
/* Idle loop code here.*/ \
}
#endif
/**
* @brief System tick event hook.
* @details This hook is invoked in the system tick handler immediately
* after processing the virtual timers queue.
*/
#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_TICK_EVENT_HOOK() { \
/* System tick event code here.*/ \
extern void update_performance_counters(); \
update_performance_counters(); \
}
#define SYSTEM_TICK_EVENT_HOOK() \
{ \
/* System tick event code here.*/ \
extern void update_performance_counters(); \
update_performance_counters(); \
}
#endif
/**
@@ -529,9 +532,10 @@
* the system is halted.
*/
#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_HALT_HOOK() { \
/* System halt code here.*/ \
}
#define SYSTEM_HALT_HOOK() \
{ \
/* System halt code here.*/ \
}
#endif
/** @} */
@@ -542,9 +546,9 @@
/* NOTE: When changing this option you also have to enable or disable the FPU
in the project options.*/
#define CORTEX_USE_FPU TRUE
#define CORTEX_ENABLE_WFI_IDLE TRUE
#define CORTEX_USE_FPU TRUE
#define CORTEX_ENABLE_WFI_IDLE TRUE
#endif /* _CHCONF_H_ */
#endif /* _CHCONF_H_ */
/** @} */

View File

@@ -31,155 +31,143 @@
namespace clock_recovery {
class GardnerTimingErrorDetector {
public:
static constexpr size_t samples_per_symbol { 2 };
public:
static constexpr size_t samples_per_symbol{2};
/*
Expects retimed samples at a rate of twice the expected symbol rate.
Calculates timing error, sends symbol and error to handler.
*/
template<typename SymbolHandler>
void operator()(
const float in,
SymbolHandler symbol_handler
) {
/* NOTE: Algorithm is sensitive to input magnitude. Timing error value
* will scale proportionally. Best practice is to use error sign only.
*/
t[2] = t[1];
t[1] = t[0];
t[0] = in;
/*
Expects retimed samples at a rate of twice the expected symbol rate.
Calculates timing error, sends symbol and error to handler.
*/
template <typename SymbolHandler>
void operator()(
const float in,
SymbolHandler symbol_handler) {
/* NOTE: Algorithm is sensitive to input magnitude. Timing error value
* will scale proportionally. Best practice is to use error sign only.
*/
t[2] = t[1];
t[1] = t[0];
t[0] = in;
if( symbol_phase == 0 ) {
const auto symbol = t[0];
const float lateness = (t[0] - t[2]) * t[1];
symbol_handler(symbol, lateness);
}
if (symbol_phase == 0) {
const auto symbol = t[0];
const float lateness = (t[0] - t[2]) * t[1];
symbol_handler(symbol, lateness);
}
symbol_phase = (symbol_phase + 1) % samples_per_symbol;
}
symbol_phase = (symbol_phase + 1) % samples_per_symbol;
}
private:
std::array<float, 3> t { };
size_t symbol_phase { 0 };
private:
std::array<float, 3> t{};
size_t symbol_phase{0};
};
class LinearErrorFilter {
public:
LinearErrorFilter(
const float filter_alpha = 0.95f,
const float error_weight = -1.0f
) : filter_alpha { filter_alpha },
error_weight { error_weight }
{
}
public:
LinearErrorFilter(
const float filter_alpha = 0.95f,
const float error_weight = -1.0f)
: filter_alpha{filter_alpha},
error_weight{error_weight} {
}
float operator()(
const float error
) {
error_filtered = filter_alpha * error_filtered + (1.0f - filter_alpha) * error;
return error_filtered * error_weight;
}
float operator()(
const float error) {
error_filtered = filter_alpha * error_filtered + (1.0f - filter_alpha) * error;
return error_filtered * error_weight;
}
private:
const float filter_alpha;
const float error_weight;
float error_filtered { 0.0f };
private:
const float filter_alpha;
const float error_weight;
float error_filtered{0.0f};
};
class FixedErrorFilter {
public:
FixedErrorFilter(
) {
}
public:
FixedErrorFilter() {
}
FixedErrorFilter(
const float weight
) : weight_ { weight }
{
}
FixedErrorFilter(
const float weight)
: weight_{weight} {
}
float operator()(
const float lateness
) const {
return (lateness < 0.0f) ? weight() : -weight();
}
float operator()(
const float lateness) const {
return (lateness < 0.0f) ? weight() : -weight();
}
float weight() const {
return weight_;
}
float weight() const {
return weight_;
}
private:
float weight_ { 1.0f / 16.0f };
private:
float weight_{1.0f / 16.0f};
};
template<typename ErrorFilter>
template <typename ErrorFilter>
class ClockRecovery {
public:
using SymbolHandler = std::function<void(const float)>;
public:
using SymbolHandler = std::function<void(const float)>;
ClockRecovery(
const float sampling_rate,
const float symbol_rate,
ErrorFilter error_filter,
SymbolHandler symbol_handler
) : symbol_handler { std::move(symbol_handler) }
{
configure(sampling_rate, symbol_rate, error_filter);
}
ClockRecovery(
const float sampling_rate,
const float symbol_rate,
ErrorFilter error_filter,
SymbolHandler symbol_handler)
: symbol_handler{std::move(symbol_handler)} {
configure(sampling_rate, symbol_rate, error_filter);
}
ClockRecovery(
SymbolHandler symbol_handler
) : symbol_handler { std::move(symbol_handler) }
{
}
ClockRecovery(
SymbolHandler symbol_handler)
: symbol_handler{std::move(symbol_handler)} {
}
void configure(
const float sampling_rate,
const float symbol_rate,
ErrorFilter error_filter
) {
resampler.configure(sampling_rate, symbol_rate * timing_error_detector.samples_per_symbol);
error_filter = error_filter;
}
void configure(
const float sampling_rate,
const float symbol_rate,
ErrorFilter error_filter) {
resampler.configure(sampling_rate, symbol_rate * timing_error_detector.samples_per_symbol);
error_filter = error_filter;
}
void operator()(
const float baseband_sample
) {
resampler(baseband_sample,
[this](const float interpolated_sample) {
this->resampler_callback(interpolated_sample);
}
);
}
void operator()(
const float baseband_sample) {
resampler(baseband_sample,
[this](const float interpolated_sample) {
this->resampler_callback(interpolated_sample);
});
}
private:
dsp::interpolation::LinearResampler resampler { };
GardnerTimingErrorDetector timing_error_detector { };
ErrorFilter error_filter { };
const SymbolHandler symbol_handler;
private:
dsp::interpolation::LinearResampler resampler{};
GardnerTimingErrorDetector timing_error_detector{};
ErrorFilter error_filter{};
const SymbolHandler symbol_handler;
void resampler_callback(const float interpolated_sample) {
timing_error_detector(interpolated_sample,
[this](const float symbol, const float lateness) {
this->symbol_callback(symbol, lateness);
}
);
}
void resampler_callback(const float interpolated_sample) {
timing_error_detector(interpolated_sample,
[this](const float symbol, const float lateness) {
this->symbol_callback(symbol, lateness);
});
}
void symbol_callback(const float symbol, const float lateness) {
// NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make symbol_handler known at compile time.
if( symbol_handler) {
symbol_handler(symbol);
}
void symbol_callback(const float symbol, const float lateness) {
// NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make symbol_handler known at compile time.
if (symbol_handler) {
symbol_handler(symbol);
}
const float adjustment = error_filter(lateness);
resampler.advance(adjustment);
}
const float adjustment = error_filter(lateness);
resampler.advance(adjustment);
}
};
} /* namespace clock_recovery */
#endif/*__CLOCK_RECOVERY_H__*/
#endif /*__CLOCK_RECOVERY_H__*/

View File

@@ -28,29 +28,28 @@
#include "portapack_shared_memory.hpp"
#include "performance_counter.hpp"
void write_m4_panic_msg(const char *panic_message, struct extctx *ctxp) {
void write_m4_panic_msg(const char* panic_message, struct extctx* ctxp) {
if (ctxp == nullptr) {
shared_memory.bb_data.data[0] = 0;
}
else {
} else {
shared_memory.bb_data.data[0] = 1;
*((uint32_t *)&shared_memory.bb_data.data[4]) = SCB->CFSR;
*((uint32_t*)&shared_memory.bb_data.data[4]) = SCB->CFSR;
memcpy(&shared_memory.bb_data.data[8], ctxp, sizeof(struct extctx));
}
for(size_t i=0; i<sizeof(shared_memory.m4_panic_msg); i++) {
if( *panic_message == 0 ) {
shared_memory.m4_panic_msg[i] = 0;
} else {
shared_memory.m4_panic_msg[i] = *(panic_message++);
}
}
for (size_t i = 0; i < sizeof(shared_memory.m4_panic_msg); i++) {
if (*panic_message == 0) {
shared_memory.m4_panic_msg[i] = 0;
} else {
shared_memory.m4_panic_msg[i] = *(panic_message++);
}
}
}
extern "C" {
#if CH_DBG_ENABLED
void port_halt(void) {
port_disable();
port_disable();
if (dbg_panic_msg == nullptr)
dbg_panic_msg = "system halted";
@@ -65,80 +64,84 @@ void port_halt(void) {
CH_IRQ_HANDLER(MemManageVector) {
#if CH_DBG_ENABLED
chDbgPanic("MemManage");
chDbgPanic("MemManage");
#else
chSysHalt();
chSysHalt();
#endif
}
CH_IRQ_HANDLER(BusFaultVector) {
#if CH_DBG_ENABLED
chDbgPanic("BusFault");
chDbgPanic("BusFault");
#else
chSysHalt();
chSysHalt();
#endif
}
CH_IRQ_HANDLER(UsageFaultVector) {
#if CH_DBG_ENABLED
chDbgPanic("UsageFault");
chDbgPanic("UsageFault");
#else
chSysHalt();
chSysHalt();
#endif
}
CH_IRQ_HANDLER(HardFaultVector) {
#if CH_DBG_ENABLED
regarm_t _saved_lr;
asm volatile ("mov %0, lr" : "=r" (_saved_lr) : : "memory");
asm volatile("mov %0, lr"
: "=r"(_saved_lr)
:
: "memory");
struct extctx *ctxp;
struct extctx* ctxp;
port_lock_from_isr();
if ((uint32_t)_saved_lr & 0x04)
ctxp = (struct extctx *)__get_PSP();
ctxp = (struct extctx*)__get_PSP();
else
ctxp = (struct extctx *)__get_MSP();
ctxp = (struct extctx*)__get_MSP();
volatile uint32_t stack_space_left = get_free_stack_space();
if (stack_space_left < 16)
write_m4_panic_msg("Stack Overflow", ctxp);
else write_m4_panic_msg("Hard Fault", ctxp);
port_disable();
else
write_m4_panic_msg("Hard Fault", ctxp);
port_disable();
while (true) {
HALT_IF_DEBUGGING();
}
#else
chSysHalt();
chSysHalt();
#endif
}
void update_performance_counters() {
auto performance_counter_active = shared_memory.request_m4_performance_counter;
if (performance_counter_active == 0x00)
return;
static bool last_paint_state = false;
if ((((chTimeNow()>>10) & 0x01) == 0x01) == last_paint_state)
return;
// Idle thread state is sometimes unuseable
if (chThdGetTicks(chSysGetIdleThread()) > 0x10000000)
auto performance_counter_active = shared_memory.request_m4_performance_counter;
if (performance_counter_active == 0x00)
return;
last_paint_state = !last_paint_state;
static bool last_paint_state = false;
if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state)
return;
auto utilisation = get_cpu_utilisation_in_percent();
auto free_stack = (uint32_t)get_free_stack_space();
auto free_heap = chCoreStatus();
// Idle thread state is sometimes unuseable
if (chThdGetTicks(chSysGetIdleThread()) > 0x10000000)
return;
shared_memory.m4_cpu_usage = utilisation;
shared_memory.m4_stack_usage = free_stack;
shared_memory.m4_heap_usage = free_heap;
last_paint_state = !last_paint_state;
auto utilisation = get_cpu_utilisation_in_percent();
auto free_stack = (uint32_t)get_free_stack_space();
auto free_heap = chCoreStatus();
shared_memory.m4_cpu_usage = utilisation;
shared_memory.m4_stack_usage = free_stack;
shared_memory.m4_heap_usage = free_heap;
}
} /* extern "C" */

View File

@@ -26,27 +26,29 @@
extern uint32_t __process_stack_base__;
extern uint32_t __process_stack_end__;
#define CRT0_STACKS_FILL_PATTERN 0x55555555
#define CRT0_STACKS_FILL_PATTERN 0x55555555
inline uint32_t get_free_stack_space(){
uint32_t *p;
for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++);
inline uint32_t get_free_stack_space() {
uint32_t* p;
for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++)
;
auto stack_space_left = p - &__process_stack_base__;
return stack_space_left;
}
/* Executes a breakpoint only when a debugger is attached. */
#define HALT_IF_DEBUGGING() \
do { \
if ((*(volatile uint32_t *)0xE000EDF0) & (1 << 0)) { \
__asm__ __volatile__("bkpt 1"); \
} \
} while (0)
#define HALT_IF_DEBUGGING() \
do { \
if ((*(volatile uint32_t*)0xE000EDF0) & (1 << 0)) { \
__asm__ __volatile__("bkpt 1"); \
} \
} while (0)
/* Stops execution until a debugger is attached. */
#define HALT_UNTIL_DEBUGGING() \
while (!((*(volatile uint32_t *)0xE000EDF0) & (1 << 0))) {} \
__asm__ __volatile__("bkpt 1")
while (!((*(volatile uint32_t*)0xE000EDF0) & (1 << 0))) { \
} \
__asm__ __volatile__("bkpt 1")
#endif/*__DEBUG_H__*/
#endif /*__DEBUG_H__*/

File diff suppressed because it is too large Load Diff

View File

@@ -37,228 +37,210 @@ namespace dsp {
namespace decimate {
class Complex8DecimateBy2CIC3 {
public:
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
public:
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst);
private:
uint32_t _i1_i0 { 0 };
uint32_t _q1_q0 { 0 };
private:
uint32_t _i1_i0{0};
uint32_t _q1_q0{0};
};
class TranslateByFSOver4AndDecimateBy2CIC3 {
public:
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
public:
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst);
private:
uint32_t _q1_i0 { 0 };
uint32_t _q0_i1 { 0 };
private:
uint32_t _q1_i0{0};
uint32_t _q0_i1{0};
};
class DecimateBy2CIC3 {
public:
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
public:
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst);
private:
uint32_t _iq0 { 0 };
uint32_t _iq1 { 0 };
private:
uint32_t _iq0{0};
uint32_t _iq1{0};
};
class FIR64AndDecimateBy2Real {
public:
static constexpr size_t taps_count = 64;
public:
static constexpr size_t taps_count = 64;
void configure(
const std::array<int16_t, taps_count>& taps
);
void configure(
const std::array<int16_t, taps_count>& taps);
buffer_s16_t execute(
const buffer_s16_t& src,
const buffer_s16_t& dst
);
buffer_s16_t execute(
const buffer_s16_t& src,
const buffer_s16_t& dst);
private:
std::array<int16_t, taps_count + 2> z { };
std::array<int16_t, taps_count> taps { };
private:
std::array<int16_t, taps_count + 2> z{};
std::array<int16_t, taps_count> taps{};
};
class FIRC8xR16x24FS4Decim4 {
public:
static constexpr size_t taps_count = 24;
static constexpr size_t decimation_factor = 4;
public:
static constexpr size_t taps_count = 24;
static constexpr size_t decimation_factor = 4;
using sample_t = complex8_t;
using tap_t = int16_t;
using sample_t = complex8_t;
using tap_t = int16_t;
enum class Shift : bool {
Down = true,
Up = false
};
enum class Shift : bool {
Down = true,
Up = false
};
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift = Shift::Down
);
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift = Shift::Down);
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
std::array<tap_t, taps_count> taps_ { };
int32_t output_scale = 0;
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_{};
std::array<tap_t, taps_count> taps_{};
int32_t output_scale = 0;
};
class FIRC8xR16x24FS4Decim8 {
public:
static constexpr size_t taps_count = 24;
static constexpr size_t decimation_factor = 8;
public:
static constexpr size_t taps_count = 24;
static constexpr size_t decimation_factor = 8;
using sample_t = complex8_t;
using tap_t = int16_t;
using sample_t = complex8_t;
using tap_t = int16_t;
enum class Shift : bool {
Down = true,
Up = false
};
enum class Shift : bool {
Down = true,
Up = false
};
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift = Shift::Down
);
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift = Shift::Down);
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
std::array<tap_t, taps_count> taps_ { };
int32_t output_scale = 0;
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_{};
std::array<tap_t, taps_count> taps_{};
int32_t output_scale = 0;
};
class FIRC16xR16x16Decim2 {
public:
static constexpr size_t taps_count = 16;
static constexpr size_t decimation_factor = 2;
public:
static constexpr size_t taps_count = 16;
static constexpr size_t decimation_factor = 2;
using sample_t = complex16_t;
using tap_t = int16_t;
using sample_t = complex16_t;
using tap_t = int16_t;
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale
);
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale);
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
std::array<tap_t, taps_count> taps_ { };
int32_t output_scale = 0;
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_{};
std::array<tap_t, taps_count> taps_{};
int32_t output_scale = 0;
};
class FIRC16xR16x32Decim8 {
public:
static constexpr size_t taps_count = 32;
static constexpr size_t decimation_factor = 8;
public:
static constexpr size_t taps_count = 32;
static constexpr size_t decimation_factor = 8;
using sample_t = complex16_t;
using tap_t = int16_t;
using sample_t = complex16_t;
using tap_t = int16_t;
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale
);
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale);
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
std::array<tap_t, taps_count> taps_ { };
int32_t output_scale = 0;
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_{};
std::array<tap_t, taps_count> taps_{};
int32_t output_scale = 0;
};
class FIRAndDecimateComplex {
public:
using sample_t = complex16_t;
using tap_t = complex16_t;
public:
using sample_t = complex16_t;
using tap_t = complex16_t;
using taps_t = tap_t[];
using taps_t = tap_t[];
/* NOTE! Current code makes an assumption that block of samples to be
* processed will be a multiple of the taps_count.
*/
/* NOTE! Current code makes an assumption that block of samples to be
* processed will be a multiple of the taps_count.
*/
template<typename T>
void configure(
const T& taps,
const size_t decimation_factor
) {
configure(taps.data(), taps.size(), decimation_factor);
}
template <typename T>
void configure(
const T& taps,
const size_t decimation_factor) {
configure(taps.data(), taps.size(), decimation_factor);
}
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
using samples_t = sample_t[];
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst);
std::unique_ptr<samples_t> samples_ { };
std::unique_ptr<taps_t> taps_reversed_ { };
size_t taps_count_ { 0 };
size_t decimation_factor_ { 1 };
private:
using samples_t = sample_t[];
template<typename T>
void configure(
const T* const taps,
const size_t taps_count,
const size_t decimation_factor
) {
configure_common(taps_count, decimation_factor);
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
}
std::unique_ptr<samples_t> samples_{};
std::unique_ptr<taps_t> taps_reversed_{};
size_t taps_count_{0};
size_t decimation_factor_{1};
void configure_common(
const size_t taps_count,
const size_t decimation_factor
);
template <typename T>
void configure(
const T* const taps,
const size_t taps_count,
const size_t decimation_factor) {
configure_common(taps_count, decimation_factor);
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
}
void configure_common(
const size_t taps_count,
const size_t decimation_factor);
};
class DecimateBy2CIC4Real {
public:
buffer_s16_t execute(
const buffer_s16_t& src,
const buffer_s16_t& dst
);
public:
buffer_s16_t execute(
const buffer_s16_t& src,
const buffer_s16_t& dst);
private:
int16_t z[5] { };
int16_t _dummy { }; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0?
private:
int16_t z[5]{};
int16_t _dummy{}; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0?
};
} /* namespace decimate */
} /* namespace dsp */
#endif/*__DSP_DECIMATE_H__*/
#endif /*__DSP_DECIMATE_H__*/

View File

@@ -31,121 +31,116 @@ namespace dsp {
namespace demodulate {
buffer_f32_t AM::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
const void* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const uint32_t sample0 = *__SIMD32(src_p)++;
const uint32_t sample1 = *__SIMD32(src_p)++;
const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
*(dst_p++) = __builtin_sqrtf(mag_sq0) * k;
*(dst_p++) = __builtin_sqrtf(mag_sq1) * k;
}
const buffer_c16_t& src,
const buffer_f32_t& dst) {
const void* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while (src_p < src_end) {
const uint32_t sample0 = *__SIMD32(src_p)++;
const uint32_t sample1 = *__SIMD32(src_p)++;
const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
*(dst_p++) = __builtin_sqrtf(mag_sq0) * k;
*(dst_p++) = __builtin_sqrtf(mag_sq1) * k;
}
return { dst.p, src.count, src.sampling_rate };
return {dst.p, src.count, src.sampling_rate};
}
buffer_f32_t SSB::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
const complex16_t* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
}
const buffer_c16_t& src,
const buffer_f32_t& dst) {
const complex16_t* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while (src_p < src_end) {
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
}
return { dst.p, src.count, src.sampling_rate };
return {dst.p, src.count, src.sampling_rate};
}
/*
static inline float angle_approx_4deg0(const complex32_t t) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return 16384.0f * x;
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return 16384.0f * x;
}
*/
static inline float angle_approx_0deg27(const complex32_t t) {
if( t.real() ) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return x / (1.0f + 0.28086f * x * x);
} else {
return (t.imag() < 0) ? -1.5707963268f : 1.5707963268f;
}
if (t.real()) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return x / (1.0f + 0.28086f * x * x);
} else {
return (t.imag() < 0) ? -1.5707963268f : 1.5707963268f;
}
}
static inline float angle_precise(const complex32_t t) {
return atan2f(t.imag(), t.real());
return atan2f(t.imag(), t.real());
}
buffer_f32_t FM::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
auto z = z_;
const buffer_c16_t& src,
const buffer_f32_t& dst) {
auto z = z_;
const void* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
*(dst_p++) = angle_precise(t0) * kf;
*(dst_p++) = angle_precise(t1) * kf;
}
z_ = z;
const void* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while (src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
*(dst_p++) = angle_precise(t0) * kf;
*(dst_p++) = angle_precise(t1) * kf;
}
z_ = z;
return { dst.p, src.count, src.sampling_rate };
return {dst.p, src.count, src.sampling_rate};
}
buffer_s16_t FM::execute(
const buffer_c16_t& src,
const buffer_s16_t& dst
) {
auto z = z_;
const buffer_c16_t& src,
const buffer_s16_t& dst) {
auto z = z_;
const void* src_p = src.p;
const auto src_end = &src.p[src.count];
void* dst_p = dst.p;
while(src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
const int32_t theta0_int = angle_approx_0deg27(t0) * ks16;
const int32_t theta0_sat = __SSAT(theta0_int, 16);
const int32_t theta1_int = angle_approx_0deg27(t1) * ks16;
const int32_t theta1_sat = __SSAT(theta1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT(
theta0_sat,
theta1_sat,
16
);
}
z_ = z;
const void* src_p = src.p;
const auto src_end = &src.p[src.count];
void* dst_p = dst.p;
while (src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
const int32_t theta0_int = angle_approx_0deg27(t0) * ks16;
const int32_t theta0_sat = __SSAT(theta0_int, 16);
const int32_t theta1_int = angle_approx_0deg27(t1) * ks16;
const int32_t theta1_sat = __SSAT(theta1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT(
theta0_sat,
theta1_sat,
16);
}
z_ = z;
return { dst.p, src.count, src.sampling_rate };
return {dst.p, src.count, src.sampling_rate};
}
void FM::configure(const float sampling_rate, const float deviation_hz) {
/*
* angle: -pi to pi. output range: -32768 to 32767.
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
* delta_theta_max = 2 * pi * deviation / sampling_rate
*/
kf = static_cast<float>(1.0f / (2.0 * pi * deviation_hz / sampling_rate));
ks16 = 32767.0f * kf;
/*
* angle: -pi to pi. output range: -32768 to 32767.
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
* delta_theta_max = 2 * pi * deviation / sampling_rate
*/
kf = static_cast<float>(1.0f / (2.0 * pi * deviation_hz / sampling_rate));
ks16 = 32767.0f * kf;
}
}
}
} // namespace demodulate
} // namespace dsp

View File

@@ -28,48 +28,44 @@ namespace dsp {
namespace demodulate {
class AM {
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst);
private:
static constexpr float k = 1.0f / 32768.0f;
private:
static constexpr float k = 1.0f / 32768.0f;
};
class SSB {
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst);
private:
static constexpr float k = 1.0f / 32768.0f;
private:
static constexpr float k = 1.0f / 32768.0f;
};
class FM {
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst);
buffer_s16_t execute(
const buffer_c16_t& src,
const buffer_s16_t& dst
);
buffer_s16_t execute(
const buffer_c16_t& src,
const buffer_s16_t& dst);
void configure(const float sampling_rate, const float deviation_hz);
void configure(const float sampling_rate, const float deviation_hz);
private:
complex16_t::rep_type z_ { 0 };
float kf { 0 };
float ks16 { 0 };
private:
complex16_t::rep_type z_{0};
float kf{0};
float ks16{0};
};
} /* namespace demodulate */
} /* namespace dsp */
#endif/*__DSP_DEMODULATE_H__*/
#endif /*__DSP_DEMODULATE_H__*/

View File

@@ -28,29 +28,26 @@
namespace dsp {
GoertzelDetector::GoertzelDetector(
const float frequency,
const uint32_t sample_rate
) {
coefficient = 2.0 * sin_f32((2.0 * pi * frequency / sample_rate) - pi / 2.0);
const float frequency,
const uint32_t sample_rate) {
coefficient = 2.0 * sin_f32((2.0 * pi * frequency / sample_rate) - pi / 2.0);
}
float GoertzelDetector::execute(
const buffer_s16_t& src
) {
const buffer_s16_t& src) {
const size_t count = src.count;
const size_t count = src.count;
for (size_t i = 0; i < count; i++) {
s[2] = s[1];
s[1] = s[0];
s[0] = src.p[i] + coefficient * s[1] - s[2];
}
for (size_t i = 0; i < count; i++) {
s[2] = s[1];
s[1] = s[0];
s[0] = src.p[i] + coefficient * s[1] - s[2];
}
const uint32_t sq0 = s[0] * s[0];
const uint32_t sq1 = s[1] * s[1];
float magnitude = __builtin_sqrtf(sq0 + sq1 - s[0] * s[1] * coefficient);
const uint32_t sq0 = s[0] * s[0];
const uint32_t sq1 = s[1] * s[1];
float magnitude = __builtin_sqrtf(sq0 + sq1 - s[0] * s[1] * coefficient);
return magnitude;
return magnitude;
}
} /* namespace dsp */

View File

@@ -28,16 +28,16 @@
namespace dsp {
class GoertzelDetector {
public:
GoertzelDetector(const float frequency, const uint32_t sample_rate);
float execute(const buffer_s16_t& src);
public:
GoertzelDetector(const float frequency, const uint32_t sample_rate);
private:
float coefficient { };
int16_t s[2] { 0 };
float execute(const buffer_s16_t& src);
private:
float coefficient{};
int16_t s[2]{0};
};
} /* namespace dsp */
#endif/*__DSP_GOERTZEL_H__*/
#endif /*__DSP_GOERTZEL_H__*/

View File

@@ -25,33 +25,57 @@
namespace dsp {
HilbertTransform::HilbertTransform() {
n = 0;
n = 0;
sos_i.configure(half_band_lpf_config);
sos_q.configure(half_band_lpf_config);
sos_i.configure(half_band_lpf_config);
sos_q.configure(half_band_lpf_config);
}
void HilbertTransform::execute(float in, float &out_i, float &out_q) {
float a = 0, b = 0;
switch (n) {
case 0: a = in; b = 0; break;
case 1: a = 0; b = -in; break;
case 2: a = -in; b = 0; break;
case 3: a = 0; b = in; break;
}
float i = sos_i.execute(a) * 2.0f;
float q = sos_q.execute(b) * 2.0f;
switch (n) {
case 0: out_i = i; out_q = q; break;
case 1: out_i = -q; out_q = i; break;
case 2: out_i = -i; out_q = -q; break;
case 3: out_i = q; out_q = -i; break;
}
void HilbertTransform::execute(float in, float& out_i, float& out_q) {
float a = 0, b = 0;
n = (n + 1) % 4;
switch (n) {
case 0:
a = in;
b = 0;
break;
case 1:
a = 0;
b = -in;
break;
case 2:
a = -in;
b = 0;
break;
case 3:
a = 0;
b = in;
break;
}
float i = sos_i.execute(a) * 2.0f;
float q = sos_q.execute(b) * 2.0f;
switch (n) {
case 0:
out_i = i;
out_q = q;
break;
case 1:
out_i = -q;
out_q = i;
break;
case 2:
out_i = -i;
out_q = -q;
break;
case 3:
out_i = q;
out_q = -i;
break;
}
n = (n + 1) % 4;
}
} /* namespace dsp */

View File

@@ -28,17 +28,16 @@
namespace dsp {
class HilbertTransform {
public:
public:
HilbertTransform();
void execute(float in, float& out_i, float& out_q);
HilbertTransform();
void execute(float in, float &out_i, float &out_q);
private:
uint8_t n = 0;
SOSFilter<5> sos_i = {};
SOSFilter<5> sos_q = {};
private:
uint8_t n = 0;
SOSFilter<5> sos_i = {};
SOSFilter<5> sos_q = {};
};
} /* namespace dsp */
#endif/*__DSP_HILBERT_H__*/
#endif /*__DSP_HILBERT_H__*/

View File

@@ -31,207 +31,219 @@ Modulator::~Modulator() {
}
Mode Modulator::get_mode() {
return mode;
return mode;
}
void Modulator::set_mode(Mode new_mode) {
mode = new_mode;
mode = new_mode;
}
void Modulator::set_over(uint32_t new_over) {
over = new_over;
}
void Modulator::set_gain_shiftbits_vumeter_beep(float new_audio_gain ,uint8_t new_audio_shift_bits_s16, bool new_play_beep ) {
//new_audio_shift_bits_s16 are the direct shift bits (FM mod >>x) , and it is fixed to >>8_FM (AK) or 4,5,6, (WM boost OFF) or 6,7 (WM boost ON)
audio_gain = new_audio_gain ;
audio_shift_bits_s16_FM = new_audio_shift_bits_s16; //FM : >>8(AK) fixed , >>4,5,6 (WM boost OFF)
if (new_audio_shift_bits_s16==8) { //FM : we are in AK codec IC => for AM-SSB-DSB we were using >>2 fixed (wm boost ON) .
audio_shift_bits_s16_AM_DSB_SSB = 2; //AM-DSB-SSB: >>2(AK) fixed , >>0,1,2 (WM boost OFF)
} else {
audio_shift_bits_s16_AM_DSB_SSB = (new_audio_shift_bits_s16-4) ; //AM-DSB-SSB: >>0,1,2 (WM boost OFF), >>2,3 (WM boost ON)
}
play_beep = new_play_beep;
void Modulator::set_gain_shiftbits_vumeter_beep(float new_audio_gain, uint8_t new_audio_shift_bits_s16, bool new_play_beep) {
// new_audio_shift_bits_s16 are the direct shift bits (FM mod >>x) , and it is fixed to >>8_FM (AK) or 4,5,6, (WM boost OFF) or 6,7 (WM boost ON)
audio_gain = new_audio_gain;
audio_shift_bits_s16_FM = new_audio_shift_bits_s16; // FM : >>8(AK) fixed , >>4,5,6 (WM boost OFF)
if (new_audio_shift_bits_s16 == 8) { // FM : we are in AK codec IC => for AM-SSB-DSB we were using >>2 fixed (wm boost ON) .
audio_shift_bits_s16_AM_DSB_SSB = 2; // AM-DSB-SSB: >>2(AK) fixed , >>0,1,2 (WM boost OFF)
} else {
audio_shift_bits_s16_AM_DSB_SSB = (new_audio_shift_bits_s16 - 4); // AM-DSB-SSB: >>0,1,2 (WM boost OFF), >>2,3 (WM boost ON)
}
play_beep = new_play_beep;
}
int32_t Modulator::apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message ) {
int32_t Modulator::apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message) {
if (play_beep) { // We need to add audio beep sample.
if (new_beep_timer) {
new_beep_timer--;
} else {
new_beep_timer = baseband_fs * 0.05; // 50ms
if (play_beep) { // We need to add audio beep sample.
if (new_beep_timer) {
new_beep_timer--;
} else {
new_beep_timer = baseband_fs * 0.05; // 50ms
if (new_beep_index == BEEP_TONES_NB) {
configured_in = false;
shared_memory.application_queue.push(new_txprogress_message);
} else {
beep_gen.configure(beep_deltas[new_beep_index], 1.0); // config sequentially the audio beep tone.
new_beep_index++;
}
}
sample_in = beep_gen.process(0); // Get sample of the selected sequence of 6 beep tones , and overwrite audio sample. Mix 0%.
}
return sample_in; // Return audio mic scaled with gain , 8 bit sample or audio beep sample.
if (new_beep_index == BEEP_TONES_NB) {
configured_in = false;
shared_memory.application_queue.push(new_txprogress_message);
} else {
beep_gen.configure(beep_deltas[new_beep_index], 1.0); // config sequentially the audio beep tone.
new_beep_index++;
}
}
sample_in = beep_gen.process(0); // Get sample of the selected sequence of 6 beep tones , and overwrite audio sample. Mix 0%.
}
return sample_in; // Return audio mic scaled with gain , 8 bit sample or audio beep sample.
}
///
SSB::SSB() : hilbert() {
mode = Mode::LSB;
SSB::SSB()
: hilbert() {
mode = Mode::LSB;
}
void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer,TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
//unused
(void)configured_in ;
(void)new_beep_index ;
(void)new_beep_timer ;
(void)new_txprogress_message ;
void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) {
// unused
(void)configured_in;
(void)new_beep_index;
(void)new_beep_timer;
(void)new_txprogress_message;
// No way to activate correctly the roger beep in this option, Maybe not enough M4 CPU power , Let's block roger beep in SSB selection by now .
int32_t sample = 0;
int8_t re = 0, im = 0;
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % 128 == 0) {
float i = 0.0, q = 0.0;
// No way to activate correctly the roger beep in this option, Maybe not enough M4 CPU power , Let's block roger beep in SSB selection by now .
int32_t sample = 0;
int8_t re = 0, im = 0;
sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off)
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
//switch (mode) {
//case Mode::LSB:
hilbert.execute(sample / 32768.0f, i, q);
//case Mode::USB: hilbert.execute(sample / 32768.0f, q, i);
//default: break;
//}
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % 128 == 0) {
float i = 0.0, q = 0.0;
i *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
q *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
switch (mode) {
case Mode::LSB: re = q; im = i; break;
case Mode::USB: re = i; im = q; break;
default: re = 0; im = 0; break;
}
//re = q;
//im = i;
//break;
}
buffer.p[counter] = { re, im };
// Update vu-meter bar in the LCD screen.
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (new_power_acc_count) {
new_power_acc_count--;
} else { // power_acc_count = 0
new_power_acc_count = new_divider;
new_level_message.value = power_acc / (new_divider *8); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter .
shared_memory.application_queue.push(new_level_message);
power_acc = 0;
}
}
sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off)
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
// switch (mode) {
// case Mode::LSB:
hilbert.execute(sample / 32768.0f, i, q);
// case Mode::USB: hilbert.execute(sample / 32768.0f, q, i);
// default: break;
// }
i *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
q *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
switch (mode) {
case Mode::LSB:
re = q;
im = i;
break;
case Mode::USB:
re = i;
im = q;
break;
default:
re = 0;
im = 0;
break;
}
// re = q;
// im = i;
// break;
}
buffer.p[counter] = {re, im};
// Update vu-meter bar in the LCD screen.
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (new_power_acc_count) {
new_power_acc_count--;
} else { // power_acc_count = 0
new_power_acc_count = new_divider;
new_level_message.value = power_acc / (new_divider * 8); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter .
shared_memory.application_queue.push(new_level_message);
power_acc = 0;
}
}
}
///
FM::FM() {
mode = Mode::FM;
mode = Mode::FM;
}
void FM::set_fm_delta(uint32_t new_delta) {
fm_delta = new_delta;
fm_delta = new_delta;
}
void FM::set_tone_gen_configure(const uint32_t set_delta, const float set_tone_mix_weight) {
tone_gen.configure(set_delta, set_tone_mix_weight);
}
tone_gen.configure(set_delta, set_tone_mix_weight);
}
void FM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
int32_t sample = 0;
int8_t re, im;
void FM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) {
int32_t sample = 0;
int8_t re, im;
for (size_t counter = 0; counter < buffer.count; counter++) {
sample = audio.p[counter>>6] >> audio_shift_bits_s16_FM ; // Orig. >>8 , sample = audio.p[counter / over] >> 8; (not enough efficient running code, over = 1536000/240000= 64 )
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
for (size_t counter = 0; counter < buffer.count; counter++) {
sample = audio.p[counter >> 6] >> audio_shift_bits_s16_FM; // Orig. >>8 , sample = audio.p[counter / over] >> 8; (not enough efficient running code, over = 1536000/240000= 64 )
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
if (play_beep) {
sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message ); // Apply beep -if selected - atom ,sample by sample.
} else {
// Update vu-meter bar in the LCD screen.
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (new_power_acc_count) {
new_power_acc_count--;
} else { // power_acc_count = 0
new_power_acc_count = new_divider;
new_level_message.value = power_acc / (new_divider / 4); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter .
shared_memory.application_queue.push(new_level_message);
power_acc = 0;
}
// TODO: pending to optimize CPU running code.
// So far , we can not handle all 3 issues at the same time (vu-meter , CTCSS, beep).
sample = tone_gen.process(sample); // Add selected Key_Tone or CTCSS subtone , atom function() , sample by sample.
}
delta = sample * fm_delta; // Modulate FM
if (play_beep) {
sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message); // Apply beep -if selected - atom ,sample by sample.
} else {
// Update vu-meter bar in the LCD screen.
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
phase += delta;
sphase = phase >> 24;
if (new_power_acc_count) {
new_power_acc_count--;
} else { // power_acc_count = 0
new_power_acc_count = new_divider;
new_level_message.value = power_acc / (new_divider / 4); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter .
shared_memory.application_queue.push(new_level_message);
power_acc = 0;
}
// TODO: pending to optimize CPU running code.
// So far , we can not handle all 3 issues at the same time (vu-meter , CTCSS, beep).
sample = tone_gen.process(sample); // Add selected Key_Tone or CTCSS subtone , atom function() , sample by sample.
}
re = (sine_table_i8[(sphase + 64) & 255]);
im = (sine_table_i8[sphase]);
buffer.p[counter] = { re, im };
}
delta = sample * fm_delta; // Modulate FM
phase += delta;
sphase = phase >> 24;
re = (sine_table_i8[(sphase + 64) & 255]);
im = (sine_table_i8[sphase]);
buffer.p[counter] = {re, im};
}
}
AM::AM() {
mode = Mode::AM;
mode = Mode::AM;
}
void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
int32_t sample = 0;
int8_t re = 0, im = 0;
float q = 0.0;
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % 128 == 0) {
sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off)
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
}
void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) {
int32_t sample = 0;
int8_t re = 0, im = 0;
float q = 0.0;
if (play_beep) {
sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message )<<5; // Apply beep -if selected - atom sample by sample.
} else {
// Update vu-meter bar in the LCD screen.
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (new_power_acc_count) {
new_power_acc_count--;
} else { // power_acc_count = 0
new_power_acc_count = new_divider;
new_level_message.value = power_acc / (new_divider *8); // Why ?orig / (new_divider / 4); // Why ?
shared_memory.application_queue.push(new_level_message);
power_acc = 0;
}
}
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % 128 == 0) {
sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off)
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
}
q = sample / 32768.0f;
q *= 256.0f; // Original 64.0f,now x4 (+12 dB's BB_modulation in AM & DSB)
switch (mode) {
case Mode::AM: re = q + 80; im = q + 80; break; // Original DC add +20_DC_level=carrier,now x4 (+12dB's AM carrier)
case Mode::DSB: re = q; im = q; break;
default: break;
}
buffer.p[counter] = { re, im };
}
if (play_beep) {
sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message) << 5; // Apply beep -if selected - atom sample by sample.
} else {
// Update vu-meter bar in the LCD screen.
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (new_power_acc_count) {
new_power_acc_count--;
} else { // power_acc_count = 0
new_power_acc_count = new_divider;
new_level_message.value = power_acc / (new_divider * 8); // Why ?orig / (new_divider / 4); // Why ?
shared_memory.application_queue.push(new_level_message);
power_acc = 0;
}
}
q = sample / 32768.0f;
q *= 256.0f; // Original 64.0f,now x4 (+12 dB's BB_modulation in AM & DSB)
switch (mode) {
case Mode::AM:
re = q + 80;
im = q + 80;
break; // Original DC add +20_DC_level=carrier,now x4 (+12dB's AM carrier)
case Mode::DSB:
re = q;
im = q;
break;
default:
break;
}
buffer.p[counter] = {re, im};
}
}
}
}
} // namespace modulate
} // namespace dsp

View File

@@ -31,89 +31,86 @@ namespace dsp {
namespace modulate {
enum class Mode {
None,
AM,
DSB,
LSB,
USB,
FM
None,
AM,
DSB,
LSB,
USB,
FM
};
///
class Modulator {
public:
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer,bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) = 0;
virtual ~Modulator();
public:
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) = 0;
virtual ~Modulator();
Mode get_mode();
void set_mode(Mode new_mode);
Mode get_mode();
void set_mode(Mode new_mode);
void set_over(uint32_t new_over);
void set_gain_shiftbits_vumeter_beep(float new_audio_gain ,uint8_t new_audio_shift_bits_s16, bool new_play_beep );
int32_t apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message );
float audio_gain { };
uint8_t audio_shift_bits_s16_FM { }; // shift bits factor to the captured ADC S16 audio sample.
uint8_t audio_shift_bits_s16_AM_DSB_SSB { };
bool play_beep { false };
uint32_t power_acc_count { 0 }; // this var it is initialized from Proc_mictx.cpp
uint32_t divider { }; // this var it is initialized from Proc_mictx.cpp
uint64_t power_acc { 0 }; // it is aux Accumulated sum (Absolute sample signal) , initialitzed to zero.
AudioLevelReportMessage level_message { };
void set_gain_shiftbits_vumeter_beep(float new_audio_gain, uint8_t new_audio_shift_bits_s16, bool new_play_beep);
int32_t apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message);
float audio_gain{};
uint8_t audio_shift_bits_s16_FM{}; // shift bits factor to the captured ADC S16 audio sample.
uint8_t audio_shift_bits_s16_AM_DSB_SSB{};
bool play_beep{false};
uint32_t power_acc_count{0}; // this var it is initialized from Proc_mictx.cpp
uint32_t divider{}; // this var it is initialized from Proc_mictx.cpp
uint64_t power_acc{0}; // it is aux Accumulated sum (Absolute sample signal) , initialitzed to zero.
AudioLevelReportMessage level_message{};
private:
static constexpr size_t baseband_fs = 1536000U;
TXProgressMessage txprogress_message { };
ToneGen beep_gen { };
uint32_t beep_index { }, beep_timer { };
private:
static constexpr size_t baseband_fs = 1536000U;
TXProgressMessage txprogress_message{};
ToneGen beep_gen{};
uint32_t beep_index{}, beep_timer{};
protected:
uint32_t over = 1;
Mode mode = Mode::None;
protected:
uint32_t over = 1;
Mode mode = Mode::None;
};
///
class SSB : public Modulator {
public:
SSB();
public:
SSB();
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider );
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider);
private:
dsp::HilbertTransform hilbert;
private:
dsp::HilbertTransform hilbert;
};
///
class FM : public Modulator {
public:
FM();
public:
FM();
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) ;
void set_fm_delta(uint32_t new_delta);
void set_tone_gen_configure(const uint32_t delta, const float tone_mix_weight);
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider);
void set_fm_delta(uint32_t new_delta);
void set_tone_gen_configure(const uint32_t delta, const float tone_mix_weight);
///
private:
uint32_t fm_delta { 0 };
uint32_t phase { 0 }, sphase { 0 };
int32_t sample { 0 }, delta { };
ToneGen tone_gen { };
///
private:
uint32_t fm_delta{0};
uint32_t phase{0}, sphase{0};
int32_t sample{0}, delta{};
ToneGen tone_gen{};
};
class AM : public Modulator {
public:
AM();
public:
AM();
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider );
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider);
};
} /* namespace modulate */
} /* namespace dsp */
#endif/*__DSP_MODULATE_H__*/
#endif /*__DSP_MODULATE_H__*/

View File

@@ -25,29 +25,28 @@
#include <array>
bool FMSquelch::execute(const buffer_f32_t& audio) {
if( threshold_squared == 0.0f ) {
return true;
}
if (threshold_squared == 0.0f) {
return true;
}
// TODO: No hard-coded array size.
std::array<float, N> squelch_energy_buffer;
const buffer_f32_t squelch_energy {
squelch_energy_buffer.data(),
squelch_energy_buffer.size()
};
non_audio_hpf.execute(audio, squelch_energy);
// TODO: No hard-coded array size.
std::array<float, N> squelch_energy_buffer;
const buffer_f32_t squelch_energy{
squelch_energy_buffer.data(),
squelch_energy_buffer.size()};
non_audio_hpf.execute(audio, squelch_energy);
float non_audio_max_squared = 0;
for(const auto sample : squelch_energy_buffer) {
const float sample_squared = sample * sample;
if( sample_squared > non_audio_max_squared ) {
non_audio_max_squared = sample_squared;
}
}
float non_audio_max_squared = 0;
for (const auto sample : squelch_energy_buffer) {
const float sample_squared = sample * sample;
if (sample_squared > non_audio_max_squared) {
non_audio_max_squared = sample_squared;
}
}
return (non_audio_max_squared < threshold_squared);
return (non_audio_max_squared < threshold_squared);
}
void FMSquelch::set_threshold(const float new_value) {
threshold_squared = new_value * new_value;
threshold_squared = new_value * new_value;
}

View File

@@ -30,16 +30,16 @@
#include <cstddef>
class FMSquelch {
public:
bool execute(const buffer_f32_t& audio);
public:
bool execute(const buffer_f32_t& audio);
void set_threshold(const float new_value);
void set_threshold(const float new_value);
private:
static constexpr size_t N = 32;
float threshold_squared { 0.0f };
private:
static constexpr size_t N = 32;
float threshold_squared{0.0f};
IIRBiquadFilter non_audio_hpf { non_audio_hpf_config };
IIRBiquadFilter non_audio_hpf{non_audio_hpf_config};
};
#endif/*__DSP_SQUELCH_H__*/
#endif /*__DSP_SQUELCH_H__*/

View File

@@ -37,88 +37,85 @@ using namespace lpc43xx;
extern "C" {
CH_IRQ_HANDLER(MAPP_IRQHandler) {
CH_IRQ_PROLOGUE();
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND);
chSysUnlockFromIsr();
chSysLockFromIsr();
EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND);
chSysUnlockFromIsr();
creg::m0apptxevent::clear();
creg::m0apptxevent::clear();
CH_IRQ_EPILOGUE();
CH_IRQ_EPILOGUE();
}
}
Thread* EventDispatcher::thread_event_loop = nullptr;
EventDispatcher::EventDispatcher(
std::unique_ptr<BasebandProcessor> baseband_processor
) : baseband_processor { std::move(baseband_processor) }
{
std::unique_ptr<BasebandProcessor> baseband_processor)
: baseband_processor{std::move(baseband_processor)} {
}
void EventDispatcher::run() {
thread_event_loop = chThdSelf();
thread_event_loop = chThdSelf();
lpc43xx::creg::m0apptxevent::enable();
lpc43xx::creg::m0apptxevent::enable();
while(is_running) {
const auto events = wait();
dispatch(events);
}
while (is_running) {
const auto events = wait();
dispatch(events);
}
lpc43xx::creg::m0apptxevent::disable();
lpc43xx::creg::m0apptxevent::disable();
}
void EventDispatcher::request_stop() {
is_running = false;
is_running = false;
}
eventmask_t EventDispatcher::wait() {
return chEvtWaitAny(ALL_EVENTS);
return chEvtWaitAny(ALL_EVENTS);
}
void EventDispatcher::dispatch(const eventmask_t events) {
if( events & EVT_MASK_BASEBAND ) {
handle_baseband_queue();
}
if (events & EVT_MASK_BASEBAND) {
handle_baseband_queue();
}
if( events & EVT_MASK_SPECTRUM ) {
handle_spectrum();
}
if (events & EVT_MASK_SPECTRUM) {
handle_spectrum();
}
}
void EventDispatcher::handle_baseband_queue() {
const auto message = shared_memory.baseband_message;
if( message ) {
on_message(message);
}
const auto message = shared_memory.baseband_message;
if (message) {
on_message(message);
}
}
void EventDispatcher::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::Shutdown:
on_message_shutdown(*reinterpret_cast<const ShutdownMessage*>(message));
break;
switch (message->id) {
case Message::ID::Shutdown:
on_message_shutdown(*reinterpret_cast<const ShutdownMessage*>(message));
break;
default:
on_message_default(message);
shared_memory.baseband_message = nullptr;
break;
}
default:
on_message_default(message);
shared_memory.baseband_message = nullptr;
break;
}
}
void EventDispatcher::on_message_shutdown(const ShutdownMessage&) {
request_stop();
request_stop();
}
void EventDispatcher::on_message_default(const Message* const message) {
baseband_processor->on_message(message);
baseband_processor->on_message(message);
}
void EventDispatcher::handle_spectrum() {
const UpdateSpectrumMessage message;
baseband_processor->on_message(&message);
const UpdateSpectrumMessage message;
baseband_processor->on_message(&message);
}

View File

@@ -35,38 +35,38 @@ constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0);
constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1);
class EventDispatcher {
public:
EventDispatcher(std::unique_ptr<BasebandProcessor> baseband_processor);
public:
EventDispatcher(std::unique_ptr<BasebandProcessor> baseband_processor);
void run();
void request_stop();
void run();
void request_stop();
static inline void events_flag(const eventmask_t events) {
chEvtSignal(thread_event_loop, events);
}
static inline void events_flag(const eventmask_t events) {
chEvtSignal(thread_event_loop, events);
}
static inline void events_flag_isr(const eventmask_t events) {
chEvtSignalI(thread_event_loop, events);
}
static inline void events_flag_isr(const eventmask_t events) {
chEvtSignalI(thread_event_loop, events);
}
private:
static Thread* thread_event_loop;
private:
static Thread* thread_event_loop;
std::unique_ptr<BasebandProcessor> baseband_processor;
std::unique_ptr<BasebandProcessor> baseband_processor;
bool is_running = true;
bool is_running = true;
eventmask_t wait();
eventmask_t wait();
void dispatch(const eventmask_t events);
void dispatch(const eventmask_t events);
void handle_baseband_queue();
void handle_baseband_queue();
void on_message(const Message* const message);
void on_message_shutdown(const ShutdownMessage&);
void on_message_default(const Message* const message);
void on_message(const Message* const message);
void on_message_shutdown(const ShutdownMessage&);
void on_message_default(const Message* const message);
void handle_spectrum();
void handle_spectrum();
};
#endif/*__EVENT_M4_H__*/
#endif /*__EVENT_M4_H__*/

View File

@@ -38,7 +38,7 @@
*/
/*
static inline int16_t q15_from_double(const double d) {
return lrint(d * 32768);
return lrint(d * 32768);
}
*/
/**
@@ -49,16 +49,16 @@ static inline int16_t q15_from_double(const double d) {
* @param i 16-bit signed integer
* @return negative absolute value of i; defined for all values of i
*/
/*
/*
static inline int16_t s16_nabs(const int16_t j) {
#if (((int16_t)-1) >> 1) == ((int16_t)-1)
// signed right shift sign-extends (arithmetic)
const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement
// if j is positive (negSign is -1), xor will invert j and sub will add 1
// otherwise j is unchanged
return (j ^ negSign) - negSign;
// signed right shift sign-extends (arithmetic)
const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement
// if j is positive (negSign is -1), xor will invert j and sub will add 1
// otherwise j is unchanged
return (j ^ negSign) - negSign;
#else
return (j < 0 ? j : -j);
return (j < 0 ? j : -j);
#endif
}
*/
@@ -71,13 +71,13 @@ static inline int16_t s16_nabs(const int16_t j) {
* @return product of j and k, in same format
*/
static inline int16_t q15_mul(const int16_t j, const int16_t k) {
const int32_t intermediate = j * k;
#if 0 // don't round
const int32_t intermediate = j * k;
#if 0 // don't round
return intermediate >> 15;
#elif 0 // biased rounding
return (intermediate + 0x4000) >> 15;
#else // unbiased rounding
return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15;
#elif 0 // biased rounding
return (intermediate + 0x4000) >> 15;
#else // unbiased rounding
return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15;
#endif
}
@@ -92,7 +92,7 @@ static inline int16_t q15_mul(const int16_t j, const int16_t k) {
* @return numer / denom in same format as numer and denom
*/
static inline int16_t q15_div(const int16_t numer, const int16_t denom) {
return (static_cast<int32_t>(numer) << 15) / denom;
return (static_cast<int32_t>(numer) << 15) / denom;
}
/**
@@ -112,41 +112,41 @@ static inline int16_t q15_div(const int16_t numer, const int16_t denom) {
*/
static inline int16_t nabs(const int16_t j) {
//return -abs(x);
return (j < 0 ? j : -j);
// return -abs(x);
return (j < 0 ? j : -j);
}
int16_t fxpt_atan2(const int16_t y, const int16_t x) {
static const int16_t k1 = 2847;
static const int16_t k2 = 11039;
if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
if (y > 0) { // 1/8
return 8192;
} else if (y < 0) { // 5/8
return 40960;
} else { // x = y = 0
return 0;
}
}
const int16_t nabs_y = nabs(y);
const int16_t nabs_x = nabs(x);
if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
const int16_t y_over_x = q15_div(y, x);
const int16_t correction = q15_mul(k1, nabs(y_over_x));
const int16_t unrotated = q15_mul(k2 + correction, y_over_x);
if (x > 0) { // octants 1, 8
return unrotated;
} else { // octants 4, 5
return 32768 + unrotated;
}
} else { // octants 2, 3, 6, 7
const int16_t x_over_y = q15_div(x, y);
const int16_t correction = q15_mul(k1, nabs(x_over_y));
const int16_t unrotated = q15_mul(k2 + correction, x_over_y);
if (y > 0) { // octants 2, 3
return 16384 - unrotated;
} else { // octants 6, 7
return 49152 - unrotated;
}
}
static const int16_t k1 = 2847;
static const int16_t k2 = 11039;
if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
if (y > 0) { // 1/8
return 8192;
} else if (y < 0) { // 5/8
return 40960;
} else { // x = y = 0
return 0;
}
}
const int16_t nabs_y = nabs(y);
const int16_t nabs_x = nabs(x);
if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
const int16_t y_over_x = q15_div(y, x);
const int16_t correction = q15_mul(k1, nabs(y_over_x));
const int16_t unrotated = q15_mul(k2 + correction, y_over_x);
if (x > 0) { // octants 1, 8
return unrotated;
} else { // octants 4, 5
return 32768 + unrotated;
}
} else { // octants 2, 3, 6, 7
const int16_t x_over_y = q15_div(x, y);
const int16_t correction = q15_mul(k1, nabs(x_over_y));
const int16_t unrotated = q15_mul(k2 + correction, x_over_y);
if (y > 0) { // octants 2, 3
return 16384 - unrotated;
} else { // octants 6, 7
return 49152 - unrotated;
}
}
}

View File

@@ -26,4 +26,4 @@
int16_t fxpt_atan2(const int16_t y, const int16_t x);
#endif/*__FXPT_ATAN2_H__*/
#endif /*__FXPT_ATAN2_H__*/

View File

@@ -32,192 +32,190 @@ namespace gpdma {
namespace lli {
enum class ChainType : uint8_t {
Loop = 0,
OneShot = 1,
Loop = 0,
OneShot = 1,
};
enum class Interrupt : uint8_t {
All = 0,
Last = 1,
All = 0,
Last = 1,
};
struct ChainConfig {
ChainType type;
size_t length;
Interrupt interrupt;
ChainType type;
size_t length;
Interrupt interrupt;
};
enum class BurstSize : uint8_t {
Transfer1 = 0,
Transfer4 = 1,
Transfer8 = 2,
Transfer16 = 3,
Transfer32 = 4,
Transfer64 = 5,
Transfer128 = 6,
Transfer256 = 7,
Transfer1 = 0,
Transfer4 = 1,
Transfer8 = 2,
Transfer16 = 3,
Transfer32 = 4,
Transfer64 = 5,
Transfer128 = 6,
Transfer256 = 7,
};
enum class TransferWidth : uint8_t {
Byte = 0,
HalfWord = 1,
Word = 2,
Byte = 0,
HalfWord = 1,
Word = 2,
};
enum class Increment : uint8_t {
No = 0,
Yes = 1,
No = 0,
Yes = 1,
};
using PeripheralIndex = uint8_t;
struct Endpoint {
PeripheralIndex peripheral;
BurstSize burst_size;
TransferWidth transfer_size;
Increment increment;
PeripheralIndex peripheral;
BurstSize burst_size;
TransferWidth transfer_size;
Increment increment;
};
struct ChannelConfig {
ChainConfig chain;
FlowControl flow_control;
Endpoint source;
Endpoint destination;
ChainConfig chain;
FlowControl flow_control;
Endpoint source;
Endpoint destination;
constexpr gpdma::channel::Control control(
const size_t transfer_size,
const bool last
) {
return {
.transfersize = transfer_size,
.sbsize = toUType(source.burst_size),
.dbsize = toUType(destination.burst_size),
.swidth = toUType(source.transfer_size),
.dwidth = toUType(destination.transfer_size),
.s = source_endpoint_type(flow_control),
.d = destination_endpoint_type(flow_control),
.si = toUType(source.increment),
.di = toUType(destination.increment),
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U,
};
}
constexpr gpdma::channel::Control control(
const size_t transfer_size,
const bool last) {
return {
.transfersize = transfer_size,
.sbsize = toUType(source.burst_size),
.dbsize = toUType(destination.burst_size),
.swidth = toUType(source.transfer_size),
.dwidth = toUType(destination.transfer_size),
.s = source_endpoint_type(flow_control),
.d = destination_endpoint_type(flow_control),
.si = toUType(source.increment),
.di = toUType(destination.increment),
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U,
};
}
constexpr gpdma::channel::Config config() {
return {
.e = 0,
.srcperipheral = source.peripheral,
.destperipheral = destination.peripheral,
.flowcntrl = flow_control,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
};
constexpr gpdma::channel::Config config() {
return {
.e = 0,
.srcperipheral = source.peripheral,
.destperipheral = destination.peripheral,
.flowcntrl = flow_control,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
};
};
constexpr ChannelConfig channel_config_baseband_tx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
constexpr ChannelConfig channel_config_baseband_tx{
{ChainType::Loop, 4, Interrupt::All},
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
{0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes},
{0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No},
};
constexpr ChannelConfig channel_config_baseband_rx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
constexpr ChannelConfig channel_config_baseband_rx{
{ChainType::Loop, 4, Interrupt::All},
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No},
{0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes},
};
constexpr ChannelConfig channel_config_audio_tx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
constexpr ChannelConfig channel_config_audio_tx{
{ChainType::Loop, 4, Interrupt::All},
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
{0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes},
{0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No},
};
constexpr ChannelConfig channel_config_audio_rx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
constexpr ChannelConfig channel_config_audio_rx{
{ChainType::Loop, 4, Interrupt::All},
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No},
{0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes},
};
constexpr ChannelConfig channel_config_rssi {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{ 0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No },
{ 0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
constexpr ChannelConfig channel_config_rssi{
{ChainType::Loop, 4, Interrupt::All},
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No},
{0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes},
};
class Chain {
public:
using chain_t = std::vector<gpdma::channel::LLI>;
using chain_p = std::unique_ptr<chain_t>;
public:
using chain_t = std::vector<gpdma::channel::LLI>;
using chain_p = std::unique_ptr<chain_t>;
Chain(const ChannelConfig& cc) :
chain(std::make_unique<chain_t>(cc.chain.length))
{
set_lli_sequential(cc.chain_type);
set_source_address()...
}
Chain(const ChannelConfig& cc)
: chain(std::make_unique<chain_t>(cc.chain.length)) {
set_lli_sequential(cc.chain_type);
set_source_address()...
}
private:
chain_p chain;
private:
chain_p chain;
void set_source_peripheral(void* const address) {
set_source_address(address, 0);
}
void set_source_peripheral(void* const address) {
set_source_address(address, 0);
}
void set_destination_peripheral(void* const address) {
set_destination_address(address, 0);
}
void set_destination_peripheral(void* const address) {
set_destination_address(address, 0);
}
void set_source_address(void* const address, const size_t increment) {
size_t offset = 0;
for(auto& item : *chain) {
item.srcaddr = (uint32_t)address + offset;
offset += increment;
}
}
void set_source_address(void* const address, const size_t increment) {
size_t offset = 0;
for (auto& item : *chain) {
item.srcaddr = (uint32_t)address + offset;
offset += increment;
}
}
void set_destination_address(void* const address, const size_t increment) {
size_t offset = 0;
for(auto& item : *chain) {
item.destaddr = (uint32_t)address + offset;
offset += increment;
}
}
void set_destination_address(void* const address, const size_t increment) {
size_t offset = 0;
for (auto& item : *chain) {
item.destaddr = (uint32_t)address + offset;
offset += increment;
}
}
void set_control(const gpdma::channel::Control control) {
for(auto& item : *chain) {
item.control = control;
}
}
void set_control(const gpdma::channel::Control control) {
for (auto& item : *chain) {
item.control = control;
}
}
void set_lli_sequential(ChainType chain_type) {
for(auto& item : *chain) {
item.lli = lli_pointer(&item + 1);
}
if( chain_type == ChainType::Loop ) {
chain[chain->size() - 1].lli = lli_pointer(&chain[0]);
} else {
chain[chain->size() - 1].lli = lli_pointer(nullptr);
}
}
void set_lli_sequential(ChainType chain_type) {
for (auto& item : *chain) {
item.lli = lli_pointer(&item + 1);
}
if (chain_type == ChainType::Loop) {
chain[chain->size() - 1].lli = lli_pointer(&chain[0]);
} else {
chain[chain->size() - 1].lli = lli_pointer(nullptr);
}
}
gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = 0,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = 0,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
};
} /* namespace lli */

View File

@@ -35,84 +35,84 @@
* @brief Enables the TM subsystem.
*/
#if !defined(HAL_USE_TM) || defined(__DOXYGEN__)
#define HAL_USE_TM FALSE
#define HAL_USE_TM FALSE
#endif
/**
* @brief Enables the PAL subsystem.
*/
#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
#define HAL_USE_PAL FALSE
#define HAL_USE_PAL FALSE
#endif
/**
* @brief Enables the ADC subsystem.
*/
#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
#define HAL_USE_ADC FALSE
#define HAL_USE_ADC FALSE
#endif
/**
* @brief Enables the CAN subsystem.
*/
#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
#define HAL_USE_CAN FALSE
#define HAL_USE_CAN FALSE
#endif
/**
* @brief Enables the EXT subsystem.
*/
#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__)
#define HAL_USE_EXT FALSE
#define HAL_USE_EXT FALSE
#endif
/**
* @brief Enables the GPT subsystem.
*/
#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
#define HAL_USE_GPT FALSE
#define HAL_USE_GPT FALSE
#endif
/**
* @brief Enables the I2C subsystem.
*/
#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
#define HAL_USE_I2C FALSE
#define HAL_USE_I2C FALSE
#endif
/**
* @brief Enables the ICU subsystem.
*/
#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
#define HAL_USE_ICU FALSE
#define HAL_USE_ICU FALSE
#endif
/**
* @brief Enables the MAC subsystem.
*/
#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
#define HAL_USE_MAC FALSE
#define HAL_USE_MAC FALSE
#endif
/**
* @brief Enables the MMC_SPI subsystem.
*/
#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
#define HAL_USE_MMC_SPI FALSE
#define HAL_USE_MMC_SPI FALSE
#endif
/**
* @brief Enables the PWM subsystem.
*/
#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
#define HAL_USE_PWM FALSE
#define HAL_USE_PWM FALSE
#endif
/**
* @brief Enables the RTC subsystem.
*/
#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
#define HAL_USE_RTC FALSE
#define HAL_USE_RTC FALSE
#endif
/**
@@ -120,9 +120,9 @@
*/
#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
#if defined(BASEBAND_flash_utility) || defined(BASEBAND_sd_over_usb)
#define HAL_USE_SDC TRUE
#define HAL_USE_SDC TRUE
#else
#define HAL_USE_SDC FALSE
#define HAL_USE_SDC FALSE
#endif /* BASEBAND_flash_utility */
#endif
@@ -130,35 +130,35 @@
* @brief Enables the SERIAL subsystem.
*/
#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL FALSE
#define HAL_USE_SERIAL FALSE
#endif
/**
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL_USB FALSE
#define HAL_USE_SERIAL_USB FALSE
#endif
/**
* @brief Enables the SPI subsystem.
*/
#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
#define HAL_USE_SPI FALSE
#define HAL_USE_SPI FALSE
#endif
/**
* @brief Enables the UART subsystem.
*/
#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
#define HAL_USE_UART FALSE
#define HAL_USE_UART FALSE
#endif
/**
* @brief Enables the USB subsystem.
*/
#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
#define HAL_USE_USB FALSE
#define HAL_USE_USB FALSE
#endif
/*===========================================================================*/
@@ -170,7 +170,7 @@
* @note Disabling this option saves both code and data space.
*/
#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
#define ADC_USE_WAIT TRUE
#define ADC_USE_WAIT TRUE
#endif
/**
@@ -178,7 +178,7 @@
* @note Disabling this option saves both code and data space.
*/
#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define ADC_USE_MUTUAL_EXCLUSION TRUE
#define ADC_USE_MUTUAL_EXCLUSION TRUE
#endif
/*===========================================================================*/
@@ -189,7 +189,7 @@
* @brief Sleep mode related APIs inclusion switch.
*/
#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
#define CAN_USE_SLEEP_MODE TRUE
#define CAN_USE_SLEEP_MODE TRUE
#endif
/*===========================================================================*/
@@ -200,7 +200,7 @@
* @brief Enables the mutual exclusion APIs on the I2C bus.
*/
#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define I2C_USE_MUTUAL_EXCLUSION TRUE
#define I2C_USE_MUTUAL_EXCLUSION TRUE
#endif
/*===========================================================================*/
@@ -211,14 +211,14 @@
* @brief Enables an event sources for incoming packets.
*/
#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
#define MAC_USE_ZERO_COPY FALSE
#define MAC_USE_ZERO_COPY FALSE
#endif
/**
* @brief Enables an event sources for incoming packets.
*/
#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
#define MAC_USE_EVENTS TRUE
#define MAC_USE_EVENTS TRUE
#endif
/*===========================================================================*/
@@ -234,7 +234,7 @@
* use a DMA channel and heavily loads the CPU.
*/
#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
#define MMC_NICE_WAITING TRUE
#define MMC_NICE_WAITING TRUE
#endif
/*===========================================================================*/
@@ -246,7 +246,7 @@
* @note Attempts are performed at 10mS intervals.
*/
#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
#define SDC_INIT_RETRY 100
#define SDC_INIT_RETRY 100
#endif
/**
@@ -255,7 +255,7 @@
* at @p FALSE.
*/
#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
#define SDC_MMC_SUPPORT FALSE
#define SDC_MMC_SUPPORT FALSE
#endif
/**
@@ -265,7 +265,7 @@
* lower priority, this may slow down the driver a bit however.
*/
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
#define SDC_NICE_WAITING FALSE
#define SDC_NICE_WAITING FALSE
#endif
/*===========================================================================*/
@@ -278,7 +278,7 @@
* default configuration.
*/
#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
#define SERIAL_DEFAULT_BITRATE 38400
#define SERIAL_DEFAULT_BITRATE 38400
#endif
/**
@@ -289,7 +289,7 @@
* buffers.
*/
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
#define SERIAL_BUFFERS_SIZE 16
#define SERIAL_BUFFERS_SIZE 16
#endif
/*===========================================================================*/
@@ -301,7 +301,7 @@
* @note Disabling this option saves both code and data space.
*/
#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
#define SPI_USE_WAIT TRUE
#define SPI_USE_WAIT TRUE
#endif
/**
@@ -309,7 +309,7 @@
* @note Disabling this option saves both code and data space.
*/
#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define SPI_USE_MUTUAL_EXCLUSION TRUE
#define SPI_USE_MUTUAL_EXCLUSION TRUE
#endif
#endif /* _HALCONF_H_ */

View File

@@ -26,44 +26,42 @@ namespace dsp {
namespace interpolation {
class LinearResampler {
public:
void configure(
const float input_rate,
const float output_rate
) {
phase_increment = calculate_increment(input_rate, output_rate);
}
public:
void configure(
const float input_rate,
const float output_rate) {
phase_increment = calculate_increment(input_rate, output_rate);
}
template<typename InterpolatedSampleHandler>
void operator()(
const float sample,
InterpolatedSampleHandler interpolated_sample_handler
) {
const float sample_delta = sample - last_sample;
while( phase < 1.0f ) {
const float interpolated_value = last_sample + phase * sample_delta;
interpolated_sample_handler(interpolated_value);
phase += phase_increment;
}
last_sample = sample;
phase -= 1.0f;
}
template <typename InterpolatedSampleHandler>
void operator()(
const float sample,
InterpolatedSampleHandler interpolated_sample_handler) {
const float sample_delta = sample - last_sample;
while (phase < 1.0f) {
const float interpolated_value = last_sample + phase * sample_delta;
interpolated_sample_handler(interpolated_value);
phase += phase_increment;
}
last_sample = sample;
phase -= 1.0f;
}
void advance(const float fraction) {
phase += (fraction * phase_increment);
}
void advance(const float fraction) {
phase += (fraction * phase_increment);
}
private:
float last_sample { 0.0f };
float phase { 0.0f };
float phase_increment { 0.0f };
private:
float last_sample{0.0f};
float phase{0.0f};
float phase_increment{0.0f};
static constexpr float calculate_increment(const float input_rate, const float output_rate) {
return input_rate / output_rate;
}
static constexpr float calculate_increment(const float input_rate, const float output_rate) {
return input_rate / output_rate;
}
};
} /* namespace interpolation */
} /* namespace dsp */
#endif/*__LINEAR_RESAMPLER_H__*/
#endif /*__LINEAR_RESAMPLER_H__*/

View File

@@ -53,69 +53,68 @@
extern "C" {
void __late_init(void) {
/*
* System initializations.
* - HAL initialization, this also initializes the configured device drivers
* and performs the board-specific initializations.
* - Kernel initialization, the main() function becomes a thread and the
* RTOS is active.
*/
halInit();
/*
* System initializations.
* - HAL initialization, this also initializes the configured device drivers
* and performs the board-specific initializations.
* - Kernel initialization, the main() function becomes a thread and the
* RTOS is active.
*/
halInit();
/* After this call, scheduler, systick, heap, etc. are available. */
/* By doing chSysInit() here, it runs before C++ constructors, which may
* require the heap.
*/
chSysInit();
/* After this call, scheduler, systick, heap, etc. are available. */
/* By doing chSysInit() here, it runs before C++ constructors, which may
* require the heap.
*/
chSysInit();
}
}
static void init() {
audio::dma::init();
audio::dma::configure();
audio::dma::enable();
audio::dma::init();
audio::dma::configure();
audio::dma::enable();
LPC_CREG->DMAMUX = portapack::gpdma_mux;
gpdma::controller.enable();
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
LPC_CREG->DMAMUX = portapack::gpdma_mux;
gpdma::controller.enable();
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
touch::dma::init();
touch::dma::allocate();
touch::dma::enable();
touch::dma::init();
touch::dma::allocate();
touch::dma::enable();
}
static void halt() {
port_disable();
while(true) {
port_wait_for_interrupt();
}
port_disable();
while (true) {
port_wait_for_interrupt();
}
}
static void shutdown() {
// TODO: Is this complete?
nvicDisableVector(DMA_IRQn);
chSysDisable();
// TODO: Is this complete?
systick_stop();
nvicDisableVector(DMA_IRQn);
ShutdownMessage shutdown_message;
shared_memory.application_queue.push(shutdown_message);
chSysDisable();
halt();
systick_stop();
ShutdownMessage shutdown_message;
shared_memory.application_queue.push(shutdown_message);
halt();
}
int main(void) {
init();
init();
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
EventDispatcher event_dispatcher;
event_dispatcher.run();
EventDispatcher event_dispatcher;
event_dispatcher.run();
shutdown();
shutdown();
return 0;
return 0;
}

View File

@@ -30,77 +30,75 @@ namespace dsp {
namespace matched_filter {
void MatchedFilter::configure(
const tap_t* const taps,
const size_t taps_count,
const size_t decimation_factor
) {
samples_ = std::make_unique<samples_t>(taps_count);
taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count;
decimation_factor_ = decimation_factor;
output = 0;
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
const tap_t* const taps,
const size_t taps_count,
const size_t decimation_factor) {
samples_ = std::make_unique<samples_t>(taps_count);
taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count;
decimation_factor_ = decimation_factor;
output = 0;
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
}
bool MatchedFilter::execute_once(
const sample_t input
) {
samples_[taps_count_ - decimation_factor_ + decimation_phase] = input;
const sample_t input) {
samples_[taps_count_ - decimation_factor_ + decimation_phase] = input;
advance_decimation_phase();
if( is_new_decimation_cycle() ) {
float sr_tr = 0.0f;
float si_tr = 0.0f;
float si_ti = 0.0f;
float sr_ti = 0.0f;
for(size_t n=0; n<taps_count_; n++) {
const auto sample = samples_[n];
const auto tap = taps_reversed_[n];
advance_decimation_phase();
if (is_new_decimation_cycle()) {
float sr_tr = 0.0f;
float si_tr = 0.0f;
float si_ti = 0.0f;
float sr_ti = 0.0f;
for (size_t n = 0; n < taps_count_; n++) {
const auto sample = samples_[n];
const auto tap = taps_reversed_[n];
sr_tr += sample.real() * tap.real();
si_ti += sample.imag() * tap.imag();
si_tr += sample.imag() * tap.real();
sr_ti += sample.real() * tap.imag();
}
sr_tr += sample.real() * tap.real();
si_ti += sample.imag() * tap.imag();
si_tr += sample.imag() * tap.real();
sr_ti += sample.real() * tap.imag();
}
// N: complex multiple of samples and taps (conjugate, tap.i negated).
// P: complex multiply of samples and taps.
const auto r_n = sr_tr + si_ti;
const auto r_p = sr_tr - si_ti;
const auto i_n = si_tr - sr_ti;
const auto i_p = si_tr + sr_ti;
// N: complex multiple of samples and taps (conjugate, tap.i negated).
// P: complex multiply of samples and taps.
const auto r_n = sr_tr + si_ti;
const auto r_p = sr_tr - si_ti;
const auto i_n = si_tr - sr_ti;
const auto i_p = si_tr + sr_ti;
const auto mag_n = std::sqrt(r_n * r_n + i_n * i_n);
const auto mag_p = std::sqrt(r_p * r_p + i_p * i_p);
const auto diff = mag_p - mag_n;
output = diff;
const auto mag_n = std::sqrt(r_n * r_n + i_n * i_n);
const auto mag_p = std::sqrt(r_p * r_p + i_p * i_p);
const auto diff = mag_p - mag_n;
output = diff;
shift_by_decimation_factor();
return true;
} else {
return false;
}
shift_by_decimation_factor();
return true;
} else {
return false;
}
}
void MatchedFilter::shift_by_decimation_factor() {
const sample_t* s = &samples_[decimation_factor_];
sample_t* t = &samples_[0];
const size_t unroll_factor = 4;
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
while(shift_count > 0) {
*t++ = *s++;
*t++ = *s++;
*t++ = *s++;
*t++ = *s++;
shift_count--;
}
const sample_t* s = &samples_[decimation_factor_];
sample_t* t = &samples_[0];
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
while(shift_count > 0) {
*t++ = *s++;
shift_count--;
}
const size_t unroll_factor = 4;
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
while (shift_count > 0) {
*t++ = *s++;
*t++ = *s++;
*t++ = *s++;
*t++ = *s++;
shift_count--;
}
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
while (shift_count > 0) {
*t++ = *s++;
shift_count--;
}
}
} /* namespace matched_filter */

View File

@@ -35,62 +35,59 @@ namespace matched_filter {
// a multiple of the complex sinusoid period.
class MatchedFilter {
public:
using sample_t = std::complex<float>;
using tap_t = std::complex<float>;
public:
using sample_t = std::complex<float>;
using tap_t = std::complex<float>;
using taps_t = tap_t[];
using taps_t = tap_t[];
template<class T>
MatchedFilter(
const T& taps,
size_t decimation_factor = 1
) {
configure(taps, decimation_factor);
}
template <class T>
MatchedFilter(
const T& taps,
size_t decimation_factor = 1) {
configure(taps, decimation_factor);
}
template<class T>
void configure(
const T& taps,
size_t decimation_factor
) {
configure(taps.data(), taps.size(), decimation_factor);
}
template <class T>
void configure(
const T& taps,
size_t decimation_factor) {
configure(taps.data(), taps.size(), decimation_factor);
}
bool execute_once(const sample_t input);
bool execute_once(const sample_t input);
float get_output() const {
return output;
}
float get_output() const {
return output;
}
private:
using samples_t = sample_t[];
private:
using samples_t = sample_t[];
std::unique_ptr<samples_t> samples_ { };
std::unique_ptr<taps_t> taps_reversed_ { };
size_t taps_count_ { 0 };
size_t decimation_factor_ { 1 };
size_t decimation_phase { 0 };
float output { 0 };
std::unique_ptr<samples_t> samples_{};
std::unique_ptr<taps_t> taps_reversed_{};
size_t taps_count_{0};
size_t decimation_factor_{1};
size_t decimation_phase{0};
float output{0};
void shift_by_decimation_factor();
void shift_by_decimation_factor();
void advance_decimation_phase() {
decimation_phase = (decimation_phase + 1) % decimation_factor_;
}
void advance_decimation_phase() {
decimation_phase = (decimation_phase + 1) % decimation_factor_;
}
bool is_new_decimation_cycle() const {
return (decimation_phase == 0);
}
bool is_new_decimation_cycle() const {
return (decimation_phase == 0);
}
void configure(
const tap_t* const taps,
const size_t taps_count,
const size_t decimation_factor
);
void configure(
const tap_t* const taps,
const size_t taps_count,
const size_t decimation_factor);
};
} /* namespace matched_filter */
} /* namespace dsp */
#endif/*__MATCHED_FILTER_H__*/
#endif /*__MATCHED_FILTER_H__*/

View File

@@ -37,12 +37,12 @@
*/
//#define LPC_ADC0_IRQ_PRIORITY 2
#define LPC_DMA_IRQ_PRIORITY 3
#define LPC_DMA_IRQ_PRIORITY 3
//#define LPC_ADC1_IRQ_PRIORITY 4
#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4
#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4
/* M4 is initialized by M0, which has already started PLL1 */
#if !defined(LPC43XX_M4_CLK) || defined(__DOXYGEN__)
#define LPC43XX_M4_CLK 200000000
#define LPC43XX_M4_CLK 200000000
#endif

View File

@@ -31,65 +31,61 @@
#include <cmath>
class OOKSlicerMagSquaredInt {
public:
using symbol_t = bool;
public:
using symbol_t = bool;
constexpr OOKSlicerMagSquaredInt(
const float samples_per_symbol
) : mag2_threshold_leak_factor {
static_cast<uint32_t>(
factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32)
)
}
{
}
constexpr OOKSlicerMagSquaredInt(
const float samples_per_symbol)
: mag2_threshold_leak_factor{
static_cast<uint32_t>(
factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32))} {
}
symbol_t operator()(const std::complex<int16_t> in) {
const uint32_t real2 = in.real() * in.real();
const uint32_t imag2 = in.imag() * in.imag();
const uint32_t mag2 = real2 + imag2;
symbol_t operator()(const std::complex<int16_t> in) {
const uint32_t real2 = in.real() * in.real();
const uint32_t imag2 = in.imag() * in.imag();
const uint32_t mag2 = real2 + imag2;
const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2
mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32;
mag2_threshold = std::max(mag2_threshold, mag2_attenuated);
const bool symbol = (mag2 > mag2_threshold);
return symbol;
}
const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2
mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32;
mag2_threshold = std::max(mag2_threshold, mag2_attenuated);
const bool symbol = (mag2 > mag2_threshold);
return symbol;
}
private:
const uint32_t mag2_threshold_leak_factor;
uint32_t mag2_threshold = 0;
private:
const uint32_t mag2_threshold_leak_factor;
uint32_t mag2_threshold = 0;
constexpr float factor_sq(float db) {
return std::pow(10.0f, db / (10.0f / 2));
}
constexpr float factor_sq(float db) {
return std::pow(10.0f, db / (10.0f / 2));
}
};
class OOKClockRecovery {
public:
constexpr OOKClockRecovery(
const float samples_per_symbol
) : symbol_phase_inc_nominal { static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol)) },
symbol_phase_inc_k { static_cast<uint32_t>(std::round(symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol)) },
phase_detector { static_cast<size_t>(std::round(samples_per_symbol)) },
phase_accumulator { symbol_phase_inc_nominal }
{
}
public:
constexpr OOKClockRecovery(
const float samples_per_symbol)
: symbol_phase_inc_nominal{static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol))},
symbol_phase_inc_k{static_cast<uint32_t>(std::round(symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol))},
phase_detector{static_cast<size_t>(std::round(samples_per_symbol))},
phase_accumulator{symbol_phase_inc_nominal} {
}
template<typename SymbolHandler>
void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) {
if( phase_accumulator() ) {
const auto detector_result = phase_detector(slicer_history);
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * symbol_phase_inc_k);
symbol_handler(detector_result.symbol);
}
}
template <typename SymbolHandler>
void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) {
if (phase_accumulator()) {
const auto detector_result = phase_detector(slicer_history);
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * symbol_phase_inc_k);
symbol_handler(detector_result.symbol);
}
}
private:
const uint32_t symbol_phase_inc_nominal;
const uint32_t symbol_phase_inc_k;
PhaseDetectorEarlyLateGate phase_detector;
PhaseAccumulator phase_accumulator;
private:
const uint32_t symbol_phase_inc_nominal;
const uint32_t symbol_phase_inc_k;
PhaseDetectorEarlyLateGate phase_detector;
PhaseAccumulator phase_accumulator;
};
#endif/*__OOK_HPP__*/
#endif /*__OOK_HPP__*/

View File

@@ -31,109 +31,106 @@
#include "baseband_packet.hpp"
struct NeverMatch {
bool operator()(const BitHistory&, const size_t) const {
return false;
}
bool operator()(const BitHistory&, const size_t) const {
return false;
}
};
struct FixedLength {
bool operator()(const BitHistory&, const size_t symbols_received) const {
return symbols_received >= length;
}
bool operator()(const BitHistory&, const size_t symbols_received) const {
return symbols_received >= length;
}
const size_t length;
const size_t length;
};
template<typename PreambleMatcher, typename UnstuffMatcher, typename EndMatcher>
template <typename PreambleMatcher, typename UnstuffMatcher, typename EndMatcher>
class PacketBuilder {
public:
using PayloadHandlerFunc = std::function<void(const baseband::Packet& packet)>;
public:
using PayloadHandlerFunc = std::function<void(const baseband::Packet& packet)>;
PacketBuilder(
const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher,
const EndMatcher end_matcher,
PayloadHandlerFunc payload_handler
) : payload_handler { std::move(payload_handler) },
preamble(preamble_matcher),
unstuff(unstuff_matcher),
end(end_matcher)
{
}
PacketBuilder(
const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher,
const EndMatcher end_matcher,
PayloadHandlerFunc payload_handler)
: payload_handler{std::move(payload_handler)},
preamble(preamble_matcher),
unstuff(unstuff_matcher),
end(end_matcher) {
}
void configure(
const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher
) {
preamble = preamble_matcher;
unstuff = unstuff_matcher;
void configure(
const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher) {
preamble = preamble_matcher;
unstuff = unstuff_matcher;
reset_state();
}
reset_state();
}
void execute(
const uint_fast8_t symbol
) {
bit_history.add(symbol);
void execute(
const uint_fast8_t symbol) {
bit_history.add(symbol);
switch(state) {
case State::Preamble:
if( preamble(bit_history, packet.size()) ) {
state = State::Payload;
}
break;
switch (state) {
case State::Preamble:
if (preamble(bit_history, packet.size())) {
state = State::Payload;
}
break;
case State::Payload:
if( !unstuff(bit_history, packet.size()) ) {
packet.add(symbol);
}
case State::Payload:
if (!unstuff(bit_history, packet.size())) {
packet.add(symbol);
}
if( end(bit_history, packet.size()) ) {
// NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make payload_handler known at compile time.
if( payload_handler ) {
packet.set_timestamp(Timestamp::now());
payload_handler(packet);
}
reset_state();
} else {
if( packet_truncated() ) {
reset_state();
}
}
break;
if (end(bit_history, packet.size())) {
// NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make payload_handler known at compile time.
if (payload_handler) {
packet.set_timestamp(Timestamp::now());
payload_handler(packet);
}
reset_state();
} else {
if (packet_truncated()) {
reset_state();
}
}
break;
default:
reset_state();
break;
}
}
default:
reset_state();
break;
}
}
private:
enum State {
Preamble,
Payload,
};
private:
enum State {
Preamble,
Payload,
};
bool packet_truncated() const {
return packet.size() >= packet.capacity();
}
bool packet_truncated() const {
return packet.size() >= packet.capacity();
}
const PayloadHandlerFunc payload_handler;
const PayloadHandlerFunc payload_handler;
BitHistory bit_history { };
PreambleMatcher preamble { };
UnstuffMatcher unstuff { };
EndMatcher end { };
BitHistory bit_history{};
PreambleMatcher preamble{};
UnstuffMatcher unstuff{};
EndMatcher end{};
State state { State::Preamble };
baseband::Packet packet { };
State state{State::Preamble};
baseband::Packet packet{};
void reset_state() {
packet.clear();
state = State::Preamble;
}
void reset_state() {
packet.clear();
state = State::Preamble;
}
};
#endif/*__PACKET_BUILDER_H__*/
#endif /*__PACKET_BUILDER_H__*/

View File

@@ -25,26 +25,25 @@
#include <cstdint>
class PhaseAccumulator {
public:
constexpr PhaseAccumulator(
const uint32_t phase_inc
) : phase_inc { phase_inc }
{
}
public:
constexpr PhaseAccumulator(
const uint32_t phase_inc)
: phase_inc{phase_inc} {
}
bool operator()() {
const auto last_phase = phase;
phase += phase_inc;
return (phase < last_phase);
}
bool operator()() {
const auto last_phase = phase;
phase += phase_inc;
return (phase < last_phase);
}
void set_inc(const uint32_t new_phase_inc) {
phase_inc = new_phase_inc;
}
void set_inc(const uint32_t new_phase_inc) {
phase_inc = new_phase_inc;
}
private:
uint32_t phase { 0 };
uint32_t phase_inc;
private:
uint32_t phase{0};
uint32_t phase_inc;
};
#endif/*__PHASE_ACCUMULATOR_HPP__*/
#endif /*__PHASE_ACCUMULATOR_HPP__*/

View File

@@ -27,44 +27,43 @@
#include <cmath>
class PhaseDetectorEarlyLateGate {
public:
using history_t = uint32_t;
public:
using history_t = uint32_t;
using symbol_t = bool;
using error_t = int;
using symbol_t = bool;
using error_t = int;
struct result_t {
symbol_t symbol;
error_t error;
};
struct result_t {
symbol_t symbol;
error_t error;
};
constexpr PhaseDetectorEarlyLateGate(
const size_t samples_per_symbol
) : sample_threshold { samples_per_symbol / 2 },
late_mask { (1UL << sample_threshold) - 1UL },
early_mask { late_mask << sample_threshold }
{
}
constexpr PhaseDetectorEarlyLateGate(
const size_t samples_per_symbol)
: sample_threshold{samples_per_symbol / 2},
late_mask{(1UL << sample_threshold) - 1UL},
early_mask{late_mask << sample_threshold} {
}
result_t operator()(const history_t symbol_history) const {
static_assert(sizeof(history_t) == sizeof(unsigned long), "popcountl size mismatch");
result_t operator()(const history_t symbol_history) const {
static_assert(sizeof(history_t) == sizeof(unsigned long), "popcountl size mismatch");
// history = ...0111, early
// history = ...1110, late
// history = ...0111, early
// history = ...1110, late
const size_t late_side = __builtin_popcountl(symbol_history & late_mask);
const size_t early_side = __builtin_popcountl(symbol_history & early_mask);
const size_t total_count = late_side + early_side;
const auto lateness = static_cast<int>(late_side) - static_cast<int>(early_side);
const symbol_t symbol = (total_count >= sample_threshold);
const error_t error = symbol ? -lateness : lateness;
return { symbol, error };
}
const size_t late_side = __builtin_popcountl(symbol_history & late_mask);
const size_t early_side = __builtin_popcountl(symbol_history & early_mask);
const size_t total_count = late_side + early_side;
const auto lateness = static_cast<int>(late_side) - static_cast<int>(early_side);
const symbol_t symbol = (total_count >= sample_threshold);
const error_t error = symbol ? -lateness : lateness;
return {symbol, error};
}
private:
const size_t sample_threshold;
const history_t late_mask;
const history_t early_mask;
private:
const size_t sample_threshold;
const history_t late_mask;
const history_t early_mask;
};
#endif/*__PHASE_DETECTOR_HPP__*/
#endif /*__PHASE_DETECTOR_HPP__*/

View File

@@ -29,53 +29,51 @@
#include "event_m4.hpp"
ACARSProcessor::ACARSProcessor() {
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
packet.clear();
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
packet.clear();
}
void ACARSProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto decimator_out = decim_1_out;
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto decimator_out = decim_1_out;
/* 38.4kHz, 32 samples */
feed_channel_stats(decimator_out);
/* 38.4kHz, 32 samples */
feed_channel_stats(decimator_out);
for(size_t i=0; i<decimator_out.count; i++) {
if( mf.execute_once(decimator_out.p[i]) ) {
clock_recovery(mf.get_output());
}
}
for (size_t i = 0; i < decimator_out.count; i++) {
if (mf.execute_once(decimator_out.p[i])) {
clock_recovery(mf.get_output());
}
}
}
void ACARSProcessor::consume_symbol(
const float raw_symbol
) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
//const auto decoded_symbol = acars_decode(sliced_symbol);
const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
// const auto decoded_symbol = acars_decode(sliced_symbol);
// DEBUG
packet.add(sliced_symbol);
if (packet.size() == 256) {
payload_handler(packet);
packet.clear();
}
// DEBUG
packet.add(sliced_symbol);
if (packet.size() == 256) {
payload_handler(packet);
packet.clear();
}
//packet_builder.execute(decoded_symbol);
// packet_builder.execute(decoded_symbol);
}
void ACARSProcessor::payload_handler(
const baseband::Packet& packet
) {
const ACARSPacketMessage message { packet };
shared_memory.application_queue.push(message);
const baseband::Packet& packet) {
const ACARSPacketMessage message{packet};
shared_memory.application_queue.push(message);
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<ACARSProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<ACARSProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -76,63 +76,70 @@
// 16 taps, 1 symbol, 2 cycles
// Number of taps: size of one symbol in samples (in/symbol)
// Cycles:
// Cycles:
// Translate+rectangular filter
// sample=38.4k, deviation=4800, symbol=2400
// Length: 16 taps, 1 symbol, 2 cycles of sinusoid
// This is actually the same as rect_taps_307k2_38k4_1t_19k2_p
constexpr std::array<std::complex<float>, 16> rect_taps_38k4_4k8_1t_2k4_p { {
{ 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f },
{ 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f },
{ -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f },
{ 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f },
{ 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f },
{ 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f },
{ -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f },
{ 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f },
} };
constexpr std::array<std::complex<float>, 16> rect_taps_38k4_4k8_1t_2k4_p{{
{6.2500000000e-02f, 0.0000000000e+00f},
{4.4194173824e-02f, 4.4194173824e-02f},
{0.0000000000e+00f, 6.2500000000e-02f},
{-4.4194173824e-02f, 4.4194173824e-02f},
{-6.2500000000e-02f, 0.0000000000e+00f},
{-4.4194173824e-02f, -4.4194173824e-02f},
{0.0000000000e+00f, -6.2500000000e-02f},
{4.4194173824e-02f, -4.4194173824e-02f},
{6.2500000000e-02f, 0.0000000000e+00f},
{4.4194173824e-02f, 4.4194173824e-02f},
{0.0000000000e+00f, 6.2500000000e-02f},
{-4.4194173824e-02f, 4.4194173824e-02f},
{-6.2500000000e-02f, 0.0000000000e+00f},
{-4.4194173824e-02f, -4.4194173824e-02f},
{0.0000000000e+00f, -6.2500000000e-02f},
{4.4194173824e-02f, -4.4194173824e-02f},
}};
class ACARSProcessor : public BasebandProcessor {
public:
ACARSProcessor();
public:
ACARSProcessor();
void execute(const buffer_c8_t& buffer) override;
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 2457600;
private:
static constexpr size_t baseband_fs = 2457600;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; // Translate already done here !
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::matched_filter::MatchedFilter mf { rect_taps_38k4_4k8_1t_2k4_p, 8 };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; // Translate already done here !
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::matched_filter::MatchedFilter mf{rect_taps_38k4_4k8_1t_2k4_p, 8};
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
4800, 2400, { 0.0555f },
[this](const float symbol) { this->consume_symbol(symbol); }
};
symbol_coding::ACARSDecoder acars_decode { };
/*PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
{ 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH
{ },
{ 128 },
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
}
};*/
baseband::Packet packet { };
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery{
4800,
2400,
{0.0555f},
[this](const float symbol) { this->consume_symbol(symbol); }};
symbol_coding::ACARSDecoder acars_decode{};
/*PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
{ 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH
{ },
{ 128 },
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
}
};*/
baseband::Packet packet{};
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
};
#endif/*__PROC_ACARS_H__*/
#endif /*__PROC_ACARS_H__*/

View File

@@ -29,138 +29,133 @@
#include <cstddef>
using namespace adsb;
void ADSBRXProcessor::execute(const buffer_c8_t& buffer) {
int8_t re, im;
uint32_t mag;
uint32_t c;
uint8_t bit, byte{};
// This is called at 2M/2048 = 977Hz
// One pulse = 500ns = 2 samples
// One bit = 2 pulses = 1us = 4 samples
int8_t re, im;
uint32_t mag;
uint32_t c;
uint8_t bit, byte{};
if (!configured) return;
for (size_t i = 0; i < buffer.count; i++) {
// Compute sample's magnitude
re = (int32_t)buffer.p[i].real();
im = (int32_t)buffer.p[i].imag();
mag = ((uint32_t)(re*re) + (uint32_t)(im*im));
// This is called at 2M/2048 = 977Hz
// One pulse = 500ns = 2 samples
// One bit = 2 pulses = 1us = 4 samples
if (decoding) {
// Decode
// 1 bit lasts 2 samples
if (sample_count & 1) {
if (bit_count >= msgLen)
{
const ADSBFrameMessage message(frame, amp);
shared_memory.application_queue.push(message);
decoding = false;
bit = (prev_mag > mag) ? 1 : 0;
}
else
{
bit = (prev_mag > mag) ? 1 : 0;
}
byte = bit | (byte << 1);
bit_count++;
if (!configured) return;
// Perform checks at the end of the first byte
if (!(bit_count & 7)) {
for (size_t i = 0; i < buffer.count; i++) {
// Compute sample's magnitude
re = (int32_t)buffer.p[i].real();
im = (int32_t)buffer.p[i].imag();
mag = ((uint32_t)(re * re) + (uint32_t)(im * im));
// Store the byte
frame.push_byte(byte);
if (decoding) {
// Decode
// Check at the end of the first byte of the message
uint8_t df = (byte >> 3);
if ( (bit_count == 8) && !(df & 0x10) ) {
msgLen = 56; // DFs 16 or greater are long 112. DFs 15 or less are short 56.
}
// 1 bit lasts 2 samples
if (sample_count & 1) {
if (bit_count >= msgLen) {
const ADSBFrameMessage message(frame, amp);
shared_memory.application_queue.push(message);
decoding = false;
bit = (prev_mag > mag) ? 1 : 0;
} else {
bit = (prev_mag > mag) ? 1 : 0;
}
// Abondon all frames that arent DF17 or DF18 extended squitters
if ( (bit_count == 8) && !((df == 17)||(df == 18)) ) {
decoding = false;
bit = (prev_mag > mag) ? 1 : 0;
frame.clear();
}
} // last bit of a byte
} // Second sample of each bit
sample_count++;
}
byte = bit | (byte << 1);
bit_count++;
// Continue looking for preamble even if in a packet
// switch if new preamble is higher magnitude
// Shift the preamble
for (c = 0; c < (ADSB_PREAMBLE_LENGTH ); c++) { shifter[c] = shifter[c + 1]; }
shifter[ADSB_PREAMBLE_LENGTH] = mag;
// First check of relations between the first 10 samples
// representing a valid preamble. We don't even investigate further
// if this simple test is not passed
if (shifter[0] < shifter[1] &&
shifter[1] > shifter[2] &&
shifter[2] < shifter[3] &&
shifter[3] > shifter[4] &&
shifter[4] < shifter[1] &&
shifter[5] < shifter[1] &&
shifter[6] < shifter[1] &&
shifter[7] < shifter[1] &&
shifter[8] > shifter[9] &&
shifter[9] < shifter[10] &&
shifter[10]> shifter[11] )
{
// The samples between the two spikes must be < than the average
// of the high spikes level. We don't test bits too near to
// the high levels as signals can be out of phase so part of the
// energy can be in the near samples
int32_t thisAmp = (shifter[1] + shifter[3] + shifter[8] + shifter[10]);
uint32_t high = thisAmp / 9;
if (
shifter[5] < high &&
shifter[6] < high &&
// Similarly samples in the range 11-13 must be low, as it is the
// space between the preamble and real data. Again we don't test
// bits too near to high levels, see above
shifter[12] < high &&
shifter[13] < high &&
shifter[14] < high )
{
if ((decoding == false) || // New preamble
((decoding == true) && (thisAmp > amp))) // Higher power than existing packet
{
decoding = true;
msgLen = 112;
amp = thisAmp;
sample_count = 0;
bit_count = 0;
frame.clear();
}
} // 4 & 5 low and 11-14 low
} // Check for preamble pattern
// Perform checks at the end of the first byte
if (!(bit_count & 7)) {
// Store the byte
frame.push_byte(byte);
// Store mag for next time
prev_mag = mag;
}
// Check at the end of the first byte of the message
uint8_t df = (byte >> 3);
if ((bit_count == 8) && !(df & 0x10)) {
msgLen = 56; // DFs 16 or greater are long 112. DFs 15 or less are short 56.
}
// Abondon all frames that arent DF17 or DF18 extended squitters
if ((bit_count == 8) && !((df == 17) || (df == 18))) {
decoding = false;
bit = (prev_mag > mag) ? 1 : 0;
frame.clear();
}
} // last bit of a byte
} // Second sample of each bit
sample_count++;
}
// Continue looking for preamble even if in a packet
// switch if new preamble is higher magnitude
// Shift the preamble
for (c = 0; c < (ADSB_PREAMBLE_LENGTH); c++) {
shifter[c] = shifter[c + 1];
}
shifter[ADSB_PREAMBLE_LENGTH] = mag;
// First check of relations between the first 10 samples
// representing a valid preamble. We don't even investigate further
// if this simple test is not passed
if (shifter[0] < shifter[1] &&
shifter[1] > shifter[2] &&
shifter[2] < shifter[3] &&
shifter[3] > shifter[4] &&
shifter[4] < shifter[1] &&
shifter[5] < shifter[1] &&
shifter[6] < shifter[1] &&
shifter[7] < shifter[1] &&
shifter[8] > shifter[9] &&
shifter[9] < shifter[10] &&
shifter[10] > shifter[11]) {
// The samples between the two spikes must be < than the average
// of the high spikes level. We don't test bits too near to
// the high levels as signals can be out of phase so part of the
// energy can be in the near samples
int32_t thisAmp = (shifter[1] + shifter[3] + shifter[8] + shifter[10]);
uint32_t high = thisAmp / 9;
if (
shifter[5] < high &&
shifter[6] < high &&
// Similarly samples in the range 11-13 must be low, as it is the
// space between the preamble and real data. Again we don't test
// bits too near to high levels, see above
shifter[12] < high &&
shifter[13] < high &&
shifter[14] < high) {
if ((decoding == false) || // New preamble
((decoding == true) && (thisAmp > amp))) // Higher power than existing packet
{
decoding = true;
msgLen = 112;
amp = thisAmp;
sample_count = 0;
bit_count = 0;
frame.clear();
}
} // 4 & 5 low and 11-14 low
} // Check for preamble pattern
// Store mag for next time
prev_mag = mag;
}
}
void ADSBRXProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::ADSBConfigure) {
bit_count = 0;
sample_count = 0;
decoding = false;
configured = true;
}
if (message->id == Message::ID::ADSBConfigure) {
bit_count = 0;
sample_count = 0;
decoding = false;
configured = true;
}
}
#ifndef _WIN32
int main() {
EventDispatcher event_dispatcher { std::make_unique<ADSBRXProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<ADSBRXProcessor>()};
event_dispatcher.run();
return 0;
}
#endif

View File

@@ -34,30 +34,30 @@ using namespace adsb;
#define ADSB_PREAMBLE_LENGTH 16
class ADSBRXProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 2000000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
ADSBFrame frame { };
bool configured { false };
uint32_t prev_mag { 0 };
size_t bit_count { 0 }, sample_count { 0 };
size_t msgLen{ 112 };
uint32_t shifter[ADSB_PREAMBLE_LENGTH+1];
bool decoding { };
bool preamble { }, active { };
uint16_t bit_pos { 0 };
uint8_t cur_bit { 0 };
uint32_t sample { 0 };
int32_t re { }, im { };
int32_t amp {0};
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 2000000;
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
ADSBFrame frame{};
bool configured{false};
uint32_t prev_mag{0};
size_t bit_count{0}, sample_count{0};
size_t msgLen{112};
uint32_t shifter[ADSB_PREAMBLE_LENGTH + 1];
bool decoding{};
bool preamble{}, active{};
uint16_t bit_pos{0};
uint8_t cur_bit{0};
uint32_t sample{0};
int32_t re{}, im{};
int32_t amp{0};
};
#endif

View File

@@ -28,46 +28,45 @@
#include <cstdint>
void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 4M/2048 = 1953Hz
// One pulse = 500ns = 2 samples
// One bit = 2 pulses = 1us = 4 samples
// Test this with ./dump1090 --freq 434000000 --gain 20
// Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast
if (!configured) return;
// This is called at 4M/2048 = 1953Hz
// One pulse = 500ns = 2 samples
// One bit = 2 pulses = 1us = 4 samples
// Test this with ./dump1090 --freq 434000000 --gain 20
// Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast
for (size_t i = 0; i < buffer.count; i++) {
if (bit_pos >= (240 << 1)) {
configured = false;
cur_bit = 0;
} else {
cur_bit = shared_memory.bb_data.data[bit_pos >> 1];
bit_pos++;
}
if (cur_bit) {
// Crude AM
buffer.p[i] = am_lut[phase & 3];
phase++;
} else {
buffer.p[i] = { 0, 0 };
}
}
if (!configured) return;
for (size_t i = 0; i < buffer.count; i++) {
if (bit_pos >= (240 << 1)) {
configured = false;
cur_bit = 0;
} else {
cur_bit = shared_memory.bb_data.data[bit_pos >> 1];
bit_pos++;
}
if (cur_bit) {
// Crude AM
buffer.p[i] = am_lut[phase & 3];
phase++;
} else {
buffer.p[i] = {0, 0};
}
}
}
void ADSBTXProcessor::on_message(const Message* const p) {
const auto message = *reinterpret_cast<const ADSBConfigureMessage*>(p);
if (message.id == Message::ID::ADSBConfigure) {
bit_pos = 0;
phase = 0;
configured = true;
}
const auto message = *reinterpret_cast<const ADSBConfigureMessage*>(p);
if (message.id == Message::ID::ADSBConfigure) {
bit_pos = 0;
phase = 0;
configured = true;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<ADSBTXProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<ADSBTXProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -27,28 +27,27 @@
#include "baseband_thread.hpp"
class ADSBTXProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const p) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
bool configured = false;
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
const complex8_t am_lut[4] = {
{ 127, 0 },
{ 0, 127 },
{ -127, 0 },
{ 0, -127 }
};
uint32_t bit_pos { 0 };
uint32_t cur_bit { 0 };
uint32_t phase { 0 };
TXProgressMessage txprogress_message { };
void on_message(const Message* const p) override;
private:
bool configured = false;
BasebandThread baseband_thread{4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit};
const complex8_t am_lut[4] = {
{127, 0},
{0, 127},
{-127, 0},
{0, -127}};
uint32_t bit_pos{0};
uint32_t cur_bit{0};
uint32_t phase{0};
TXProgressMessage txprogress_message{};
};
#endif

View File

@@ -28,97 +28,95 @@
#include <cstdint>
void AFSKProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 2.28M/2048 = 1113Hz
if (!configured) return;
for (size_t i = 0; i<buffer.count; i++) {
// This is called at 2.28M/2048 = 1113Hz
if (sample_count >= afsk_samples_per_bit) {
if (configured) {
cur_word = *word_ptr;
if (!cur_word) {
// End of data
if (repeat_counter < afsk_repeat) {
// Repeat
bit_pos = 0;
word_ptr = (uint16_t*)shared_memory.bb_data.data;
cur_word = *word_ptr;
txprogress_message.done = false;
txprogress_message.progress = repeat_counter + 1;
shared_memory.application_queue.push(txprogress_message);
repeat_counter++;
} else {
// Stop
cur_word = 0;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
configured = false;
}
}
}
cur_bit = (cur_word >> (symbol_count - bit_pos)) & 1;
if (!configured) return;
if (bit_pos >= symbol_count) {
bit_pos = 0;
word_ptr++;
} else {
bit_pos++;
}
sample_count = 0;
} else {
sample_count++;
}
if (cur_bit)
tone_phase += afsk_phase_inc_mark;
else
tone_phase += afsk_phase_inc_space;
for (size_t i = 0; i < buffer.count; i++) {
if (sample_count >= afsk_samples_per_bit) {
if (configured) {
cur_word = *word_ptr;
tone_sample = sine_table_i8[(tone_phase & 0xFF000000U) >> 24];
if (!cur_word) {
// End of data
if (repeat_counter < afsk_repeat) {
// Repeat
bit_pos = 0;
word_ptr = (uint16_t*)shared_memory.bb_data.data;
cur_word = *word_ptr;
txprogress_message.done = false;
txprogress_message.progress = repeat_counter + 1;
shared_memory.application_queue.push(txprogress_message);
repeat_counter++;
} else {
// Stop
cur_word = 0;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
configured = false;
}
}
}
delta = tone_sample * fm_delta;
phase += delta;
sphase = phase + (64 << 24);
cur_bit = (cur_word >> (symbol_count - bit_pos)) & 1;
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
buffer.p[i] = {re, im};
}
if (bit_pos >= symbol_count) {
bit_pos = 0;
word_ptr++;
} else {
bit_pos++;
}
sample_count = 0;
} else {
sample_count++;
}
if (cur_bit)
tone_phase += afsk_phase_inc_mark;
else
tone_phase += afsk_phase_inc_space;
tone_sample = sine_table_i8[(tone_phase & 0xFF000000U) >> 24];
delta = tone_sample * fm_delta;
phase += delta;
sphase = phase + (64 << 24);
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
buffer.p[i] = {re, im};
}
}
void AFSKProcessor::on_message(const Message* const msg) {
const auto message = *reinterpret_cast<const AFSKTxConfigureMessage*>(msg);
if (message.id == Message::ID::AFSKTxConfigure) {
if (message.samples_per_bit) {
afsk_samples_per_bit = message.samples_per_bit;
afsk_phase_inc_mark = message.phase_inc_mark * AFSK_DELTA_COEF;
afsk_phase_inc_space = message.phase_inc_space * AFSK_DELTA_COEF;
afsk_repeat = message.repeat - 1;
fm_delta = message.fm_delta * (0xFFFFFFULL / AFSK_SAMPLERATE);
symbol_count = message.symbol_count - 1;
const auto message = *reinterpret_cast<const AFSKTxConfigureMessage*>(msg);
sample_count = afsk_samples_per_bit;
repeat_counter = 0;
bit_pos = 0;
word_ptr = (uint16_t*)shared_memory.bb_data.data;
cur_word = 0;
cur_bit = 0;
configured = true;
} else
configured = false; // Kill
}
if (message.id == Message::ID::AFSKTxConfigure) {
if (message.samples_per_bit) {
afsk_samples_per_bit = message.samples_per_bit;
afsk_phase_inc_mark = message.phase_inc_mark * AFSK_DELTA_COEF;
afsk_phase_inc_space = message.phase_inc_space * AFSK_DELTA_COEF;
afsk_repeat = message.repeat - 1;
fm_delta = message.fm_delta * (0xFFFFFFULL / AFSK_SAMPLERATE);
symbol_count = message.symbol_count - 1;
sample_count = afsk_samples_per_bit;
repeat_counter = 0;
bit_pos = 0;
word_ptr = (uint16_t*)shared_memory.bb_data.data;
cur_word = 0;
cur_bit = 0;
configured = true;
} else
configured = false; // Kill
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<AFSKProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<AFSKProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -30,35 +30,35 @@
#define AFSK_DELTA_COEF ((1ULL << 32) / AFSK_SAMPLERATE)
class AFSKProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const msg) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
bool configured = false;
BasebandThread baseband_thread { AFSK_SAMPLERATE, this, NORMALPRIO + 20, baseband::Direction::Transmit };
uint32_t afsk_samples_per_bit { 0 };
uint32_t afsk_phase_inc_mark { 0 };
uint32_t afsk_phase_inc_space { 0 };
uint8_t afsk_repeat { 0 };
uint32_t fm_delta { 0 };
uint8_t symbol_count { 0 };
uint8_t repeat_counter { 0 };
uint8_t bit_pos { 0 };
uint16_t * word_ptr { };
uint16_t cur_word { 0 };
uint8_t cur_bit { 0 };
uint32_t sample_count { 0 };
uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 };
int32_t tone_sample { 0 }, delta { 0 };
int8_t re { 0 }, im { 0 };
TXProgressMessage txprogress_message { };
void on_message(const Message* const msg) override;
private:
bool configured = false;
BasebandThread baseband_thread{AFSK_SAMPLERATE, this, NORMALPRIO + 20, baseband::Direction::Transmit};
uint32_t afsk_samples_per_bit{0};
uint32_t afsk_phase_inc_mark{0};
uint32_t afsk_phase_inc_space{0};
uint8_t afsk_repeat{0};
uint32_t fm_delta{0};
uint8_t symbol_count{0};
uint8_t repeat_counter{0};
uint8_t bit_pos{0};
uint16_t* word_ptr{};
uint16_t cur_word{0};
uint8_t cur_bit{0};
uint32_t sample_count{0};
uint32_t tone_phase{0}, phase{0}, sphase{0};
int32_t tone_sample{0}, delta{0};
int8_t re{0}, im{0};
TXProgressMessage txprogress_message{};
};
#endif

View File

@@ -26,166 +26,162 @@
#include "event_m4.hpp"
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 3072000 / 2048 = 1500Hz
// This is called at 3072000 / 2048 = 1500Hz
if (!configured) return;
// FM demodulation
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
if (!configured) return;
feed_channel_stats(channel_out);
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
// FM demodulation
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
// Audio signal processing
for (size_t c = 0; c < audio.count; c++) {
feed_channel_stats(channel_out);
const int32_t sample_int = audio.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;
// Delay line put
delay_line[delay_line_index & 0x3F] = current_sample;
// Delay line get, and LPF
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4;
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
delay_line_index++;
prev_filtered = sample_filtered;
prev_mixed = sample_mixed;
// Slice
sample_bits <<= 1;
sample_bits |= (sample_filtered < -20) ? 1 : 0;
// Check for "clean" transition: either 0011 or 1100
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
// Adjust phase
if (phase < 0x8000)
phase += 0x800; // Is this a proper value ?
else
phase -= 0x800;
}
phase += phase_inc;
if (phase >= 0x10000) {
phase &= 0xFFFF;
if (trigger_word) {
// Continuous-stream value-triggered mode (AX.25) - UNTESTED
word_bits <<= 1;
word_bits |= (sample_bits & 1);
bit_counter++;
if (triggered) {
if (bit_counter == word_length) {
bit_counter = 0;
data_message.is_data = true;
data_message.value = word_bits & word_mask;
shared_memory.application_queue.push(data_message);
}
} else {
if ((word_bits & word_mask) == trigger_value) {
triggered = !triggered;
bit_counter = 0;
data_message.is_data = true;
data_message.value = trigger_value;
shared_memory.application_queue.push(data_message);
}
}
} else {
// RS232-like modem mode
if (state == WAIT_START) {
if (!(sample_bits & 1)) {
// Got start bit
state = RECEIVE;
bit_counter = 0;
}
} else if (state == WAIT_STOP) {
if (sample_bits & 1) {
// Got stop bit
state = WAIT_START;
}
} else {
word_bits <<= 1;
word_bits |= (sample_bits & 1);
bit_counter++;
}
if (bit_counter == word_length) {
bit_counter = 0;
state = WAIT_STOP;
data_message.is_data = true;
data_message.value = word_bits;
shared_memory.application_queue.push(data_message);
}
}
}
}
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
// Audio signal processing
for (size_t c = 0; c < audio.count; c++) {
const int32_t sample_int = audio.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;
// Delay line put
delay_line[delay_line_index & 0x3F] = current_sample;
// Delay line get, and LPF
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit / 2)) & 0x3F] * current_sample) / 4;
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
delay_line_index++;
prev_filtered = sample_filtered;
prev_mixed = sample_mixed;
// Slice
sample_bits <<= 1;
sample_bits |= (sample_filtered < -20) ? 1 : 0;
// Check for "clean" transition: either 0011 or 1100
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
// Adjust phase
if (phase < 0x8000)
phase += 0x800; // Is this a proper value ?
else
phase -= 0x800;
}
phase += phase_inc;
if (phase >= 0x10000) {
phase &= 0xFFFF;
if (trigger_word) {
// Continuous-stream value-triggered mode (AX.25) - UNTESTED
word_bits <<= 1;
word_bits |= (sample_bits & 1);
bit_counter++;
if (triggered) {
if (bit_counter == word_length) {
bit_counter = 0;
data_message.is_data = true;
data_message.value = word_bits & word_mask;
shared_memory.application_queue.push(data_message);
}
} else {
if ((word_bits & word_mask) == trigger_value) {
triggered = !triggered;
bit_counter = 0;
data_message.is_data = true;
data_message.value = trigger_value;
shared_memory.application_queue.push(data_message);
}
}
} else {
// RS232-like modem mode
if (state == WAIT_START) {
if (!(sample_bits & 1)) {
// Got start bit
state = RECEIVE;
bit_counter = 0;
}
} else if (state == WAIT_STOP) {
if (sample_bits & 1) {
// Got stop bit
state = WAIT_START;
}
} else {
word_bits <<= 1;
word_bits |= (sample_bits & 1);
bit_counter++;
}
if (bit_counter == word_length) {
bit_counter = 0;
state = WAIT_STOP;
data_message.is_data = true;
data_message.value = word_bits;
shared_memory.application_queue.push(data_message);
}
}
}
}
}
void AFSKRxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::AFSKRxConfigure)
configure(*reinterpret_cast<const AFSKRxConfigureMessage*>(message));
if (message->id == Message::ID::AFSKRxConfigure)
configure(*reinterpret_cast<const AFSKRxConfigureMessage*>(message));
}
void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) {
/*constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
/*constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
const size_t demod_input_fs = channel_filter_output_fs;*/
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(audio_fs, 5000);
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
samples_per_bit = audio_fs / message.baudrate;
phase_inc = (0x10000 * message.baudrate) / audio_fs;
phase = 0;
trigger_word = message.trigger_word;
word_length = message.word_length;
trigger_value = message.trigger_value;
word_mask = (1 << word_length) - 1;
// Delay line
delay_line_index = 0;
triggered = false;
state = WAIT_START;
configured = true;
const size_t demod_input_fs = channel_filter_output_fs;*/
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(audio_fs, 5000);
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
samples_per_bit = audio_fs / message.baudrate;
phase_inc = (0x10000 * message.baudrate) / audio_fs;
phase = 0;
trigger_word = message.trigger_word;
word_length = message.word_length;
trigger_value = message.trigger_value;
word_mask = (1 << word_length) - 1;
// Delay line
delay_line_index = 0;
triggered = false;
state = WAIT_START;
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<AFSKRxProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<AFSKRxProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -36,68 +36,66 @@
#include "message.hpp"
class AFSKRxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
size_t samples_per_bit { };
enum State {
WAIT_START = 0,
WAIT_STOP,
RECEIVE
};
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<float, 32> audio { };
const buffer_f32_t audio_buffer {
audio.data(),
audio.size()
};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
dsp::demodulate::FM demod { };
AudioOutput audio_output { };
void on_message(const Message* const message) override;
State state { };
size_t delay_line_index { };
uint32_t bit_counter { 0 };
uint32_t word_bits { 0 };
uint32_t sample_bits { 0 };
uint32_t phase { }, phase_inc { };
int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { };
uint32_t word_length { };
uint32_t word_mask { };
uint32_t trigger_value { };
bool configured { false };
bool wait_start { };
bool bit_value { };
bool trigger_word { };
bool triggered { };
void configure(const AFSKRxConfigureMessage& message);
AFSKDataMessage data_message { false, 0 };
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
size_t samples_per_bit{};
enum State {
WAIT_START = 0,
WAIT_STOP,
RECEIVE
};
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
std::array<float, 32> audio{};
const buffer_f32_t audio_buffer{
audio.data(),
audio.size()};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line{0};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::decimate::FIRAndDecimateComplex channel_filter{};
dsp::demodulate::FM demod{};
AudioOutput audio_output{};
State state{};
size_t delay_line_index{};
uint32_t bit_counter{0};
uint32_t word_bits{0};
uint32_t sample_bits{0};
uint32_t phase{}, phase_inc{};
int32_t sample_mixed{}, prev_mixed{}, sample_filtered{}, prev_filtered{};
uint32_t word_length{};
uint32_t word_mask{};
uint32_t trigger_value{};
bool configured{false};
bool wait_start{};
bool bit_value{};
bool trigger_word{};
bool triggered{};
void configure(const AFSKRxConfigureMessage& message);
AFSKDataMessage data_message{false, 0};
};
#endif/*__PROC_TPMS_H__*/
#endif /*__PROC_TPMS_H__*/

View File

@@ -28,45 +28,43 @@
#include "event_m4.hpp"
AISProcessor::AISProcessor() {
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
}
void AISProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto decimator_out = decim_1_out;
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto decimator_out = decim_1_out;
/* 38.4kHz, 32 samples */
feed_channel_stats(decimator_out);
/* 38.4kHz, 32 samples */
feed_channel_stats(decimator_out);
for(size_t i=0; i<decimator_out.count; i++) {
if( mf.execute_once(decimator_out.p[i]) ) {
clock_recovery(mf.get_output());
}
}
for (size_t i = 0; i < decimator_out.count; i++) {
if (mf.execute_once(decimator_out.p[i])) {
clock_recovery(mf.get_output());
}
}
}
void AISProcessor::consume_symbol(
const float raw_symbol
) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
const auto decoded_symbol = nrzi_decode(sliced_symbol);
const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
const auto decoded_symbol = nrzi_decode(sliced_symbol);
packet_builder.execute(decoded_symbol);
packet_builder.execute(decoded_symbol);
}
void AISProcessor::payload_handler(
const baseband::Packet& packet
) {
const AISPacketMessage message { packet };
shared_memory.application_queue.push(message);
const baseband::Packet& packet) {
const AISPacketMessage message{packet};
shared_memory.application_queue.push(message);
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<AISProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<AISProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -43,43 +43,42 @@
#include "ais_baseband.hpp"
class AISProcessor : public BasebandProcessor {
public:
AISProcessor();
public:
AISProcessor();
void execute(const buffer_c8_t& buffer) override;
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 2457600;
private:
static constexpr size_t baseband_fs = 2457600;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::matched_filter::MatchedFilter mf{baseband::ais::square_taps_38k4_1t_p, 2};
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
19200, 9600, { 0.0555f },
[this](const float symbol) { this->consume_symbol(symbol); }
};
symbol_coding::NRZIDecoder nrzi_decode { };
PacketBuilder<BitPattern, BitPattern, BitPattern> packet_builder {
{ 0b0101010101111110, 16, 1 },
{ 0b111110, 6 },
{ 0b01111110, 8 },
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
}
};
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery{
19200,
9600,
{0.0555f},
[this](const float symbol) { this->consume_symbol(symbol); }};
symbol_coding::NRZIDecoder nrzi_decode{};
PacketBuilder<BitPattern, BitPattern, BitPattern> packet_builder{
{0b0101010101111110, 16, 1},
{0b111110, 6},
{0b01111110, 8},
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
}};
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
};
#endif/*__PROC_AIS_H__*/
#endif /*__PROC_AIS_H__*/

View File

@@ -28,91 +28,91 @@
#include <array>
void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
if( !configured ) {
return;
}
if (!configured) {
return;
}
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
// TODO: Feed channel_stats post-decimation data?
feed_channel_stats(channel_out);
// TODO: Feed channel_stats post-decimation data?
feed_channel_stats(channel_out);
auto audio = demodulate(channel_out);
audio_compressor.execute_in_place(audio);
audio_output.write(audio);
auto audio = demodulate(channel_out);
audio_compressor.execute_in_place(audio);
audio_output.write(audio);
}
buffer_f32_t NarrowbandAMAudio::demodulate(const buffer_c16_t& channel) {
if( modulation_ssb ) {
return demod_ssb.execute(channel, audio_buffer);
} else {
return demod_am.execute(channel, audio_buffer);
}
if (modulation_ssb) {
return demod_ssb.execute(channel, audio_buffer);
} else {
return demod_am.execute(channel, audio_buffer);
}
}
void NarrowbandAMAudio::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
switch (message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
case Message::ID::AMConfigure:
configure(*reinterpret_cast<const AMConfigureMessage*>(message));
break;
case Message::ID::AMConfigure:
configure(*reinterpret_cast<const AMConfigureMessage*>(message));
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
}
void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t decim_2_input_fs = decim_1_output_fs;
constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor;
constexpr size_t decim_2_input_fs = decim_1_output_fs;
constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor;
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
//const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
// const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
decim_0.configure(message.decim_0_filter.taps, 33554432);
decim_1.configure(message.decim_1_filter.taps, 131072);
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(1.0f);
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
audio_output.configure(message.audio_hpf_config);
decim_0.configure(message.decim_0_filter.taps, 33554432);
decim_1.configure(message.decim_1_filter.taps, 131072);
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(1.0f);
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
audio_output.configure(message.audio_hpf_config);
configured = true;
configured = true;
}
void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
if (message.config) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NarrowbandAMAudio>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<NarrowbandAMAudio>()};
event_dispatcher.run();
return 0;
}

View File

@@ -36,51 +36,49 @@
#include <cstdint>
class NarrowbandAMAudio : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t decim_2_decimation_factor = 4;
static constexpr size_t channel_filter_decimation_factor = 1;
void on_message(const Message* const message) override;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t decim_2_decimation_factor = 4;
static constexpr size_t channel_filter_decimation_factor = 1;
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<float, 32> audio { };
const buffer_f32_t audio_buffer {
audio.data(),
audio.size()
};
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex decim_2 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
std::array<float, 32> audio{};
const buffer_f32_t audio_buffer{
audio.data(),
audio.size()};
bool modulation_ssb = false;
dsp::demodulate::AM demod_am { };
dsp::demodulate::SSB demod_ssb { };
FeedForwardCompressor audio_compressor { };
AudioOutput audio_output { };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::decimate::FIRAndDecimateComplex decim_2{};
dsp::decimate::FIRAndDecimateComplex channel_filter{};
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
SpectrumCollector channel_spectrum { };
bool modulation_ssb = false;
dsp::demodulate::AM demod_am{};
dsp::demodulate::SSB demod_ssb{};
FeedForwardCompressor audio_compressor{};
AudioOutput audio_output{};
bool configured { false };
void configure(const AMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
SpectrumCollector channel_spectrum{};
buffer_f32_t demodulate(const buffer_c16_t& channel);
bool configured{false};
void configure(const AMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
buffer_f32_t demodulate(const buffer_c16_t& channel);
};
#endif/*__PROC_AM_AUDIO_H__*/
#endif /*__PROC_AM_AUDIO_H__*/

View File

@@ -30,59 +30,57 @@
#include <cstdint>
void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
if( !configured ) {
return;
}
std::fill(spectrum.begin(), spectrum.end(), 0);
if (!configured) {
return;
}
for(size_t i=0; i<spectrum.size(); i++) {
spectrum[i] += buffer.p[i];
}
std::fill(spectrum.begin(), spectrum.end(), 0);
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
channel_spectrum.feed(buffer_c16);
for (size_t i = 0; i < spectrum.size(); i++) {
spectrum[i] += buffer.p[i];
}
int8_t re ;
//int8_t im;
//int8_t mag;
const buffer_c16_t buffer_c16{spectrum.data(), spectrum.size(), buffer.sampling_rate};
channel_spectrum.feed(buffer_c16);
for (size_t i = 0; i < 128; i++)
{
re = buffer.p[i].real();
//im = buffer.p[i].imag();
//mag = __builtin_sqrtf((re * re) + (im * im)) ;
const unsigned int v = re + 127.0f; //timescope
audio_spectrum.db[i] = std::max(0U, std::min(255U, v));
}
AudioSpectrumMessage message { &audio_spectrum };
shared_memory.application_queue.push(message);
int8_t re;
// int8_t im;
// int8_t mag;
for (size_t i = 0; i < 128; i++) {
re = buffer.p[i].real();
// im = buffer.p[i].imag();
// mag = __builtin_sqrtf((re * re) + (im * im)) ;
const unsigned int v = re + 127.0f; // timescope
audio_spectrum.db[i] = std::max(0U, std::min(255U, v));
}
AudioSpectrumMessage message{&audio_spectrum};
shared_memory.application_queue.push(message);
}
void WidebandFMAudio::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
switch (message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
case Message::ID::WFMConfigure:
configure(*reinterpret_cast<const WFMConfigureMessage*>(message));
break;
default:
break;
}
case Message::ID::WFMConfigure:
configure(*reinterpret_cast<const WFMConfigureMessage*>(message));
break;
default:
break;
}
}
void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
(void)message; // avoid warning
configured = true;
(void)message; // avoid warning
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<WidebandFMAudio>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<WidebandFMAudio>()};
event_dispatcher.run();
return 0;
}

View File

@@ -36,30 +36,28 @@
#include "tv_collector.hpp"
class WidebandFMAudio : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 2000000;
private:
static constexpr size_t baseband_fs = 2000000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
AudioSpectrum audio_spectrum { };
TvCollector channel_spectrum { };
std::array<complex16_t, 256> spectrum { };
bool configured { false };
void configure(const WFMConfigureMessage& message);
AudioSpectrum audio_spectrum{};
TvCollector channel_spectrum{};
std::array<complex16_t, 256> spectrum{};
bool configured{false};
void configure(const WFMConfigureMessage& message);
};
#endif/*__PROC_AM_TV_H__*/
#endif /*__PROC_AM_TV_H__*/

View File

@@ -28,230 +28,223 @@
#include "stdio.h"
void APRSRxProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 3072000 / 2048 = 1500Hz
// This is called at 3072000 / 2048 = 1500Hz
if (!configured) return;
// FM demodulation
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
if (!configured) return;
feed_channel_stats(channel_out);
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
// FM demodulation
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
// Audio signal processing
for (size_t c = 0; c < audio.count; c++) {
feed_channel_stats(channel_out);
const int32_t sample_int = audio.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;
// Delay line put
delay_line[delay_line_index & 0x3F] = current_sample;
// Delay line get, and LPF
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4;
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
delay_line_index++;
prev_filtered = sample_filtered;
prev_mixed = sample_mixed;
auto audio = demod.execute(channel_out, audio_buffer);
// Slice
sample_bits <<= 1;
audio_output.write(audio);
uint8_t bit = (sample_filtered < -20) ? 1 : 0;
sample_bits |= bit;
// Audio signal processing
for (size_t c = 0; c < audio.count; c++) {
const int32_t sample_int = audio.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
/*
int16_t scaled = bit == 1 ? 32767 : -32767;
current_sample /= 128;
if( stream ) {
const size_t bytes_to_write = sizeof(scaled) * 1;
const auto result = stream->write(&scaled, bytes_to_write);
}
// Delay line put
delay_line[delay_line_index & 0x3F] = current_sample;
// Delay line get, and LPF
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit / 2)) & 0x3F] * current_sample) / 4;
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
delay_line_index++;
prev_filtered = sample_filtered;
prev_mixed = sample_mixed;
// Slice
sample_bits <<= 1;
uint8_t bit = (sample_filtered < -20) ? 1 : 0;
sample_bits |= bit;
/*
int16_t scaled = bit == 1 ? 32767 : -32767;
if( stream ) {
const size_t bytes_to_write = sizeof(scaled) * 1;
const auto result = stream->write(&scaled, bytes_to_write);
}
*/
// Check for "clean" transition: either 0011 or 1100
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
// Adjust phase
if (phase < 0x8000)
phase += 0x800; // Is this a proper value ?
else
phase -= 0x800;
}
phase += phase_inc;
if (phase >= 0x10000) {
phase &= 0xFFFF;
if (true) {
uint8_t bit;
if(__builtin_popcount(sample_bits & 0xFF) >= 0x05){
bit = 0x1;
}
else {
bit = 0x0;
}
if(parse_bit(bit)){
parse_packet();
}
}
}
}
// Check for "clean" transition: either 0011 or 1100
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
// Adjust phase
if (phase < 0x8000)
phase += 0x800; // Is this a proper value ?
else
phase -= 0x800;
}
phase += phase_inc;
if (phase >= 0x10000) {
phase &= 0xFFFF;
if (true) {
uint8_t bit;
if (__builtin_popcount(sample_bits & 0xFF) >= 0x05) {
bit = 0x1;
} else {
bit = 0x0;
}
if (parse_bit(bit)) {
parse_packet();
}
}
}
}
}
void APRSRxProcessor::parse_packet(){
//validate crc
if(packet_buffer_size >= aprs::APRS_MIN_LENGTH){
uint16_t crc = 0xFFFF;
void APRSRxProcessor::parse_packet() {
// validate crc
if (packet_buffer_size >= aprs::APRS_MIN_LENGTH) {
uint16_t crc = 0xFFFF;
for(size_t i = 0; i < packet_buffer_size; i++){
uint8_t byte = packet_buffer[i];
crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF;
}
for (size_t i = 0; i < packet_buffer_size; i++) {
uint8_t byte = packet_buffer[i];
crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF;
}
if(crc == 0xF0B8){
parse_ax25();
}
}
if (crc == 0xF0B8) {
parse_ax25();
}
}
}
void APRSRxProcessor::parse_ax25(){
aprs_packet.clear();
aprs_packet.set_valid_checksum(true);
void APRSRxProcessor::parse_ax25() {
aprs_packet.clear();
aprs_packet.set_valid_checksum(true);
for(size_t i = 0; i < packet_buffer_size; i++){
aprs_packet.set(i, packet_buffer[i]);
}
for (size_t i = 0; i < packet_buffer_size; i++) {
aprs_packet.set(i, packet_buffer[i]);
}
APRSPacketMessage packet_message { aprs_packet };
shared_memory.application_queue.push(packet_message);
APRSPacketMessage packet_message{aprs_packet};
shared_memory.application_queue.push(packet_message);
}
bool APRSRxProcessor::parse_bit(const uint8_t current_bit){
uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1;
last_bit = current_bit;
bool APRSRxProcessor::parse_bit(const uint8_t current_bit) {
uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1;
last_bit = current_bit;
//int16_t log = decoded_bit == 0 ? -32768 : 32767;
//if(stream){
// const size_t bytes_to_write = sizeof(log) * 1;
// const auto result = stream->write(&log, bytes_to_write);
//}
// int16_t log = decoded_bit == 0 ? -32768 : 32767;
// if(stream){
// const size_t bytes_to_write = sizeof(log) * 1;
// const auto result = stream->write(&log, bytes_to_write);
// }
if(decoded_bit & 0x1){
if(ones_count < 8){
ones_count++;
}
}
else {
if(ones_count > 6){ //not valid
state = WAIT_FLAG;
current_byte = 0;
ones_count = 0;
byte_index = 0;
packet_buffer_size = 0;
return false;
}
else if(ones_count == 6){ //flag
bool done = false;
if(state == IN_FRAME){
done = true;
}
else {
packet_buffer_size = 0;
}
state = WAIT_FRAME;
current_byte = 0;
ones_count = 0;
byte_index = 0;
if (decoded_bit & 0x1) {
if (ones_count < 8) {
ones_count++;
}
} else {
if (ones_count > 6) { // not valid
state = WAIT_FLAG;
current_byte = 0;
ones_count = 0;
byte_index = 0;
packet_buffer_size = 0;
return false;
} else if (ones_count == 6) { // flag
bool done = false;
if (state == IN_FRAME) {
done = true;
} else {
packet_buffer_size = 0;
}
state = WAIT_FRAME;
current_byte = 0;
ones_count = 0;
byte_index = 0;
return done;
}
else if(ones_count == 5){ //bit stuff
ones_count = 0;
return false;
}
else {
ones_count = 0;
}
}
return done;
} else if (ones_count == 5) { // bit stuff
ones_count = 0;
return false;
} else {
ones_count = 0;
}
}
//store
current_byte = current_byte >> 1;
current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0);
byte_index++;
// store
current_byte = current_byte >> 1;
current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0);
byte_index++;
if(byte_index >= 8){
byte_index = 0;
if(state == WAIT_FRAME){
state = IN_FRAME;
}
if (byte_index >= 8) {
byte_index = 0;
if (state == WAIT_FRAME) {
state = IN_FRAME;
}
if(state == IN_FRAME){
if(packet_buffer_size + 1 >= 256){
state = WAIT_FLAG;
current_byte = 0;
ones_count = 0;
byte_index = 0;
packet_buffer_size = 0;
return false;
}
packet_buffer[packet_buffer_size++] = current_byte;
}
}
if (state == IN_FRAME) {
if (packet_buffer_size + 1 >= 256) {
state = WAIT_FLAG;
current_byte = 0;
ones_count = 0;
byte_index = 0;
packet_buffer_size = 0;
return false;
}
packet_buffer[packet_buffer_size++] = current_byte;
}
}
return false;
return false;
}
void APRSRxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::APRSRxConfigure)
configure(*reinterpret_cast<const APRSRxConfigureMessage*>(message));
if(message->id == Message::ID::CaptureConfig)
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
if (message->id == Message::ID::APRSRxConfigure)
configure(*reinterpret_cast<const APRSRxConfigureMessage*>(message));
if (message->id == Message::ID::CaptureConfig)
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
}
void APRSRxProcessor::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
//stream = std::make_unique<StreamInput>(message.config);
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
//stream.reset();
audio_output.set_stream(nullptr);
}
if (message.config) {
// stream = std::make_unique<StreamInput>(message.config);
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
// stream.reset();
audio_output.set_stream(nullptr);
}
}
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(audio_fs, 5000);
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(audio_fs, 5000);
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
samples_per_bit = audio_fs / message.baudrate;
phase_inc = (0x10000 * message.baudrate) / audio_fs;
phase = 0;
// Delay line
delay_line_index = 0;
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
state = WAIT_FLAG;
configured = true;
samples_per_bit = audio_fs / message.baudrate;
phase_inc = (0x10000 * message.baudrate) / audio_fs;
phase = 0;
// Delay line
delay_line_index = 0;
state = WAIT_FLAG;
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<APRSRxProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<APRSRxProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -36,113 +36,110 @@
#include "fifo.hpp"
#include "message.hpp"
#include "aprs_packet.hpp"
#include "aprs_packet.hpp"
static uint16_t crc_ccitt_tab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};
class APRSRxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
size_t samples_per_bit { };
enum State {
WAIT_FLAG,
WAIT_FRAME,
IN_FRAME
};
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<float, 32> audio { };
const buffer_f32_t audio_buffer {
audio.data(),
audio.size()
};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
std::unique_ptr<StreamInput> stream { };
void on_message(const Message* const message) override;
dsp::demodulate::FM demod { };
AudioOutput audio_output { };
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
State state { };
size_t delay_line_index { };
uint32_t bit_counter { 0 };
uint32_t word_bits { 0 };
uint32_t sample_bits { 0 };
uint32_t phase { }, phase_inc { };
int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { };
uint8_t last_bit = 0;
uint8_t ones_count = 0 ;
uint8_t current_byte = 0;
uint8_t byte_index = 0;
uint8_t packet_buffer[256];
size_t packet_buffer_size = 0;
size_t samples_per_bit{};
bool configured { false };
bool wait_start { 0 };
bool bit_value { 0 };
enum State {
WAIT_FLAG,
WAIT_FRAME,
IN_FRAME
};
aprs::APRSPacket aprs_packet { };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
void configure(const APRSRxConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
void parse_packet();
bool parse_bit(const uint8_t bit);
void parse_ax25();
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
std::array<float, 32> audio{};
const buffer_f32_t audio_buffer{
audio.data(),
audio.size()};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line{0};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::decimate::FIRAndDecimateComplex channel_filter{};
std::unique_ptr<StreamInput> stream{};
dsp::demodulate::FM demod{};
AudioOutput audio_output{};
State state{};
size_t delay_line_index{};
uint32_t bit_counter{0};
uint32_t word_bits{0};
uint32_t sample_bits{0};
uint32_t phase{}, phase_inc{};
int32_t sample_mixed{}, prev_mixed{}, sample_filtered{}, prev_filtered{};
uint8_t last_bit = 0;
uint8_t ones_count = 0;
uint8_t current_byte = 0;
uint8_t byte_index = 0;
uint8_t packet_buffer[256];
size_t packet_buffer_size = 0;
bool configured{false};
bool wait_start{0};
bool bit_value{0};
aprs::APRSPacket aprs_packet{};
void configure(const APRSRxConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
void parse_packet();
bool parse_bit(const uint8_t bit);
void parse_ax25();
};
#endif/*__PROC_TPMS_H__*/
#endif /*__PROC_TPMS_H__*/

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
@@ -27,95 +27,93 @@
#include <cstdint>
void AudioTXProcessor::execute(const buffer_c8_t& buffer){
if (!configured) return;
// Zero-order hold (poop)
for (size_t i = 0; i < buffer.count; i++) {
resample_acc += resample_inc;
if (resample_acc >= 0x10000) {
resample_acc -= 0x10000;
if (stream) {
stream->read(&audio_sample, 1);
bytes_read++;
}
}
sample = tone_gen.process(audio_sample - 0x80);
// FM
delta = sample * fm_delta;
phase += delta;
sphase = phase + (64 << 24);
re = sine_table_i8[(sphase & 0xFF000000U) >> 24];
im = sine_table_i8[(phase & 0xFF000000U) >> 24];
buffer.p[i] = { (int8_t)re, (int8_t)im };
}
progress_samples += buffer.count;
if (progress_samples >= progress_interval_samples) {
progress_samples -= progress_interval_samples;
txprogress_message.progress = bytes_read; // Inform UI about progress
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
}
void AudioTXProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
// Zero-order hold (poop)
for (size_t i = 0; i < buffer.count; i++) {
resample_acc += resample_inc;
if (resample_acc >= 0x10000) {
resample_acc -= 0x10000;
if (stream) {
stream->read(&audio_sample, 1);
bytes_read++;
}
}
sample = tone_gen.process(audio_sample - 0x80);
// FM
delta = sample * fm_delta;
phase += delta;
sphase = phase + (64 << 24);
re = sine_table_i8[(sphase & 0xFF000000U) >> 24];
im = sine_table_i8[(phase & 0xFF000000U) >> 24];
buffer.p[i] = {(int8_t)re, (int8_t)im};
}
progress_samples += buffer.count;
if (progress_samples >= progress_interval_samples) {
progress_samples -= progress_interval_samples;
txprogress_message.progress = bytes_read; // Inform UI about progress
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
}
}
void AudioTXProcessor::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::AudioTXConfig:
audio_config(*reinterpret_cast<const AudioTXConfigMessage*>(message));
break;
switch (message->id) {
case Message::ID::AudioTXConfig:
audio_config(*reinterpret_cast<const AudioTXConfigMessage*>(message));
break;
case Message::ID::ReplayConfig:
configured = false;
bytes_read = 0;
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
break;
case Message::ID::SamplerateConfig:
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
break;
case Message::ID::FIFOData:
configured = true;
break;
default:
break;
}
case Message::ID::ReplayConfig:
configured = false;
bytes_read = 0;
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
break;
case Message::ID::SamplerateConfig:
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
break;
case Message::ID::FIFOData:
configured = true;
break;
default:
break;
}
}
void AudioTXProcessor::audio_config(const AudioTXConfigMessage& message) {
fm_delta = message.deviation_hz * (0xFFFFFFULL / baseband_fs);
tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight);
progress_interval_samples = message.divider;
resample_acc = 0;
fm_delta = message.deviation_hz * (0xFFFFFFULL / baseband_fs);
tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight);
progress_interval_samples = message.divider;
resample_acc = 0;
}
void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) {
if( message.config ) {
stream = std::make_unique<StreamOutput>(message.config);
// Tell application that the buffers and FIFO pointers are ready, prefill
shared_memory.application_queue.push(sig_message);
} else {
stream.reset();
}
if (message.config) {
stream = std::make_unique<StreamOutput>(message.config);
// Tell application that the buffers and FIFO pointers are ready, prefill
shared_memory.application_queue.push(sig_message);
} else {
stream.reset();
}
}
void AudioTXProcessor::samplerate_config(const SamplerateConfigMessage& message) {
resample_inc = (((uint64_t)message.sample_rate) << 16) / baseband_fs; // 16.16 fixed point message.sample_rate
resample_inc = (((uint64_t)message.sample_rate) << 16) / baseband_fs; // 16.16 fixed point message.sample_rate
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<AudioTXProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<AudioTXProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
@@ -29,38 +29,38 @@
#include "stream_output.hpp"
class AudioTXProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const msg) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 1536000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
std::unique_ptr<StreamOutput> stream { };
ToneGen tone_gen { };
uint32_t resample_inc { }, resample_acc { };
uint32_t fm_delta { 0 };
uint32_t phase { 0 }, sphase { 0 };
uint8_t audio_sample { };
int32_t sample { 0 }, delta { };
int8_t re { 0 }, im { 0 };
size_t progress_interval_samples = 0 , progress_samples = 0;
bool configured { false };
uint32_t bytes_read { 0 };
void samplerate_config(const SamplerateConfigMessage& message);
void audio_config(const AudioTXConfigMessage& message);
void replay_config(const ReplayConfigMessage& message);
TXProgressMessage txprogress_message { };
RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest };
void on_message(const Message* const msg) override;
private:
static constexpr size_t baseband_fs = 1536000;
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit};
std::unique_ptr<StreamOutput> stream{};
ToneGen tone_gen{};
uint32_t resample_inc{}, resample_acc{};
uint32_t fm_delta{0};
uint32_t phase{0}, sphase{0};
uint8_t audio_sample{};
int32_t sample{0}, delta{};
int8_t re{0}, im{0};
size_t progress_interval_samples = 0, progress_samples = 0;
bool configured{false};
uint32_t bytes_read{0};
void samplerate_config(const SamplerateConfigMessage& message);
void audio_config(const AudioTXConfigMessage& message);
void replay_config(const ReplayConfigMessage& message);
TXProgressMessage txprogress_message{};
RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest};
};
#endif

View File

@@ -27,313 +27,264 @@
#include "event_m4.hpp"
void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
// FM demodulation
if (!configured) return;
/*const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto channel = decim_1.execute(decim_0_out, dst_buffer);
// FM demodulation
feed_channel_stats(channel);
auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/
/*const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto channel = decim_1.execute(decim_0_out, dst_buffer);
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
feed_channel_stats(decim_0_out);
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
feed_channel_stats(channel);
/*std::fill(spectrum.begin(), spectrum.end(), 0);
for(size_t i=0; i<spectrum.size(); i++) {
spectrum[i] += buffer.p[i];
}
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
feed_channel_stats(buffer_c16);
auto audio_oversampled = demod.execute(buffer_c16, work_audio_buffer);*/
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/
/*const int32_t sample_int = audio_oversampled.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;*/
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
rb_head++;
rb_head=(rb_head)%RB_SIZE;
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
feed_channel_stats(decim_0_out);
rb_buf[rb_head] = current_sample;
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
skipSamples = skipSamples - 1;
/*std::fill(spectrum.begin(), spectrum.end(), 0);
for(size_t i=0; i<spectrum.size(); i++) {
spectrum[i] += buffer.p[i];
}
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
feed_channel_stats(buffer_c16);
auto audio_oversampled = demod.execute(buffer_c16, work_audio_buffer);*/
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
/*const int32_t sample_int = audio_oversampled.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;*/
if (skipSamples<1)
{
int32_t current_sample = audio_oversampled.p[c]; // if I directly use this, some results can pass crc but not correct.
rb_head++;
rb_head = (rb_head) % RB_SIZE;
int32_t threshold_tmp=0;
for (int c=0;c<8;c++)
{
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
}
g_threshold = (int32_t)threshold_tmp/8;
rb_buf[rb_head] = current_sample;
int transitions=0;
if (rb_buf[(rb_head+9)%RB_SIZE] > g_threshold)
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE])
transitions = transitions + 1;
}
}
else
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE])
transitions = transitions + 1;
}
}
bool packet_detected=false;
//if ( transitions==4 && abs(g_threshold)<15500)
if ( transitions==4)
{
skipSamples = skipSamples - 1;
uint8_t packet_data[500];
int packet_length;
uint32_t packet_crc;
//uint32_t calced_crc; // NOTE: restore when CRC is passing
uint64_t packet_addr_l;
//uint32_t result; // NOTE: restore when CRC is passing
uint8_t crc[3];
uint8_t packet_header_arr[2];
if (skipSamples < 1) {
int32_t threshold_tmp = 0;
for (int c = 0; c < 8; c++) {
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head + c) % RB_SIZE];
}
g_threshold = (int32_t)threshold_tmp / 8;
packet_addr_l=0;
for (int i=0;i<4;i++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
packet_addr_l|=((uint64_t)byte_temp)<<(8*i);
}
int transitions = 0;
if (rb_buf[(rb_head + 9) % RB_SIZE] > g_threshold) {
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + c) % RB_SIZE] > rb_buf[(rb_head + c + 1) % RB_SIZE])
transitions = transitions + 1;
}
} else {
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + c) % RB_SIZE] < rb_buf[(rb_head + c + 1) % RB_SIZE])
transitions = transitions + 1;
}
}
channel_number = 38;
bool packet_detected = false;
// if ( transitions==4 && abs(g_threshold)<15500)
if (transitions == 4) {
uint8_t packet_data[500];
int packet_length;
uint32_t packet_crc;
// uint32_t calced_crc; // NOTE: restore when CRC is passing
uint64_t packet_addr_l;
// uint32_t result; // NOTE: restore when CRC is passing
uint8_t crc[3];
uint8_t packet_header_arr[2];
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
packet_addr_l = 0;
for (int i = 0; i < 4; i++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + (i + 1) * 8 + c) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
uint8_t byte_temp = (uint8_t)(((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
packet_addr_l |= ((uint64_t)byte_temp) << (8 * i);
}
packet_header_arr[t] = byte;
}
channel_number = 38;
for (int t = 0; t < 2; t++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + 5 * 8 + t * 8 + c) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
uint8_t lfsr_1 = byte_temp2 | 2;
int header_length = 2;
int header_counter = 0;
while(header_length--)
{
for(uint8_t i = 0x80; i; i >>= 1)
{
if(lfsr_1 & 0x80)
{
lfsr_1 ^= 0x11;
(packet_header_arr[header_counter]) ^= i;
}
lfsr_1 <<= 1;
}
header_counter = header_counter + 1;
}
packet_header_arr[t] = byte;
}
uint8_t byte_temp2 = (uint8_t)(((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
uint8_t lfsr_1 = byte_temp2 | 2;
int header_length = 2;
int header_counter = 0;
while (header_length--) {
for (uint8_t i = 0x80; i; i >>= 1) {
if (lfsr_1 & 0x80) {
lfsr_1 ^= 0x11;
(packet_header_arr[header_counter]) ^= i;
}
lfsr_1 <<= 1;
}
header_counter = header_counter + 1;
}
if (packet_addr_l==0x8E89BED6)
{
if (packet_addr_l == 0x8E89BED6) {
uint8_t byte_temp3 = (uint8_t)(((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
packet_length = byte_temp3 & 0x3F;
uint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
packet_length=byte_temp3&0x3F;
}
else
{
packet_length=0;
}
} else {
packet_length = 0;
}
for (int t=0;t<packet_length+2+3;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
for (int t = 0; t < packet_length + 2 + 3; t++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + 5 * 8 + t * 8 + c) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
packet_data[t] = byte;
}
packet_data[t] = byte;
}
uint8_t byte_temp4 = (uint8_t)(((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
uint8_t lfsr_2 = byte_temp4 | 2;
int pdu_crc_length = packet_length + 2 + 3;
int pdu_crc_counter = 0;
while (pdu_crc_length--) {
for (uint8_t i = 0x80; i; i >>= 1) {
if (lfsr_2 & 0x80) {
lfsr_2 ^= 0x11;
(packet_data[pdu_crc_counter]) ^= i;
}
lfsr_2 <<= 1;
}
pdu_crc_counter = pdu_crc_counter + 1;
}
uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
uint8_t lfsr_2 = byte_temp4 | 2;
int pdu_crc_length = packet_length+2+3;
int pdu_crc_counter = 0;
while(pdu_crc_length--)
{
for(uint8_t i = 0x80; i; i >>= 1)
{
if(lfsr_2 & 0x80)
{
lfsr_2 ^= 0x11;
(packet_data[pdu_crc_counter]) ^= i;
}
lfsr_2 <<= 1;
}
pdu_crc_counter = pdu_crc_counter + 1;
}
if (packet_addr_l == 0x8E89BED6) {
crc[0] = crc[1] = crc[2] = 0x55;
} else {
crc[0] = crc[1] = crc[2] = 0;
}
uint8_t v, t, d, crc_length;
uint32_t crc_result = 0;
crc_length = packet_length + 2;
int counter = 0;
while (crc_length--) {
uint8_t byte_temp5 = (uint8_t)(((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
d = byte_temp5;
for (v = 0; v < 8; v++, d >>= 1) {
t = crc[0] >> 7;
crc[0] <<= 1;
if (crc[1] & 0x80) crc[0] |= 1;
crc[1] <<= 1;
if (crc[2] & 0x80) crc[1] |= 1;
crc[2] <<= 1;
if (t != (d & 1)) {
crc[2] ^= 0x5B;
crc[1] ^= 0x06;
}
}
counter = counter + 1;
}
for (v = 0; v < 3; v++) crc_result = (crc_result << 8) | crc[v];
// calced_crc = crc_result; // NOTE: restore when CRC is passing
if (packet_addr_l==0x8E89BED6)
{
crc[0]=crc[1]=crc[2]=0x55;
}
else
{
crc[0]=crc[1]=crc[2]=0;
}
packet_crc = 0;
for (int c = 0; c < 3; c++) packet_crc = (packet_crc << 8) | packet_data[packet_length + 2 + c];
uint8_t v, t, d, crc_length;
uint32_t crc_result=0;
crc_length = packet_length + 2;
int counter = 0;
while(crc_length--)
{
uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
d = byte_temp5;
for(v = 0; v < 8; v++, d >>= 1)
{
t = crc[0] >> 7;
crc[0] <<= 1;
if(crc[1] & 0x80) crc[0] |= 1;
crc[1] <<= 1;
if(crc[2] & 0x80) crc[1] |= 1;
crc[2] <<= 1;
if(t != (d & 1))
{
crc[2] ^= 0x5B;
crc[1] ^= 0x06;
}
}
counter = counter + 1;
}
for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v];
//calced_crc = crc_result; // NOTE: restore when CRC is passing
if (packet_addr_l == 0x8E89BED6)
// if (packet_crc==calced_crc) // NOTE: restore when CRC is passing
{
uint8_t mac_data[6];
int counter = 0;
for (int i = 7; i >= 2; i--) {
uint8_t byte_temp6 = (uint8_t)(((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
// result = byte_temp6; // NOTE: restore when CRC is passing
mac_data[counter] = byte_temp6;
counter = counter + 1;
}
packet_crc=0;
for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c];
data_message.is_data = false;
data_message.value = 'A';
shared_memory.application_queue.push(data_message);
if (packet_addr_l==0x8E89BED6)
//if (packet_crc==calced_crc) // NOTE: restore when CRC is passing
{
uint8_t mac_data[6];
int counter = 0;
for (int i = 7; i >= 2; i--)
{
uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
//result = byte_temp6; // NOTE: restore when CRC is passing
mac_data[counter] = byte_temp6;
counter = counter + 1;
}
data_message.is_data = true;
data_message.value = mac_data[0];
shared_memory.application_queue.push(data_message);
data_message.is_data = false;
data_message.value = 'A';
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[1];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[0];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[2];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[1];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[3];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[2];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[4];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[3];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[5];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[4];
shared_memory.application_queue.push(data_message);
data_message.is_data = false;
data_message.value = 'B';
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[5];
shared_memory.application_queue.push(data_message);
packet_detected = true;
} else
packet_detected = false;
}
data_message.is_data = false;
data_message.value = 'B';
shared_memory.application_queue.push(data_message);
packet_detected = true;
}
else
packet_detected = false;
}
if (packet_detected)
{
skipSamples=20;
}
}
}
if (packet_detected) {
skipSamples = 20;
}
}
}
}
void BTLERxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::BTLERxConfigure)
configure(*reinterpret_cast<const BTLERxConfigureMessage*>(message));
if (message->id == Message::ID::BTLERxConfigure)
configure(*reinterpret_cast<const BTLERxConfigureMessage*>(message));
}
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
(void)message; //avoid warning
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
(void)message; // avoid warning
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
configured = true;
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<BTLERxProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<BTLERxProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -37,59 +37,54 @@
#include "message.hpp"
class BTLERxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 4000000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
void on_message(const Message* const message) override;
std::array<complex16_t, 512> spectrum { };
const buffer_c16_t spectrum_buffer {
spectrum.data(),
spectrum.size()
};
private:
static constexpr size_t baseband_fs = 4000000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
const buffer_s16_t work_audio_buffer {
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)
};
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
std::array<int16_t, 1000> rb_buf { 0 };
std::array<complex16_t, 512> spectrum{};
const buffer_c16_t spectrum_buffer{
spectrum.data(),
spectrum.size()};
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
dsp::demodulate::FM demod { };
int rb_head {-1};
int32_t g_threshold {0};
uint8_t channel_number {38};
int skipSamples {1000};
int RB_SIZE {1000};
const buffer_s16_t work_audio_buffer{
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)};
bool configured { false };
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line{0};
std::array<int16_t, 1000> rb_buf{0};
void configure(const BTLERxConfigureMessage& message);
AFSKDataMessage data_message { false, 0 };
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
dsp::decimate::FIRC16xR16x16Decim2 decim_1{};
dsp::demodulate::FM demod{};
int rb_head{-1};
int32_t g_threshold{0};
uint8_t channel_number{38};
int skipSamples{1000};
int RB_SIZE{1000};
bool configured{false};
void configure(const BTLERxConfigureMessage& message);
AFSKDataMessage data_message{false, 0};
};
#endif/*__PROC_BTLERX_H__*/
#endif /*__PROC_BTLERX_H__*/

View File

@@ -29,84 +29,83 @@
#include "utility.hpp"
CaptureProcessor::CaptureProcessor() {
decim_0.configure(taps_200k_decim_0.taps, 33554432);
decim_1.configure(taps_200k_decim_1.taps, 131072);
channel_spectrum.set_decimation_factor(1);
decim_0.configure(taps_200k_decim_0.taps, 33554432);
decim_1.configure(taps_200k_decim_1.taps, 131072);
channel_spectrum.set_decimation_factor(1);
}
void CaptureProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto& decimator_out = decim_1_out;
const auto& channel = decimator_out;
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto& decimator_out = decim_1_out;
const auto& channel = decimator_out;
if( stream ) {
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
const size_t written = stream->write(decimator_out.p, bytes_to_write);
if( written != bytes_to_write )
{
//TODO eventually report error somewhere
}
}
if (stream) {
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
const size_t written = stream->write(decimator_out.p, bytes_to_write);
if (written != bytes_to_write) {
// TODO eventually report error somewhere
}
}
feed_channel_stats(channel);
feed_channel_stats(channel);
spectrum_samples += channel.count;
if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
}
spectrum_samples += channel.count;
if (spectrum_samples >= spectrum_interval_samples) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
}
}
void CaptureProcessor::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
switch (message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
case Message::ID::SamplerateConfig:
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
case Message::ID::SamplerateConfig:
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
break;
default:
break;
}
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
}
void CaptureProcessor::samplerate_config(const SamplerateConfigMessage& message) {
baseband_fs = message.sample_rate;
baseband_thread.set_sampling_rate(baseband_fs);
size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor;
baseband_fs = message.sample_rate;
baseband_thread.set_sampling_rate(baseband_fs);
size_t decim_1_input_fs = decim_0_output_fs;
size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor;
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs;
channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs;
size_t decim_1_input_fs = decim_0_output_fs;
size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
spectrum_samples = 0;
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs;
channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs;
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
spectrum_samples = 0;
}
void CaptureProcessor::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
stream = std::make_unique<StreamInput>(message.config);
} else {
stream.reset();
}
if (message.config) {
stream = std::make_unique<StreamInput>(message.config);
} else {
stream.reset();
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<CaptureProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<CaptureProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -37,41 +37,40 @@
#include <memory>
class CaptureProcessor : public BasebandProcessor {
public:
CaptureProcessor();
public:
CaptureProcessor();
void execute(const buffer_c8_t& buffer) override;
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
void on_message(const Message* const message) override;
private:
// TODO: Repeated value needs to be transmitted from application side.
size_t baseband_fs = 0;
static constexpr auto spectrum_rate_hz = 50.0f;
private:
// TODO: Repeated value needs to be transmitted from application side.
size_t baseband_fs = 0;
static constexpr auto spectrum_rate_hz = 50.0f;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
dsp::decimate::FIRC16xR16x16Decim2 decim_1{};
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
std::unique_ptr<StreamInput> stream { };
std::unique_ptr<StreamInput> stream{};
SpectrumCollector channel_spectrum { };
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
SpectrumCollector channel_spectrum{};
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
void samplerate_config(const SamplerateConfigMessage& message);
void capture_config(const CaptureConfigMessage& message);
void samplerate_config(const SamplerateConfigMessage& message);
void capture_config(const CaptureConfigMessage& message);
};
#endif/*__PROC_CAPTURE_HPP__*/
#endif /*__PROC_CAPTURE_HPP__*/

View File

@@ -26,94 +26,90 @@
#include "event_m4.hpp"
float ERTProcessor::abs(const complex8_t& v) {
// const int16_t r = v.real() - offset_i;
// const int16_t i = v.imag() - offset_q;
// const uint32_t r2 = r * r;
// const uint32_t i2 = i * i;
// const uint32_t r2_i2 = r2 + i2;
// return std::sqrt(static_cast<float>(r2_i2));
const float r = static_cast<float>(v.real()) - offset_i;
const float i = static_cast<float>(v.imag()) - offset_q;
const float r2 = r * r;
const float i2 = i * i;
const float r2_i2 = r2 + i2;
return std::sqrt(r2_i2);
// const int16_t r = v.real() - offset_i;
// const int16_t i = v.imag() - offset_q;
// const uint32_t r2 = r * r;
// const uint32_t i2 = i * i;
// const uint32_t r2_i2 = r2 + i2;
// return std::sqrt(static_cast<float>(r2_i2));
const float r = static_cast<float>(v.real()) - offset_i;
const float i = static_cast<float>(v.imag()) - offset_q;
const float r2 = r * r;
const float i2 = i * i;
const float r2_i2 = r2 + i2;
return std::sqrt(r2_i2);
}
void ERTProcessor::execute(const buffer_c8_t& buffer) {
/* 4.194304MHz, 2048 samples */
/* 4.194304MHz, 2048 samples */
const complex8_t* src = &buffer.p[0];
const complex8_t* const src_end = &buffer.p[buffer.count];
const complex8_t* src = &buffer.p[0];
const complex8_t* const src_end = &buffer.p[buffer.count];
average_i += src->real();
average_q += src->imag();
average_count++;
if( average_count == average_window ) {
offset_i = static_cast<float>(average_i) / average_window;
offset_q = static_cast<float>(average_q) / average_window;
average_i = 0;
average_q = 0;
average_count = 0;
}
average_i += src->real();
average_q += src->imag();
average_count++;
if (average_count == average_window) {
offset_i = static_cast<float>(average_i) / average_window;
offset_q = static_cast<float>(average_q) / average_window;
average_i = 0;
average_q = 0;
average_count = 0;
}
const float gain = 128 * samples_per_symbol;
const float k = 1.0f / gain;
const float gain = 128 * samples_per_symbol;
const float k = 1.0f / gain;
while(src < src_end) {
float sum = 0.0f;
for(size_t i=0; i<(samples_per_symbol / 2); i++) {
sum += abs(*(src++));
}
sum_half_period[1] = sum_half_period[0];
sum_half_period[0] = sum;
while (src < src_end) {
float sum = 0.0f;
for (size_t i = 0; i < (samples_per_symbol / 2); i++) {
sum += abs(*(src++));
}
sum_half_period[1] = sum_half_period[0];
sum_half_period[0] = sum;
sum_period[2] = sum_period[1];
sum_period[1] = sum_period[0];
sum_period[0] = (sum_half_period[0] + sum_half_period[1]) * k;
sum_period[2] = sum_period[1];
sum_period[1] = sum_period[0];
sum_period[0] = (sum_half_period[0] + sum_half_period[1]) * k;
manchester[2] = manchester[1];
manchester[1] = manchester[0];
manchester[0] = sum_period[2] - sum_period[0];
manchester[2] = manchester[1];
manchester[1] = manchester[0];
manchester[0] = sum_period[2] - sum_period[0];
const auto data = manchester[0] - manchester[2];
const auto data = manchester[0] - manchester[2];
clock_recovery(data);
}
clock_recovery(data);
}
}
void ERTProcessor::consume_symbol(
const float raw_symbol
) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
scm_builder.execute(sliced_symbol);
scmplus_builder.execute(sliced_symbol);
idm_builder.execute(sliced_symbol);
const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
scm_builder.execute(sliced_symbol);
scmplus_builder.execute(sliced_symbol);
idm_builder.execute(sliced_symbol);
}
void ERTProcessor::scm_handler(
const baseband::Packet& packet
) {
const ERTPacketMessage message { ert::Packet::Type::SCM, packet };
shared_memory.application_queue.push(message);
const baseband::Packet& packet) {
const ERTPacketMessage message{ert::Packet::Type::SCM, packet};
shared_memory.application_queue.push(message);
}
void ERTProcessor::scmplus_handler(
const baseband::Packet& packet
) {
const ERTPacketMessage message { ert::Packet::Type::SCMPLUS, packet };
shared_memory.application_queue.push(message);
const baseband::Packet& packet) {
const ERTPacketMessage message{ert::Packet::Type::SCMPLUS, packet};
shared_memory.application_queue.push(message);
}
void ERTProcessor::idm_handler(
const baseband::Packet& packet
) {
const ERTPacketMessage message { ert::Packet::Type::IDM, packet };
shared_memory.application_queue.push(message);
const baseband::Packet& packet) {
const ERTPacketMessage message{ert::Packet::Type::IDM, packet};
shared_memory.application_queue.push(message);
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<ERTProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<ERTProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -40,85 +40,83 @@
#include <bitset>
// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x1f2a60)[2:].zfill(21))])
constexpr uint64_t scm_preamble_and_sync_manchester { 0b101010101001011001100110010110100101010101 };
constexpr size_t scm_preamble_and_sync_length { 42 - 10 };
constexpr size_t scm_payload_length_max { 150 };
constexpr uint64_t scm_preamble_and_sync_manchester{0b101010101001011001100110010110100101010101};
constexpr size_t scm_preamble_and_sync_length{42 - 10};
constexpr size_t scm_payload_length_max{150};
// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x16a3)[2:].zfill(16))])
constexpr uint64_t scmplus_preamble_and_sync_manchester { 0b01010110011010011001100101011010 };
constexpr size_t scmplus_preamble_and_sync_length { 32 - 0 };
constexpr size_t scmplus_payload_length_max { 224 };
constexpr uint64_t scmplus_preamble_and_sync_manchester{0b01010110011010011001100101011010};
constexpr size_t scmplus_preamble_and_sync_length{32 - 0};
constexpr size_t scmplus_payload_length_max{224};
// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x555516a3)[2:].zfill(32))])
constexpr uint64_t idm_preamble_and_sync_manchester { 0b0110011001100110011001100110011001010110011010011001100101011010 };
constexpr size_t idm_preamble_and_sync_length { 64 - 16 };
constexpr size_t idm_payload_length_max { 1408 };
constexpr uint64_t idm_preamble_and_sync_manchester{0b0110011001100110011001100110011001010110011010011001100101011010};
constexpr size_t idm_preamble_and_sync_length{64 - 16};
constexpr size_t idm_payload_length_max{1408};
class ERTProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
const uint32_t baseband_sampling_rate = 4194304;
const size_t decimation = 1;
const float symbol_rate = 32768;
private:
const uint32_t baseband_sampling_rate = 4194304;
const size_t decimation = 1;
const float symbol_rate = 32768;
const uint32_t channel_sampling_rate = baseband_sampling_rate / decimation;
const size_t samples_per_symbol = channel_sampling_rate / symbol_rate;
const float clock_recovery_rate = symbol_rate * 2;
const uint32_t channel_sampling_rate = baseband_sampling_rate / decimation;
const size_t samples_per_symbol = channel_sampling_rate / symbol_rate;
const float clock_recovery_rate = symbol_rate * 2;
BasebandThread baseband_thread { baseband_sampling_rate, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
BasebandThread baseband_thread{baseband_sampling_rate, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
clock_recovery_rate, symbol_rate, { 1.0f / 18.0f },
[this](const float symbol) { this->consume_symbol(symbol); }
};
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery{
clock_recovery_rate,
symbol_rate,
{1.0f / 18.0f},
[this](const float symbol) { this->consume_symbol(symbol); }};
PacketBuilder<BitPattern, NeverMatch, FixedLength> scm_builder {
{ scm_preamble_and_sync_manchester, scm_preamble_and_sync_length, 1 },
{ },
{ scm_payload_length_max },
[this](const baseband::Packet& packet) {
this->scm_handler(packet);
}
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> scm_builder{
{scm_preamble_and_sync_manchester, scm_preamble_and_sync_length, 1},
{},
{scm_payload_length_max},
[this](const baseband::Packet& packet) {
this->scm_handler(packet);
}};
PacketBuilder<BitPattern, NeverMatch, FixedLength> scmplus_builder {
{ scmplus_preamble_and_sync_manchester, scmplus_preamble_and_sync_length, 1 },
{ },
{ scmplus_payload_length_max },
[this](const baseband::Packet& packet) {
this->scmplus_handler(packet);
}
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> idm_builder {
{ idm_preamble_and_sync_manchester, idm_preamble_and_sync_length, 1 },
{ },
{ idm_payload_length_max },
[this](const baseband::Packet& packet) {
this->idm_handler(packet);
}
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> scmplus_builder{
{scmplus_preamble_and_sync_manchester, scmplus_preamble_and_sync_length, 1},
{},
{scmplus_payload_length_max},
[this](const baseband::Packet& packet) {
this->scmplus_handler(packet);
}};
void consume_symbol(const float symbol);
void scm_handler(const baseband::Packet& packet);
void scmplus_handler(const baseband::Packet& packet);
void idm_handler(const baseband::Packet& packet);
PacketBuilder<BitPattern, NeverMatch, FixedLength> idm_builder{
{idm_preamble_and_sync_manchester, idm_preamble_and_sync_length, 1},
{},
{idm_payload_length_max},
[this](const baseband::Packet& packet) {
this->idm_handler(packet);
}};
float sum_half_period[2];
float sum_period[3];
float manchester[3];
void consume_symbol(const float symbol);
void scm_handler(const baseband::Packet& packet);
void scmplus_handler(const baseband::Packet& packet);
void idm_handler(const baseband::Packet& packet);
const size_t average_window { 2048 };
int32_t average_i { 0 };
int32_t average_q { 0 };
size_t average_count { 0 };
float offset_i { 0.0f };
float offset_q { 0.0f };
float sum_half_period[2];
float sum_period[3];
float manchester[3];
float abs(const complex8_t& v);
const size_t average_window{2048};
int32_t average_i{0};
int32_t average_q{0};
size_t average_count{0};
float offset_i{0.0f};
float offset_q{0.0f};
float abs(const complex8_t& v);
};
#endif/*__PROC_ERT_H__*/
#endif /*__PROC_ERT_H__*/

View File

@@ -34,83 +34,83 @@
void initialize_flash();
void erase_flash();
void initialize_sdcard();
void write_firmware(FIL *);
void write_page(size_t, uint8_t *, size_t);
void write_firmware(FIL*);
void write_page(size_t, uint8_t*, size_t);
int main() {
const TCHAR *filename = reinterpret_cast<const TCHAR *>(&shared_memory.bb_data.data[0]);
const TCHAR* filename = reinterpret_cast<const TCHAR*>(&shared_memory.bb_data.data[0]);
initialize_flash();
palSetPad(LED_PORT, LEDRX_PAD);
erase_flash();
initialize_flash();
palSetPad(LED_PORT, LEDRX_PAD);
erase_flash();
initialize_sdcard();
initialize_sdcard();
FIL firmware_file;
if (f_open(&firmware_file, filename, FA_READ) != FR_OK) chDbgPanic("no file");
FIL firmware_file;
if (f_open(&firmware_file, filename, FA_READ) != FR_OK) chDbgPanic("no file");
palSetPad(LED_PORT, LEDTX_PAD);
palSetPad(LED_PORT, LEDTX_PAD);
write_firmware(&firmware_file);
write_firmware(&firmware_file);
palClearPad(LED_PORT, LEDTX_PAD);
palClearPad(LED_PORT, LEDRX_PAD);
palClearPad(LED_PORT, LEDTX_PAD);
palClearPad(LED_PORT, LEDRX_PAD);
f_close(&firmware_file);
f_close(&firmware_file);
while(1)
__WFE();
while (1)
__WFE();
return 0;
return 0;
}
void initialize_flash() {
w25q80bv::disable_spifi();
w25q80bv::initialite_spi();
w25q80bv::setup();
w25q80bv::disable_spifi();
w25q80bv::initialite_spi();
w25q80bv::setup();
w25q80bv::wait_for_device();
w25q80bv::wait_not_busy();
w25q80bv::wait_for_device();
w25q80bv::wait_not_busy();
}
void erase_flash() {
w25q80bv::remove_write_protection();
w25q80bv::wait_not_busy();
w25q80bv::remove_write_protection();
w25q80bv::wait_not_busy();
w25q80bv::erase_chip();
w25q80bv::wait_not_busy();
w25q80bv::erase_chip();
w25q80bv::wait_not_busy();
}
void initialize_sdcard() {
static FATFS fs;
static FATFS fs;
sdcStart(&SDCD1, nullptr);
if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1");
if (f_mount(&fs, reinterpret_cast<const TCHAR*>(_T("")), 1) != FR_OK) chDbgPanic("no sd card #2");
sdcStart(&SDCD1, nullptr);
if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1");
if (f_mount(&fs, reinterpret_cast<const TCHAR*>(_T("")), 1) != FR_OK) chDbgPanic("no sd card #2");
}
void write_firmware(FIL *firmware_file) {
uint8_t *data_buffer = &shared_memory.bb_data.data[0];
void write_firmware(FIL* firmware_file) {
uint8_t* data_buffer = &shared_memory.bb_data.data[0];
for (size_t page_index = 0; page_index < NUM_PAGES; page_index++) {
if (page_index % 32 == 0)
palTogglePad(LED_PORT, LEDTX_PAD);
for (size_t page_index = 0; page_index < NUM_PAGES; page_index++) {
if (page_index % 32 == 0)
palTogglePad(LED_PORT, LEDTX_PAD);
size_t bytes_read;
if (f_read(firmware_file, data_buffer, PAGE_LEN, &bytes_read) != FR_OK) chDbgPanic("no data");
size_t bytes_read;
if (f_read(firmware_file, data_buffer, PAGE_LEN, &bytes_read) != FR_OK) chDbgPanic("no data");
if (bytes_read > 0)
write_page(page_index, data_buffer, bytes_read);
if (bytes_read > 0)
write_page(page_index, data_buffer, bytes_read);
if (bytes_read < PAGE_LEN)
return;
}
if (bytes_read < PAGE_LEN)
return;
}
}
void write_page(size_t page_index, uint8_t *data_buffer, size_t data_length) {
w25q80bv::wait_not_busy();
w25q80bv::remove_write_protection();
w25q80bv::wait_not_busy();
w25q80bv::write(page_index, data_buffer, data_length);
w25q80bv::wait_not_busy();
void write_page(size_t page_index, uint8_t* data_buffer, size_t data_length) {
w25q80bv::wait_not_busy();
w25q80bv::remove_write_protection();
w25q80bv::wait_not_busy();
w25q80bv::write(page_index, data_buffer, data_length);
w25q80bv::wait_not_busy();
}

View File

@@ -28,80 +28,79 @@
#include <cstdint>
void FSKProcessor::execute(const buffer_c8_t& buffer) {
int8_t re, im;
// This is called at 2.28M/2048 = 1113Hz
for (size_t i = 0; i < buffer.count; i++) {
int8_t re, im;
if (configured) {
if (sample_count >= samples_per_bit) {
if (bit_pos > length) {
// End of data
cur_bit = 0;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
configured = false;
} else {
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
bit_pos++;
if (progress_count >= progress_notice) {
progress_count = 0;
txprogress_message.progress++;
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
} else {
progress_count++;
}
}
sample_count = 0;
} else {
sample_count++;
}
if (cur_bit)
phase += shift_one;
else
phase += shift_zero;
sphase = phase + (64 << 24);
// This is called at 2.28M/2048 = 1113Hz
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
} else {
re = 0;
im = 0;
}
buffer.p[i] = {re, im};
}
for (size_t i = 0; i < buffer.count; i++) {
if (configured) {
if (sample_count >= samples_per_bit) {
if (bit_pos > length) {
// End of data
cur_bit = 0;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
configured = false;
} else {
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
bit_pos++;
if (progress_count >= progress_notice) {
progress_count = 0;
txprogress_message.progress++;
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
} else {
progress_count++;
}
}
sample_count = 0;
} else {
sample_count++;
}
if (cur_bit)
phase += shift_one;
else
phase += shift_zero;
sphase = phase + (64 << 24);
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
} else {
re = 0;
im = 0;
}
buffer.p[i] = {re, im};
}
}
void FSKProcessor::on_message(const Message* const p) {
const auto message = *reinterpret_cast<const FSKConfigureMessage*>(p);
if (message.id == Message::ID::FSKConfigure) {
samples_per_bit = message.samples_per_bit;
length = message.stream_length + 32; // Why ?!
shift_one = message.shift * (0xFFFFFFFFULL / 2280000);
shift_zero = -shift_one;
progress_notice = message.progress_notice;
sample_count = samples_per_bit;
progress_count = 0;
bit_pos = 0;
cur_bit = 0;
txprogress_message.progress = 0;
txprogress_message.done = false;
configured = true;
}
const auto message = *reinterpret_cast<const FSKConfigureMessage*>(p);
if (message.id == Message::ID::FSKConfigure) {
samples_per_bit = message.samples_per_bit;
length = message.stream_length + 32; // Why ?!
shift_one = message.shift * (0xFFFFFFFFULL / 2280000);
shift_zero = -shift_one;
progress_notice = message.progress_notice;
sample_count = samples_per_bit;
progress_count = 0;
bit_pos = 0;
cur_bit = 0;
txprogress_message.progress = 0;
txprogress_message.done = false;
configured = true;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<FSKProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<FSKProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -27,27 +27,27 @@
#include "baseband_thread.hpp"
class FSKProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const p) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
bool configured = false;
BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
uint32_t samples_per_bit { 0 };
uint32_t length { 0 };
uint32_t shift_zero { }, shift_one { };
uint32_t bit_pos { 0 };
uint32_t progress_notice { }, progress_count { 0 };
uint8_t cur_bit { 0 };
uint32_t sample_count { 0 };
uint32_t phase { 0 }, sphase { 0 };
TXProgressMessage txprogress_message { };
void on_message(const Message* const p) override;
private:
bool configured = false;
BasebandThread baseband_thread{2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit};
uint32_t samples_per_bit{0};
uint32_t length{0};
uint32_t shift_zero{}, shift_one{};
uint32_t bit_pos{0};
uint32_t progress_notice{}, progress_count{0};
uint8_t cur_bit{0};
uint32_t sample_count{0};
uint32_t phase{0}, sphase{0};
TXProgressMessage txprogress_message{};
};
#endif

View File

@@ -30,99 +30,98 @@
#include "utility.hpp"
ReplayProcessor::ReplayProcessor() {
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
spectrum_samples = 0;
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
channel_spectrum.set_decimation_factor(1);
configured = false;
spectrum_samples = 0;
channel_spectrum.set_decimation_factor(1);
configured = false;
}
void ReplayProcessor::execute(const buffer_c8_t& buffer) {
/* 4MHz, 2048 samples */
if (!configured) return;
// File data is in C16 format, we need C8
// File samplerate is 500kHz, we're at 4MHz
// iq_buffer can only be 512 C16 samples (RAM limitation)
// To fill up the 2048-sample C8 buffer, we need:
// 2048 samples * 2 bytes per sample = 4096 bytes
// Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each
// So 256 * 4 bytes per sample (C16) = 1024 bytes from the file
if( stream ) { //sizeof(*buffer.p) = sizeof(C8) = 2*int8 = 2 bytes //buffer.count = 2048
const size_t bytes_to_read = sizeof(*buffer.p) * 1 * (buffer.count ); // *2 (C16), /8 (oversampling) should be == 1024
bytes_read += stream->read(iq_buffer.p, bytes_to_read);
}
// Fill and "stretch"
for (size_t i = 0; i < buffer.count; i++) {
auto re_out = iq_buffer.p[i].real() ;
auto im_out = iq_buffer.p[i].imag() ;
buffer.p[i] = { (int8_t)re_out, (int8_t)im_out };
}
spectrum_samples += buffer.count;
if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples;
/* 4MHz, 2048 samples */
txprogress_message.progress = bytes_read / 1024; // Inform UI about progress
if (!configured) return;
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
}
// File data is in C16 format, we need C8
// File samplerate is 500kHz, we're at 4MHz
// iq_buffer can only be 512 C16 samples (RAM limitation)
// To fill up the 2048-sample C8 buffer, we need:
// 2048 samples * 2 bytes per sample = 4096 bytes
// Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each
// So 256 * 4 bytes per sample (C16) = 1024 bytes from the file
if (stream) { // sizeof(*buffer.p) = sizeof(C8) = 2*int8 = 2 bytes //buffer.count = 2048
const size_t bytes_to_read = sizeof(*buffer.p) * 1 * (buffer.count); // *2 (C16), /8 (oversampling) should be == 1024
bytes_read += stream->read(iq_buffer.p, bytes_to_read);
}
// Fill and "stretch"
for (size_t i = 0; i < buffer.count; i++) {
auto re_out = iq_buffer.p[i].real();
auto im_out = iq_buffer.p[i].imag();
buffer.p[i] = {(int8_t)re_out, (int8_t)im_out};
}
spectrum_samples += buffer.count;
if (spectrum_samples >= spectrum_interval_samples) {
spectrum_samples -= spectrum_interval_samples;
txprogress_message.progress = bytes_read / 1024; // Inform UI about progress
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
}
}
void ReplayProcessor::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
switch (message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
case Message::ID::SamplerateConfig:
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
break;
case Message::ID::ReplayConfig:
configured = false;
bytes_read = 0;
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
break;
// App has prefilled the buffers, we're ready to go now
case Message::ID::FIFOData:
configured = true;
break;
case Message::ID::SamplerateConfig:
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
break;
default:
break;
}
case Message::ID::ReplayConfig:
configured = false;
bytes_read = 0;
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
break;
// App has prefilled the buffers, we're ready to go now
case Message::ID::FIFOData:
configured = true;
break;
default:
break;
}
}
void ReplayProcessor::samplerate_config(const SamplerateConfigMessage& message) {
baseband_fs = message.sample_rate;
baseband_thread.set_sampling_rate(baseband_fs);
spectrum_interval_samples = baseband_fs / spectrum_rate_hz;
baseband_fs = message.sample_rate;
baseband_thread.set_sampling_rate(baseband_fs);
spectrum_interval_samples = baseband_fs / spectrum_rate_hz;
}
void ReplayProcessor::replay_config(const ReplayConfigMessage& message) {
if( message.config ) {
stream = std::make_unique<StreamOutput>(message.config);
// Tell application that the buffers and FIFO pointers are ready, prefill
shared_memory.application_queue.push(sig_message);
} else {
stream.reset();
}
if (message.config) {
stream = std::make_unique<StreamOutput>(message.config);
// Tell application that the buffers and FIFO pointers are ready, prefill
shared_memory.application_queue.push(sig_message);
} else {
stream.reset();
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<ReplayProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<ReplayProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -35,44 +35,43 @@
#include <memory>
class ReplayProcessor : public BasebandProcessor {
public:
ReplayProcessor();
public:
ReplayProcessor();
void execute(const buffer_c8_t& buffer) override;
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
void on_message(const Message* const message) override;
private:
size_t baseband_fs = 0;
static constexpr auto spectrum_rate_hz = 50.0f;
private:
size_t baseband_fs = 0;
static constexpr auto spectrum_rate_hz = 50.0f;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit};
std::array<complex8_t, 2048> iq { };
const buffer_c8_t iq_buffer {
iq.data(),
iq.size(),
baseband_fs
};
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
std::array<complex8_t, 2048> iq{};
const buffer_c8_t iq_buffer{
iq.data(),
iq.size(),
baseband_fs};
std::unique_ptr<StreamOutput> stream { };
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
SpectrumCollector channel_spectrum { };
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
bool configured { false };
uint32_t bytes_read { 0 };
std::unique_ptr<StreamOutput> stream{};
void samplerate_config(const SamplerateConfigMessage& message);
void replay_config(const ReplayConfigMessage& message);
TXProgressMessage txprogress_message { };
RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest };
SpectrumCollector channel_spectrum{};
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
bool configured{false};
uint32_t bytes_read{0};
void samplerate_config(const SamplerateConfigMessage& message);
void replay_config(const ReplayConfigMessage& message);
TXProgressMessage txprogress_message{};
RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest};
};
#endif/*__PROC_GPS_SIM_HPP__*/
#endif /*__PROC_GPS_SIM_HPP__*/

View File

@@ -28,88 +28,87 @@
#include <cstdint>
void JammerProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
for (size_t i = 0; i < buffer.count; i++) {
if (!configured) return;
if (!jammer_duration) {
// Find next enabled range
do {
current_range++;
if (current_range == JAMMER_MAX_CH) current_range = 0;
} while (!jammer_channels[current_range].enabled);
jammer_duration = jammer_channels[current_range].duration;
jammer_bw = jammer_channels[current_range].width / 2; // TODO: Exact value
// Ask for retune
message.freq = jammer_channels[current_range].center;
message.range = current_range;
shared_memory.application_queue.push(message);
} else {
jammer_duration--;
}
// Phase noise
if (!period_counter) {
period_counter = noise_period;
if (noise_type == JammerType::TYPE_FSK) {
sample = (sample + lfsr) >> 1;
} else if (noise_type == JammerType::TYPE_TONE) {
tone_delta = 150000 + (lfsr >> 9); // Approx 100Hz to 6kHz
} else if (noise_type == JammerType::TYPE_SWEEP) {
sample++; // This is like saw wave FM
}
feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1;
lfsr = (lfsr << 1) | feedback;
if (!lfsr) lfsr = 0x1337; // Shouldn't do this :(
} else {
period_counter--;
}
if (noise_type == JammerType::TYPE_TONE) {
aphase += tone_delta;
sample = sine_table_i8[(aphase & 0xFF000000) >> 24];
}
delta = sample * jammer_bw;
phase += delta;
sphase = phase + (64 << 24);
for (size_t i = 0; i < buffer.count; i++) {
if (!jammer_duration) {
// Find next enabled range
do {
current_range++;
if (current_range == JAMMER_MAX_CH) current_range = 0;
} while (!jammer_channels[current_range].enabled);
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
jammer_duration = jammer_channels[current_range].duration;
jammer_bw = jammer_channels[current_range].width / 2; // TODO: Exact value
buffer.p[i] = {re, im};
}
// Ask for retune
message.freq = jammer_channels[current_range].center;
message.range = current_range;
shared_memory.application_queue.push(message);
} else {
jammer_duration--;
}
// Phase noise
if (!period_counter) {
period_counter = noise_period;
if (noise_type == JammerType::TYPE_FSK) {
sample = (sample + lfsr) >> 1;
} else if (noise_type == JammerType::TYPE_TONE) {
tone_delta = 150000 + (lfsr >> 9); // Approx 100Hz to 6kHz
} else if (noise_type == JammerType::TYPE_SWEEP) {
sample++; // This is like saw wave FM
}
feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1;
lfsr = (lfsr << 1) | feedback;
if (!lfsr) lfsr = 0x1337; // Shouldn't do this :(
} else {
period_counter--;
}
if (noise_type == JammerType::TYPE_TONE) {
aphase += tone_delta;
sample = sine_table_i8[(aphase & 0xFF000000) >> 24];
}
delta = sample * jammer_bw;
phase += delta;
sphase = phase + (64 << 24);
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
buffer.p[i] = {re, im};
}
};
void JammerProcessor::on_message(const Message* const msg) {
if (msg->id == Message::ID::JammerConfigure) {
const auto message = *reinterpret_cast<const JammerConfigureMessage*>(msg);
if (message.run) {
jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
noise_type = message.type;
noise_period = 3072000 / message.speed;
if (noise_type == JammerType::TYPE_SWEEP)
noise_period >>= 8;
period_counter = 0;
jammer_duration = 0;
current_range = 0;
lfsr = 0xDEAD0012;
configured = true;
} else {
configured = false;
}
}
if (msg->id == Message::ID::JammerConfigure) {
const auto message = *reinterpret_cast<const JammerConfigureMessage*>(msg);
if (message.run) {
jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
noise_type = message.type;
noise_period = 3072000 / message.speed;
if (noise_type == JammerType::TYPE_SWEEP)
noise_period >>= 8;
period_counter = 0;
jammer_duration = 0;
current_range = 0;
lfsr = 0xDEAD0012;
configured = true;
} else {
configured = false;
}
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<JammerProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<JammerProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -31,29 +31,29 @@
using namespace jammer;
class JammerProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const msg) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
bool configured { false };
BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
JammerChannel * jammer_channels { };
JammerType noise_type { };
uint32_t tone_delta { 0 }, lfsr { }, feedback { };
uint32_t noise_period { 0 }, period_counter { 0 };
uint32_t jammer_duration { 0 };
uint32_t current_range { 0 };
int64_t jammer_center { 0 }, jammer_bw { 0 };
uint32_t sample_count { 0 };
uint32_t aphase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 };
int8_t sample { 0 };
int8_t re { 0 }, im { 0 };
RetuneMessage message { };
void on_message(const Message* const msg) override;
private:
bool configured{false};
BasebandThread baseband_thread{3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit};
JammerChannel* jammer_channels{};
JammerType noise_type{};
uint32_t tone_delta{0}, lfsr{}, feedback{};
uint32_t noise_period{0}, period_counter{0};
uint32_t jammer_duration{0};
uint32_t current_range{0};
int64_t jammer_center{0}, jammer_bw{0};
uint32_t sample_count{0};
uint32_t aphase{0}, phase{0}, delta{0}, sphase{0};
int8_t sample{0};
int8_t re{0}, im{0};
RetuneMessage message{};
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
@@ -28,147 +28,146 @@
#include <cstdint>
void MicTXProcessor::execute(const buffer_c8_t& buffer){
void MicTXProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 1536000/2048 = 750Hz
// This is called at 1536000/2048 = 750Hz
if (!configured) return;
audio_input.read_audio_buffer(audio_buffer);
modulator->set_gain_shiftbits_vumeter_beep(audio_gain, audio_shift_bits_s16, play_beep ) ;
modulator->execute(audio_buffer, buffer, configured, beep_index, beep_timer, txprogress_message, level_message, power_acc_count, divider ); // Now "Key Tones & CTCSS" baseband additon inside FM mod. dsp_modulate.cpp"
/* Original fw 1.3.1 good reference, beep and vu-meter
for (size_t i = 0; i < buffer.count; i++) {
if (!play_beep) {
sample = audio_buffer.p[i >> 6] >> 8; // 1536000 / 64 = 24000
sample *= audio_gain;
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (power_acc_count) {
power_acc_count--;
} else {
power_acc_count = divider;
level_message.value = power_acc / (divider / 4); // Why ?
shared_memory.application_queue.push(level_message);
power_acc = 0;
}
} else {
if (beep_timer) {
beep_timer--;
} else {
beep_timer = baseband_fs * 0.05; // 50ms
if (beep_index == BEEP_TONES_NB) {
configured = false;
shared_memory.application_queue.push(txprogress_message);
} else {
beep_gen.configure(beep_deltas[beep_index], 1.0);
beep_index++;
}
}
sample = beep_gen.process(0); // TODO : Pending how to move inside modulate.cpp
}
*/
/* Original fw 1.3.1 good reference FM moulation version, including "key tones CTCSS" fw 1.3.1
sample = tone_gen.process(sample);
// FM
if (configured) {
delta = sample * fm_delta;
phase += delta;
sphase = phase >> 24;
if (!configured) return;
re = (sine_table_i8[(sphase + 64) & 255]);
im = (sine_table_i8[sphase]);
} else {
re = 0;
im = 0;
}
buffer.p[i] = { re, im };
} */
audio_input.read_audio_buffer(audio_buffer);
modulator->set_gain_shiftbits_vumeter_beep(audio_gain, audio_shift_bits_s16, play_beep);
modulator->execute(audio_buffer, buffer, configured, beep_index, beep_timer, txprogress_message, level_message, power_acc_count, divider); // Now "Key Tones & CTCSS" baseband additon inside FM mod. dsp_modulate.cpp"
/* Original fw 1.3.1 good reference, beep and vu-meter
for (size_t i = 0; i < buffer.count; i++) {
if (!play_beep) {
sample = audio_buffer.p[i >> 6] >> 8; // 1536000 / 64 = 24000
sample *= audio_gain;
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
if (power_acc_count) {
power_acc_count--;
} else {
power_acc_count = divider;
level_message.value = power_acc / (divider / 4); // Why ?
shared_memory.application_queue.push(level_message);
power_acc = 0;
}
} else {
if (beep_timer) {
beep_timer--;
} else {
beep_timer = baseband_fs * 0.05; // 50ms
if (beep_index == BEEP_TONES_NB) {
configured = false;
shared_memory.application_queue.push(txprogress_message);
} else {
beep_gen.configure(beep_deltas[beep_index], 1.0);
beep_index++;
}
}
sample = beep_gen.process(0); // TODO : Pending how to move inside modulate.cpp
}
*/
/* Original fw 1.3.1 good reference FM moulation version, including "key tones CTCSS" fw 1.3.1
sample = tone_gen.process(sample);
// FM
if (configured) {
delta = sample * fm_delta;
phase += delta;
sphase = phase >> 24;
re = (sine_table_i8[(sphase + 64) & 255]);
im = (sine_table_i8[sphase]);
} else {
re = 0;
im = 0;
}
buffer.p[i] = { re, im };
} */
}
void MicTXProcessor::on_message(const Message* const msg) {
const AudioTXConfigMessage config_message = *reinterpret_cast<const AudioTXConfigMessage*>(msg);
const RequestSignalMessage request_message = *reinterpret_cast<const RequestSignalMessage*>(msg);
switch(msg->id) {
case Message::ID::AudioTXConfig:
if (fm_enabled) {
dsp::modulate::FM *fm = new dsp::modulate::FM();
const AudioTXConfigMessage config_message = *reinterpret_cast<const AudioTXConfigMessage*>(msg);
const RequestSignalMessage request_message = *reinterpret_cast<const RequestSignalMessage*>(msg);
// Config fm_delta private var inside DSP modulate.cpp
fm->set_fm_delta(config_message.deviation_hz * (0xFFFFFFUL / baseband_fs));
switch (msg->id) {
case Message::ID::AudioTXConfig:
if (fm_enabled) {
dsp::modulate::FM* fm = new dsp::modulate::FM();
// Config properly the private tone_gen function parameters inside DSP modulate.cpp
fm->set_tone_gen_configure(config_message.tone_key_delta, config_message.tone_key_mix_weight);
modulator = fm;
}
// Config fm_delta private var inside DSP modulate.cpp
fm->set_fm_delta(config_message.deviation_hz * (0xFFFFFFUL / baseband_fs));
if (usb_enabled) {
modulator = new dsp::modulate::SSB();
modulator->set_mode(dsp::modulate::Mode::USB);
}
if (lsb_enabled) {
modulator = new dsp::modulate::SSB();
modulator->set_mode(dsp::modulate::Mode::LSB);
}
if (am_enabled) {
modulator = new dsp::modulate::AM();
modulator->set_mode(dsp::modulate::Mode::AM);
}
if (dsb_enabled) {
modulator = new dsp::modulate::AM();
modulator->set_mode(dsp::modulate::Mode::DSB);
}
// Config properly the private tone_gen function parameters inside DSP modulate.cpp
fm->set_tone_gen_configure(config_message.tone_key_delta, config_message.tone_key_mix_weight);
modulator = fm;
}
modulator->set_over(baseband_fs / 24000); // Keep no change.
am_enabled = config_message.am_enabled;
usb_enabled = config_message.usb_enabled;
lsb_enabled = config_message.lsb_enabled;
dsb_enabled = config_message.dsb_enabled;
if (!am_enabled || !usb_enabled || !lsb_enabled || !dsb_enabled) {
fm_enabled = true;
}
audio_gain = config_message.audio_gain;
audio_shift_bits_s16 = config_message.audio_shift_bits_s16;
divider = config_message.divider;
power_acc_count = 0;
if (usb_enabled) {
modulator = new dsp::modulate::SSB();
modulator->set_mode(dsp::modulate::Mode::USB);
}
// now this config moved, in the case Message::ID::AudioTXConfig , only FM case.
// tone_gen.configure(config_message.tone_key_delta, config_message.tone_key_mix_weight);
txprogress_message.done = true;
if (lsb_enabled) {
modulator = new dsp::modulate::SSB();
modulator->set_mode(dsp::modulate::Mode::LSB);
}
if (am_enabled) {
modulator = new dsp::modulate::AM();
modulator->set_mode(dsp::modulate::Mode::AM);
}
if (dsb_enabled) {
modulator = new dsp::modulate::AM();
modulator->set_mode(dsp::modulate::Mode::DSB);
}
play_beep = false;
configured = true;
break;
case Message::ID::RequestSignal:
if (request_message.signal == RequestSignalMessage::Signal::BeepRequest) {
beep_index = 0;
beep_timer = 0;
play_beep = true;
}
break;
modulator->set_over(baseband_fs / 24000); // Keep no change.
default:
break;
}
am_enabled = config_message.am_enabled;
usb_enabled = config_message.usb_enabled;
lsb_enabled = config_message.lsb_enabled;
dsb_enabled = config_message.dsb_enabled;
if (!am_enabled || !usb_enabled || !lsb_enabled || !dsb_enabled) {
fm_enabled = true;
}
audio_gain = config_message.audio_gain;
audio_shift_bits_s16 = config_message.audio_shift_bits_s16;
divider = config_message.divider;
power_acc_count = 0;
// now this config moved, in the case Message::ID::AudioTXConfig , only FM case.
// tone_gen.configure(config_message.tone_key_delta, config_message.tone_key_mix_weight);
txprogress_message.done = true;
play_beep = false;
configured = true;
break;
case Message::ID::RequestSignal:
if (request_message.signal == RequestSignalMessage::Signal::BeepRequest) {
beep_index = 0;
beep_timer = 0;
play_beep = true;
}
break;
default:
break;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<MicTXProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<MicTXProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
@@ -30,51 +30,50 @@
#include "dsp_modulate.hpp"
class MicTXProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const msg) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 1536000U;
bool configured { false };
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
int16_t audio_data[64];
buffer_s16_t audio_buffer {
audio_data,
sizeof(int16_t) * 64
};
AudioInput audio_input { };
// ToneGen tone_gen { }; moved to dsp_modulate.cpp
// ToneGen beep_gen { }; moved to dsp_modulate.cpp
dsp::modulate::Modulator *modulator = NULL ;
void on_message(const Message* const msg) override;
bool am_enabled { false };
bool fm_enabled { true };
bool usb_enabled { false };
bool lsb_enabled { false };
bool dsb_enabled { false };
private:
static constexpr size_t baseband_fs = 1536000U;
uint32_t divider { };
float audio_gain { };
uint8_t audio_shift_bits_s16 { } ; // shift bits factor to the captured ADC S16 audio sample.
bool configured{false};
uint64_t power_acc { 0 };
uint32_t power_acc_count { 0 };
bool play_beep { false };
uint32_t fm_delta { 0 };
uint32_t phase { 0 }, sphase { 0 };
int32_t sample { 0 }, delta { };
uint32_t beep_index { }, beep_timer { };
int8_t re { 0 }, im { 0 };
AudioLevelReportMessage level_message { };
TXProgressMessage txprogress_message { };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit};
int16_t audio_data[64];
buffer_s16_t audio_buffer{
audio_data,
sizeof(int16_t) * 64};
AudioInput audio_input{};
// ToneGen tone_gen { }; moved to dsp_modulate.cpp
// ToneGen beep_gen { }; moved to dsp_modulate.cpp
dsp::modulate::Modulator* modulator = NULL;
bool am_enabled{false};
bool fm_enabled{true};
bool usb_enabled{false};
bool lsb_enabled{false};
bool dsb_enabled{false};
uint32_t divider{};
float audio_gain{};
uint8_t audio_shift_bits_s16{}; // shift bits factor to the captured ADC S16 audio sample.
uint64_t power_acc{0};
uint32_t power_acc_count{0};
bool play_beep{false};
uint32_t fm_delta{0};
uint32_t phase{0}, sphase{0};
int32_t sample{0}, delta{};
uint32_t beep_index{}, beep_timer{};
int8_t re{0}, im{0};
AudioLevelReportMessage level_message{};
TXProgressMessage txprogress_message{};
};
#endif

View File

@@ -30,148 +30,147 @@
#include <cstddef>
void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
//bool new_state;
if( !configured ) {
return;
}
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
// bool new_state;
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
if (!configured) {
return;
}
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
feed_channel_stats(channel_out);
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
if (!pitch_rssi_enabled) {
// Normal mode, output demodulated audio
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
if (ctcss_detect_enabled) {
/* 24kHz int16_t[16]
* -> FIR filter, <300Hz pass, >300Hz stop, gain of 1
* -> 12kHz int16_t[8] */
auto audio_ctcss = ctcss_filter.execute(audio, work_audio_buffer);
// s16 to f32 for hpf
std::array<float, 8> audio_f;
for (size_t i = 0; i < audio_ctcss.count; i++) {
audio_f[i] = audio_ctcss.p[i] * ki;
}
hpf.execute_in_place(buffer_f32_t {
audio_f.data(),
audio_ctcss.count,
audio_ctcss.sampling_rate
});
// Zero-crossing detection
for (size_t c = 0; c < audio_ctcss.count; c++) {
cur_sample = audio_f[c];
if (cur_sample * prev_sample < 0.0) {
z_acc += z_timer;
z_timer = 0;
z_count++;
} else
z_timer++;
prev_sample = cur_sample;
}
if (z_count >= 30) {
ctcss_message.value = (100 * 12000 / 2 * z_count) / z_acc;
shared_memory.application_queue.push(ctcss_message);
z_count = 0;
z_acc = 0;
}
}
} else {
// Direction-finding mode; output tone with pitch related to RSSI
for (size_t c = 0; c < 16; c++) {
tone_buffer.p[c] = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]) * 128;
tone_phase += tone_delta;
}
audio_output.write(tone_buffer);
/*new_state = audio_output.is_squelched();
if (new_state && !old_state)
shared_memory.application_queue.push(sig_message);
old_state = new_state;*/
}
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
feed_channel_stats(channel_out);
if (!pitch_rssi_enabled) {
// Normal mode, output demodulated audio
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
if (ctcss_detect_enabled) {
/* 24kHz int16_t[16]
* -> FIR filter, <300Hz pass, >300Hz stop, gain of 1
* -> 12kHz int16_t[8] */
auto audio_ctcss = ctcss_filter.execute(audio, work_audio_buffer);
// s16 to f32 for hpf
std::array<float, 8> audio_f;
for (size_t i = 0; i < audio_ctcss.count; i++) {
audio_f[i] = audio_ctcss.p[i] * ki;
}
hpf.execute_in_place(buffer_f32_t{
audio_f.data(),
audio_ctcss.count,
audio_ctcss.sampling_rate});
// Zero-crossing detection
for (size_t c = 0; c < audio_ctcss.count; c++) {
cur_sample = audio_f[c];
if (cur_sample * prev_sample < 0.0) {
z_acc += z_timer;
z_timer = 0;
z_count++;
} else
z_timer++;
prev_sample = cur_sample;
}
if (z_count >= 30) {
ctcss_message.value = (100 * 12000 / 2 * z_count) / z_acc;
shared_memory.application_queue.push(ctcss_message);
z_count = 0;
z_acc = 0;
}
}
} else {
// Direction-finding mode; output tone with pitch related to RSSI
for (size_t c = 0; c < 16; c++) {
tone_buffer.p[c] = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]) * 128;
tone_phase += tone_delta;
}
audio_output.write(tone_buffer);
/*new_state = audio_output.is_squelched();
if (new_state && !old_state)
shared_memory.application_queue.push(sig_message);
old_state = new_state;*/
}
}
void NarrowbandFMAudio::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
switch (message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
case Message::ID::NBFMConfigure:
configure(*reinterpret_cast<const NBFMConfigureMessage*>(message));
break;
case Message::ID::NBFMConfigure:
configure(*reinterpret_cast<const NBFMConfigureMessage*>(message));
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
case Message::ID::PitchRSSIConfigure:
pitch_rssi_config(*reinterpret_cast<const PitchRSSIConfigureMessage*>(message));
break;
default:
break;
}
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
case Message::ID::PitchRSSIConfigure:
pitch_rssi_config(*reinterpret_cast<const PitchRSSIConfigureMessage*>(message));
break;
default:
break;
}
}
void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation;
const size_t demod_input_fs = channel_filter_output_fs;
const size_t demod_input_fs = channel_filter_output_fs;
decim_0.configure(message.decim_0_filter.taps, 33554432);
decim_1.configure(message.decim_1_filter.taps, 131072);
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
demod.configure(demod_input_fs, message.deviation);
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(1.0f);
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
hpf.configure(audio_24k_hpf_30hz_config);
ctcss_filter.configure(taps_64_lp_025_025.taps);
decim_0.configure(message.decim_0_filter.taps, 33554432);
decim_1.configure(message.decim_1_filter.taps, 131072);
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
demod.configure(demod_input_fs, message.deviation);
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(1.0f);
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
configured = true;
hpf.configure(audio_24k_hpf_30hz_config);
ctcss_filter.configure(taps_64_lp_025_025.taps);
configured = true;
}
void NarrowbandFMAudio::pitch_rssi_config(const PitchRSSIConfigureMessage& message) {
pitch_rssi_enabled = message.enabled;
tone_delta = (message.rssi + 1000) * ((1ULL << 32) / 24000);
pitch_rssi_enabled = message.enabled;
tone_delta = (message.rssi + 1000) * ((1ULL << 32) / 24000);
}
void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
if (message.config) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NarrowbandFMAudio>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<NarrowbandFMAudio>()};
event_dispatcher.run();
return 0;
}

View File

@@ -37,73 +37,69 @@
#include <cstdint>
class NarrowbandFMAudio : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 3072000;
private:
static constexpr size_t baseband_fs = 3072000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
const buffer_s16_t work_audio_buffer {
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)
};
std::array<int16_t, 16> audio { };
const buffer_s16_t audio_buffer {
(int16_t*)audio.data(),
sizeof(audio) / sizeof(int16_t)
};
std::array<int16_t, 16> tone { };
const buffer_s16_t tone_buffer {
(int16_t*)tone.data(),
sizeof(tone) / sizeof(int16_t)
};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
const buffer_s16_t work_audio_buffer{
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
// For CTCSS decoding
dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter { };
IIRBiquadFilter hpf { };
std::array<int16_t, 16> audio{};
const buffer_s16_t audio_buffer{
(int16_t*)audio.data(),
sizeof(audio) / sizeof(int16_t)};
dsp::demodulate::FM demod { };
std::array<int16_t, 16> tone{};
const buffer_s16_t tone_buffer{
(int16_t*)tone.data(),
sizeof(tone) / sizeof(int16_t)};
AudioOutput audio_output { };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::decimate::FIRAndDecimateComplex channel_filter{};
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
SpectrumCollector channel_spectrum { };
uint32_t tone_phase { 0 };
uint32_t tone_delta { 0 };
bool pitch_rssi_enabled { false };
float cur_sample { }, prev_sample { };
uint32_t z_acc { 0}, z_timer { 0 }, z_count { 0 };
bool ctcss_detect_enabled { true };
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
// For CTCSS decoding
dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter{};
IIRBiquadFilter hpf{};
bool configured { false };
void pitch_rssi_config(const PitchRSSIConfigureMessage& message);
void configure(const NBFMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
//RequestSignalMessage sig_message { RequestSignalMessage::Signal::Squelched };
CodedSquelchMessage ctcss_message { 0 };
dsp::demodulate::FM demod{};
AudioOutput audio_output{};
SpectrumCollector channel_spectrum{};
uint32_t tone_phase{0};
uint32_t tone_delta{0};
bool pitch_rssi_enabled{false};
float cur_sample{}, prev_sample{};
uint32_t z_acc{0}, z_timer{0}, z_count{0};
bool ctcss_detect_enabled{true};
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
bool configured{false};
void pitch_rssi_config(const PitchRSSIConfigureMessage& message);
void configure(const NBFMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
// RequestSignalMessage sig_message { RequestSignalMessage::Signal::Squelched };
CodedSquelchMessage ctcss_message{0};
};
#endif/*__PROC_NFM_AUDIO_H__*/
#endif /*__PROC_NFM_AUDIO_H__*/

View File

@@ -26,13 +26,13 @@
#include <cstdint>
void NOOPProcessor::execute(const buffer_c8_t& buffer) {
for (size_t i = 0; i<buffer.count; i++) {
buffer.p[i] = {0, 0};
}
for (size_t i = 0; i < buffer.count; i++) {
buffer.p[i] = {0, 0};
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NOOPProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<NOOPProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -27,11 +27,11 @@
#include "baseband_thread.hpp"
class NOOPProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
private:
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
private:
BasebandThread baseband_thread{1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit};
};
#endif

View File

@@ -27,257 +27,231 @@
#include "event_m4.hpp"
void NRFRxProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
// FM demodulation
if (!configured) return;
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
feed_channel_stats(decim_0_out);
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
int g_srate = 4; //4 for 250KPS
//int g_srate = 1; //1 for 1MPS, not working yet
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
rb_head++;
rb_head=(rb_head)%RB_SIZE;
// FM demodulation
rb_buf[rb_head] = current_sample;
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
feed_channel_stats(decim_0_out);
skipSamples = skipSamples - 1;
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
int g_srate = 4; // 4 for 250KPS
// int g_srate = 1; //1 for 1MPS, not working yet
int32_t current_sample = audio_oversampled.p[c]; // if I directly use this, some results can pass crc but not correct.
rb_head++;
rb_head = (rb_head) % RB_SIZE;
if (skipSamples<1)
{
int32_t threshold_tmp=0;
for (int c=0;c<8*g_srate;c++)
{
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
}
rb_buf[rb_head] = current_sample;
g_threshold = (int32_t)threshold_tmp/(8*g_srate);
int transitions=0;
if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold)
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
transitions = transitions + 1;
}
}
else
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
transitions = transitions + 1;
}
}
bool packet_detected=false;
//if ( transitions==4 && abs(g_threshold)<15500)
if ( transitions==4 && abs(g_threshold)<15500)
{
int packet_length = 0;
uint8_t tmp_buf[10];
uint8_t packet_data[500];
uint8_t packet_packed[50];
uint16_t pcf;
uint32_t packet_crc;
uint32_t calced_crc;
uint64_t packet_addr_l;
skipSamples = skipSamples - 1;
/* extract address */
packet_addr_l=0;
if (skipSamples < 1) {
int32_t threshold_tmp = 0;
for (int c = 0; c < 8 * g_srate; c++) {
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head + c) % RB_SIZE];
}
for (int t=0;t<5;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
g_threshold = (int32_t)threshold_tmp / (8 * g_srate);
for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8;
int transitions = 0;
if (rb_buf[(rb_head + 9 * g_srate) % RB_SIZE] > g_threshold) {
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + c * g_srate) % RB_SIZE] > rb_buf[(rb_head + (c + 1) * g_srate) % RB_SIZE])
transitions = transitions + 1;
}
} else {
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + c * g_srate) % RB_SIZE] < rb_buf[(rb_head + (c + 1) * g_srate) % RB_SIZE])
transitions = transitions + 1;
}
}
//channel_number = 26;
bool packet_detected = false;
// if ( transitions==4 && abs(g_threshold)<15500)
if (transitions == 4 && abs(g_threshold) < 15500) {
int packet_length = 0;
uint8_t tmp_buf[10];
uint8_t packet_data[500];
uint8_t packet_packed[50];
uint16_t pcf;
uint32_t packet_crc;
uint32_t calced_crc;
uint64_t packet_addr_l;
/* extract pcf */
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
/* extract address */
packet_addr_l = 0;
pcf = tmp_buf[0]<<8 | tmp_buf[1];
pcf >>=7;
for (int t = 0; t < 5; t++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + (1 * 8 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
tmp_buf[t] = byte;
}
/* extract packet length, avoid excessive length packets */
if(packet_length == 0)
packet_length=(int)pcf>>3;
if (packet_length>32)
packet_detected = false;
for (int t = 0; t < 5; t++) packet_addr_l |= ((uint64_t)tmp_buf[t]) << (4 - t) * 8;
/* extract data */
for (int t=0;t<packet_length;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(6*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
packet_data[t]=byte;
}
// channel_number = 26;
/* Prepare packed bit stream for CRC calculation */
uint64_t packet_header=packet_addr_l;
packet_header<<=9;
packet_header|=pcf;
for (int c=0;c<7;c++){
packet_packed[c]=(packet_header>>((6-c)*8))&0xFF;
}
/* extract pcf */
for (int t = 0; t < 2; t++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + (6 * 8 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
tmp_buf[t] = byte;
}
for (int c=0;c<packet_length;c++){
packet_packed[c+7]=packet_data[c];
}
pcf = tmp_buf[0] << 8 | tmp_buf[1];
pcf >>= 7;
/* calculate packet crc */
const uint8_t* data = packet_packed;
size_t data_len = 7+packet_length;
bool bit;
uint8_t cc;
uint_fast16_t crc=0x3C18;
while (data_len--) {
cc = *data++;
for (uint8_t i = 0x80; i > 0; i >>= 1)
{
bit = crc & 0x8000;
if (cc & i)
{
bit = !bit;
}
crc <<= 1;
if (bit)
{
crc ^= 0x1021;
}
}
crc &= 0xffff;
}
calced_crc = (uint16_t)(crc & 0xffff);
/* extract packet length, avoid excessive length packets */
if (packet_length == 0)
packet_length = (int)pcf >> 3;
if (packet_length > 32)
packet_detected = false;
/* extract crc */
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
packet_crc = tmp_buf[0]<<8 | tmp_buf[1];
/* extract data */
for (int t = 0; t < packet_length; t++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + (6 * 8 + 9 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
packet_data[t] = byte;
}
/* NRF24L01+ packet found, dump information */
//if (packet_addr_l==0xE7E7E7E7)
if (packet_crc==calced_crc)
{
data_message.is_data = false;
data_message.value = 'A';
shared_memory.application_queue.push(data_message);
/* Prepare packed bit stream for CRC calculation */
uint64_t packet_header = packet_addr_l;
packet_header <<= 9;
packet_header |= pcf;
for (int c = 0; c < 7; c++) {
packet_packed[c] = (packet_header >> ((6 - c) * 8)) & 0xFF;
}
data_message.is_data = true;
data_message.value = packet_addr_l;
shared_memory.application_queue.push(data_message);
for (int c = 0; c < packet_length; c++) {
packet_packed[c + 7] = packet_data[c];
}
for (int c=0;c<7;c++)
{
data_message.is_data = true;
data_message.value = packet_addr_l >> 8;
shared_memory.application_queue.push(data_message);
}
/*data_message.is_data = true;
data_message.value = packet_addr_l;
shared_memory.application_queue.push(data_message);
/* calculate packet crc */
const uint8_t* data = packet_packed;
size_t data_len = 7 + packet_length;
bool bit;
uint8_t cc;
uint_fast16_t crc = 0x3C18;
while (data_len--) {
cc = *data++;
for (uint8_t i = 0x80; i > 0; i >>= 1) {
bit = crc & 0x8000;
if (cc & i) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x1021;
}
}
crc &= 0xffff;
}
calced_crc = (uint16_t)(crc & 0xffff);
data_message.is_data = true;
data_message.value = packet_addr_l >> 8;
shared_memory.application_queue.push(data_message);*/
/* extract crc */
for (int t = 0; t < 2; t++) {
bool current_bit;
uint8_t byte = 0;
for (int c = 0; c < 8; c++) {
if (rb_buf[(rb_head + ((6 + packet_length) * 8 + 9 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7 - c);
}
tmp_buf[t] = byte;
}
packet_crc = tmp_buf[0] << 8 | tmp_buf[1];
data_message.is_data = false;
data_message.value = 'B';
shared_memory.application_queue.push(data_message);
/* NRF24L01+ packet found, dump information */
// if (packet_addr_l==0xE7E7E7E7)
if (packet_crc == calced_crc) {
data_message.is_data = false;
data_message.value = 'A';
shared_memory.application_queue.push(data_message);
for (int c=0;c<packet_length;c++)
{
data_message.is_data = true;
data_message.value = packet_data[c];
shared_memory.application_queue.push(data_message);
}
data_message.is_data = true;
data_message.value = packet_addr_l;
shared_memory.application_queue.push(data_message);
data_message.is_data = false;
data_message.value = 'C';
shared_memory.application_queue.push(data_message);
for (int c = 0; c < 7; c++) {
data_message.is_data = true;
data_message.value = packet_addr_l >> 8;
shared_memory.application_queue.push(data_message);
}
/*data_message.is_data = true;
data_message.value = packet_addr_l;
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = packet_addr_l >> 8;
shared_memory.application_queue.push(data_message);*/
packet_detected = true;
}
else
packet_detected = false;
}
data_message.is_data = false;
data_message.value = 'B';
shared_memory.application_queue.push(data_message);
if (packet_detected)
{
skipSamples=20;
}
}
}
for (int c = 0; c < packet_length; c++) {
data_message.is_data = true;
data_message.value = packet_data[c];
shared_memory.application_queue.push(data_message);
}
data_message.is_data = false;
data_message.value = 'C';
shared_memory.application_queue.push(data_message);
packet_detected = true;
} else
packet_detected = false;
}
if (packet_detected) {
skipSamples = 20;
}
}
}
}
void NRFRxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::NRFRxConfigure)
configure(*reinterpret_cast<const NRFRxConfigureMessage*>(message));
if (message->id == Message::ID::NRFRxConfigure)
configure(*reinterpret_cast<const NRFRxConfigureMessage*>(message));
}
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
(void)message; //avoir unused warning
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
(void)message; // avoir unused warning
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
configured = true;
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NRFRxProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<NRFRxProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -37,60 +37,55 @@
#include "message.hpp"
class NRFRxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 4000000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
void on_message(const Message* const message) override;
std::array<complex16_t, 512> spectrum { };
const buffer_c16_t spectrum_buffer {
spectrum.data(),
spectrum.size()
};
private:
static constexpr size_t baseband_fs = 4000000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
const buffer_s16_t work_audio_buffer {
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)
};
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
std::array<int16_t, 1000> rb_buf { 0 };
std::array<complex16_t, 512> spectrum{};
const buffer_c16_t spectrum_buffer{
spectrum.data(),
spectrum.size()};
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
dsp::demodulate::FM demod { };
int rb_head {-1};
int32_t g_threshold {0};
//uint8_t g_srate {8};
uint8_t channel_number {38};
int skipSamples {1000};
int RB_SIZE {1000};
const buffer_s16_t work_audio_buffer{
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)};
bool configured { false };
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line{0};
std::array<int16_t, 1000> rb_buf{0};
void configure(const NRFRxConfigureMessage& message);
AFSKDataMessage data_message { false, 0 };
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
dsp::decimate::FIRC16xR16x16Decim2 decim_1{};
dsp::demodulate::FM demod{};
int rb_head{-1};
int32_t g_threshold{0};
// uint8_t g_srate {8};
uint8_t channel_number{38};
int skipSamples{1000};
int RB_SIZE{1000};
bool configured{false};
void configure(const NRFRxConfigureMessage& message);
AFSKDataMessage data_message{false, 0};
};
#endif/*__PROC_NRFRX_H__*/
#endif /*__PROC_NRFRX_H__*/

View File

@@ -28,234 +28,234 @@
#include <cstdint>
inline void OOKProcessor::write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i) {
int8_t re, im;
int8_t re, im;
if (bit_value) {
phase = (phase + 200); // What ?
sphase = phase + (64 << 18);
if (bit_value) {
phase = (phase + 200); // What ?
sphase = phase + (64 << 18);
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
} else {
re = 0;
im = 0;
}
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
} else {
re = 0;
im = 0;
}
buffer.p[i] = {re, im};
buffer.p[i] = {re, im};
}
bool OOKProcessor::scan_init(unsigned int order) {
if (order > MAX_DE_BRUIJN_ORDER)
return false;
if (order > MAX_DE_BRUIJN_ORDER)
return false;
scan_done = false;
scan_progress = 0;
scan_done = false;
scan_progress = 0;
k = 0;
idx = 1;
k = 0;
idx = 1;
duval_symbols = 2; // 2 for binary, 3 for ternary encoders
duval_length = 0;
duval_bit = 0;
duval_sample_bit = 0;
duval_symbol = 0;
duval_symbols = 2; // 2 for binary, 3 for ternary encoders
duval_length = 0;
duval_bit = 0;
duval_sample_bit = 0;
duval_symbol = 0;
memset(v, 0, sizeof(v));
return true;
memset(v, 0, sizeof(v));
return true;
}
bool OOKProcessor::scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr) {
// encode data: 0 = 1000, 1 = 1110
// @TODO: make this user-configurable
const uint8_t sym[] = { 0b0001, 0b0111 };
constexpr auto symbol_length = 4;
// encode data: 0 = 1000, 1 = 1110
// @TODO: make this user-configurable
const uint8_t sym[] = {0b0001, 0b0111};
constexpr auto symbol_length = 4;
// iterate over every symbol in the sequence and convert it to bits with required bitrate
for (; duval_bit < duval_length; duval_bit++) {
auto val = v_tmp[duval_bit];
for (; duval_symbol < symbol_length; duval_symbol++) {
auto s = sym[val] & (1 << duval_symbol);
for (; duval_sample_bit < samples_per_bit; duval_sample_bit++) {
if (buf_ptr >= buffer.count) {
// buffer is full - continue next time
txprogress_message.done = false;
txprogress_message.progress = scan_progress++;
shared_memory.application_queue.push(txprogress_message);
return false;
}
write_sample(buffer, s, buf_ptr++);
}
duval_sample_bit = 0;
}
duval_symbol = 0;
}
duval_bit = 0;
return true;
// iterate over every symbol in the sequence and convert it to bits with required bitrate
for (; duval_bit < duval_length; duval_bit++) {
auto val = v_tmp[duval_bit];
for (; duval_symbol < symbol_length; duval_symbol++) {
auto s = sym[val] & (1 << duval_symbol);
for (; duval_sample_bit < samples_per_bit; duval_sample_bit++) {
if (buf_ptr >= buffer.count) {
// buffer is full - continue next time
txprogress_message.done = false;
txprogress_message.progress = scan_progress++;
shared_memory.application_queue.push(txprogress_message);
return false;
}
write_sample(buffer, s, buf_ptr++);
}
duval_sample_bit = 0;
}
duval_symbol = 0;
}
duval_bit = 0;
return true;
}
inline size_t OOKProcessor::duval_algo_step() {
size_t buf_ptr = 0;
const unsigned int w = de_bruijn_length;
size_t buf_ptr = 0;
const unsigned int w = de_bruijn_length;
// Duval's algorithm for generating de Bruijn sequence
while (idx) {
if (w % idx == 0) {
for (unsigned int k = 0; k < idx; k++)
v_tmp[buf_ptr++] = v[k];
k = 0;
}
// Duval's algorithm for generating de Bruijn sequence
while (idx) {
if (w % idx == 0) {
for (unsigned int k = 0; k < idx; k++)
v_tmp[buf_ptr++] = v[k];
k = 0;
}
for (unsigned int j = 0; j < w - idx; j++)
v[idx + j] = v[j];
for (unsigned int j = 0; j < w - idx; j++)
v[idx + j] = v[j];
for (idx = w; (idx > 0) && (v[idx - 1] >= duval_symbols - 1); idx--) ;
for (idx = w; (idx > 0) && (v[idx - 1] >= duval_symbols - 1); idx--)
;
if (idx)
v[idx - 1]++;
if (idx)
v[idx - 1]++;
if (buf_ptr) {
// we fill at most de_bruijn_length number of elements
return buf_ptr;
}
}
if (buf_ptr) {
// we fill at most de_bruijn_length number of elements
return buf_ptr;
}
}
return 0;
return 0;
}
void OOKProcessor::scan_process(const buffer_c8_t& buffer) {
size_t buf_ptr = 0;
size_t buf_ptr = 0;
// transmit any leftover bits from previous step
if (!scan_encode(buffer, buf_ptr))
return;
// transmit any leftover bits from previous step
if (!scan_encode(buffer, buf_ptr))
return;
while (1) {
// calculate next chunk of deBruijn sequence
duval_length = duval_algo_step();
while (1) {
// calculate next chunk of deBruijn sequence
duval_length = duval_algo_step();
if (duval_length == 0) {
// last chunk - done
if (!scan_done) {
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
}
scan_done = 1;
if (duval_length == 0) {
// last chunk - done
if (!scan_done) {
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
}
scan_done = 1;
// clear the remaining buffer in case we have any bytes left
for (size_t i = buf_ptr; i < buffer.count; i++)
buffer.p[i] = { 0, 0 };
// clear the remaining buffer in case we have any bytes left
for (size_t i = buf_ptr; i < buffer.count; i++)
buffer.p[i] = {0, 0};
break;
}
break;
}
duval_bit = 0;
duval_sample_bit = 0;
duval_symbol = 0;
duval_bit = 0;
duval_sample_bit = 0;
duval_symbol = 0;
// encode the sequence into required format
if (!scan_encode(buffer, buf_ptr))
break;
}
// encode the sequence into required format
if (!scan_encode(buffer, buf_ptr))
break;
}
}
void OOKProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 2.28M/2048 = 1113Hz
// This is called at 2.28M/2048 = 1113Hz
if (!configured) return;
if (!configured) return;
if (de_bruijn_length) {
scan_process(buffer);
return;
}
if (de_bruijn_length) {
scan_process(buffer);
return;
}
for (size_t i = 0; i < buffer.count; i++) {
for (size_t i = 0; i < buffer.count; i++) {
// Synthesis at 2.28M/10 = 228kHz
if (!s) {
s = 10 - 1;
if (sample_count >= samples_per_bit) {
if (configured) {
if (bit_pos >= length) {
// End of data
if (pause_counter == 0) {
pause_counter = pause;
cur_bit = 0;
} else if (pause_counter == 1) {
if (repeat_counter < repeat) {
// Repeat
cur_bit = shared_memory.bb_data.data[0] & 0x80;
txprogress_message.progress = repeat_counter + 1;
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
bit_pos = 1;
repeat_counter++;
} else {
// Stop
cur_bit = 0;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
configured = false;
}
pause_counter = 0;
} else {
pause_counter--;
}
} else {
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
bit_pos++;
}
}
// Synthesis at 2.28M/10 = 228kHz
if (!s) {
s = 10 - 1;
if (sample_count >= samples_per_bit) {
if (configured) {
if (bit_pos >= length) {
// End of data
if (pause_counter == 0) {
pause_counter = pause;
cur_bit = 0;
} else if (pause_counter == 1) {
if (repeat_counter < repeat) {
// Repeat
cur_bit = shared_memory.bb_data.data[0] & 0x80;
txprogress_message.progress = repeat_counter + 1;
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
bit_pos = 1;
repeat_counter++;
} else {
// Stop
cur_bit = 0;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
configured = false;
}
pause_counter = 0;
} else {
pause_counter--;
}
} else {
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
bit_pos++;
}
}
sample_count = 0;
} else {
sample_count++;
}
} else {
s--;
}
sample_count = 0;
} else {
sample_count++;
}
} else {
s--;
}
write_sample(buffer, cur_bit, i);
}
write_sample(buffer, cur_bit, i);
}
}
void OOKProcessor::on_message(const Message* const p) {
const auto message = *reinterpret_cast<const OOKConfigureMessage*>(p);
const auto message = *reinterpret_cast<const OOKConfigureMessage*>(p);
if (message.id == Message::ID::OOKConfigure) {
configured = false;
if (message.id == Message::ID::OOKConfigure) {
configured = false;
repeat = message.repeat - 1;
length = message.stream_length;
pause = message.pause_symbols + 1;
de_bruijn_length = message.de_bruijn_length;
samples_per_bit = message.samples_per_bit;
repeat = message.repeat - 1;
length = message.stream_length;
pause = message.pause_symbols + 1;
de_bruijn_length = message.de_bruijn_length;
samples_per_bit = message.samples_per_bit;
if (!length && !samples_per_bit) {
// shutdown
return;
}
if (!length && !samples_per_bit) {
// shutdown
return;
}
if (de_bruijn_length) {
if (!scan_init(de_bruijn_length))
return;
} else {
samples_per_bit /= 10;
}
if (de_bruijn_length) {
if (!scan_init(de_bruijn_length))
return;
} else {
samples_per_bit /= 10;
}
pause_counter = 0;
s = 0;
sample_count = samples_per_bit;
repeat_counter = 0;
bit_pos = 0;
cur_bit = 0;
txprogress_message.progress = 0;
txprogress_message.done = false;
configured = true;
}
pause_counter = 0;
s = 0;
sample_count = samples_per_bit;
repeat_counter = 0;
bit_pos = 0;
cur_bit = 0;
txprogress_message.progress = 0;
txprogress_message.done = false;
configured = true;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<OOKProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<OOKProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -27,51 +27,51 @@
#include "baseband_thread.hpp"
class OOKProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const p) override;
void on_message(const Message* const p) override;
private:
bool configured = false;
private:
bool configured = false;
BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
BasebandThread baseband_thread{2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit};
uint32_t samples_per_bit { 0 };
uint8_t repeat { 0 };
uint32_t length { 0 };
uint32_t pause { 0 };
uint8_t de_bruijn_length { 0 };
uint32_t samples_per_bit{0};
uint8_t repeat{0};
uint32_t length{0};
uint32_t pause{0};
uint8_t de_bruijn_length{0};
uint32_t pause_counter { 0 };
uint8_t repeat_counter { 0 };
uint8_t s { 0 };
uint16_t bit_pos { 0 };
uint8_t cur_bit { 0 };
uint32_t sample_count { 0 };
uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 };
int32_t tone_sample { 0 }, sig { 0 }, frq { 0 };
uint32_t pause_counter{0};
uint8_t repeat_counter{0};
uint8_t s{0};
uint16_t bit_pos{0};
uint8_t cur_bit{0};
uint32_t sample_count{0};
uint32_t tone_phase{0}, phase{0}, sphase{0};
int32_t tone_sample{0}, sig{0}, frq{0};
TXProgressMessage txprogress_message { };
TXProgressMessage txprogress_message{};
static constexpr auto MAX_DE_BRUIJN_ORDER = 24;
uint8_t v[MAX_DE_BRUIJN_ORDER];
uint8_t v_tmp[MAX_DE_BRUIJN_ORDER];
unsigned int idx { 0 };
unsigned int k { 0 };
unsigned int duval_symbols { 0 };
unsigned int duval_length { 0 };
unsigned int duval_bit { 0 };
unsigned int duval_sample_bit { 0 };
unsigned int duval_symbol { 0 };
size_t scan_progress{ 0 };
uint8_t scan_done { true };
static constexpr auto MAX_DE_BRUIJN_ORDER = 24;
uint8_t v[MAX_DE_BRUIJN_ORDER];
uint8_t v_tmp[MAX_DE_BRUIJN_ORDER];
unsigned int idx{0};
unsigned int k{0};
unsigned int duval_symbols{0};
unsigned int duval_length{0};
unsigned int duval_bit{0};
unsigned int duval_sample_bit{0};
unsigned int duval_symbol{0};
size_t scan_progress{0};
uint8_t scan_done{true};
size_t duval_algo_step();
void scan_process(const buffer_c8_t& buffer);
bool scan_init(unsigned int order);
bool scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr);
void write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i);
size_t duval_algo_step();
void scan_process(const buffer_c8_t& buffer);
bool scan_init(unsigned int order);
bool scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr);
void write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i);
};
#endif

View File

@@ -28,88 +28,81 @@
#include <cstdint>
#include <cstddef>
#include <algorithm> // std::max
#include <algorithm> // std::max
#include <cmath>
void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 1500Hz
if (!configured) return;
// Get 24kHz audio
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
auto audio = demod.execute(channel_out, audio_buffer);
smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate
audio_output.write(audio);
processDemodulatedSamples(audio.p, 16);
extractFrames();
// This is called at 1500Hz
if (!configured) return;
// Get 24kHz audio
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
auto audio = demod.execute(channel_out, audio_buffer);
smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate
audio_output.write(audio);
processDemodulatedSamples(audio.p, 16);
extractFrames();
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::OnDataWord(uint32_t word, int pos)
{
packet.set(pos, word);
return 0;
int POCSAGProcessor::OnDataWord(uint32_t word, int pos) {
packet.set(pos, word);
return 0;
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::OnDataFrame(int len, int baud)
{
if (len > 0)
{
packet.set_bitrate(baud);
packet.set_flag(pocsag::PacketFlag::NORMAL);
packet.set_timestamp(Timestamp::now());
const POCSAGPacketMessage message(packet);
shared_memory.application_queue.push(message);
}
return 0;
int POCSAGProcessor::OnDataFrame(int len, int baud) {
if (len > 0) {
packet.set_bitrate(baud);
packet.set_flag(pocsag::PacketFlag::NORMAL);
packet.set_timestamp(Timestamp::now());
const POCSAGPacketMessage message(packet);
shared_memory.application_queue.push(message);
}
return 0;
}
void POCSAGProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::POCSAGConfigure)
configure();
if (message->id == Message::ID::POCSAGConfigure)
configure();
}
void POCSAGProcessor::configure() {
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
const size_t demod_input_fs = channel_filter_output_fs;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(demod_input_fs, 4500);
// Smoothing should be roughly sample rate over max baud
// 24k / 3.2k is 7.5
smooth.SetSize(8);
audio_output.configure(false);
const size_t demod_input_fs = channel_filter_output_fs;
// Set up the frame extraction, limits of baud
setFrameExtractParams(demod_input_fs, 4000, 300, 32);
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(demod_input_fs, 4500);
// Smoothing should be roughly sample rate over max baud
// 24k / 3.2k is 7.5
smooth.SetSize(8);
audio_output.configure(false);
// Mark the class as ready to accept data
configured = true;
// Set up the frame extraction, limits of baud
setFrameExtractParams(demod_input_fs, 4000, 300, 32);
// Mark the class as ready to accept data
configured = true;
}
// -----------------------------
// Frame extractraction methods
// -----------------------------
@@ -118,422 +111,381 @@ void POCSAGProcessor::configure() {
#define MAX_WITHOUT_SINGLE (64)
#define MAX_BAD_TRANS (10)
#define M_SYNC (0x7cd215d8)
#define M_NOTSYNC (0x832dea27)
#define M_SYNC (0x7cd215d8)
#define M_NOTSYNC (0x832dea27)
#define M_IDLE (0x7a89c197)
#define M_IDLE (0x7a89c197)
// ====================================================================
//
// ====================================================================
inline int bitsDiff(unsigned long left, unsigned long right)
{
unsigned long xord = left ^ right;
int count = 0;
for (int i = 0; i < 32; i++)
{
if ((xord & 0x01) != 0) ++count;
xord = xord >> 1;
}
return(count);
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::initFrameExtraction()
{
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
m_badTransitions = 0;
m_bitsStart = 0;
m_bitsEnd = 0;
m_inverted = false;
resetVals();
inline int bitsDiff(unsigned long left, unsigned long right) {
unsigned long xord = left ^ right;
int count = 0;
for (int i = 0; i < 32; i++) {
if ((xord & 0x01) != 0) ++count;
xord = xord >> 1;
}
return (count);
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::resetVals()
{
// Reset the parameters
// --------------------
m_goodTransitions = 0;
m_badTransitions = 0;
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
m_valMid = 0;
void POCSAGProcessor::initFrameExtraction() {
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
// And reset the counts
// --------------------
m_lastTransPos_1024 = 0;
m_lastBitPos_1024 = 0;
m_lastSample = 0;
m_sampleNo = 0;
m_nextBitPos_1024 = m_maxSymSamples_1024;
m_nextBitPosInt = (long)m_nextBitPos_1024;
m_badTransitions = 0;
m_bitsStart = 0;
m_bitsEnd = 0;
m_inverted = false;
// Extraction
m_fifo.numBits = 0;
m_gotSync = false;
m_numCode = 0;
resetVals();
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue)
{
m_samplesPerSec = a_samplesPerSec;
m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud);
m_maxSymSamples_1024 = (uint32_t)(1024.0f*(float)a_samplesPerSec / (float)a_minBaud);
m_maxRunOfSameValue = maxRunOfSameValue;
void POCSAGProcessor::resetVals() {
// Reset the parameters
// --------------------
m_goodTransitions = 0;
m_badTransitions = 0;
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
m_valMid = 0;
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
// And reset the counts
// --------------------
m_lastTransPos_1024 = 0;
m_lastBitPos_1024 = 0;
m_lastSample = 0;
m_sampleNo = 0;
m_nextBitPos_1024 = m_maxSymSamples_1024;
m_nextBitPosInt = (long)m_nextBitPos_1024;
m_nextBitPos_1024 = m_averageSymbolLen_1024 / 2;
m_nextBitPosInt = m_nextBitPos_1024 >> 10;
initFrameExtraction();
// Extraction
m_fifo.numBits = 0;
m_gotSync = false;
m_numCode = 0;
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::processDemodulatedSamples(float * sampleBuff, int noOfSamples)
{
bool transition = false;
uint32_t samplePos_1024 = 0;
uint32_t len_1024 = 0;
void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue) {
m_samplesPerSec = a_samplesPerSec;
m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud);
m_maxSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_minBaud);
m_maxRunOfSameValue = maxRunOfSameValue;
// Loop through the block of data
// ------------------------------
for (int pos = 0; pos < noOfSamples; ++pos)
{
m_sample = sampleBuff[pos];
m_valMid += (m_sample - m_valMid) / 1024.0f;
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
++m_sampleNo;
m_nextBitPos_1024 = m_averageSymbolLen_1024 / 2;
m_nextBitPosInt = m_nextBitPos_1024 >> 10;
// Detect Transition
// -----------------
transition = ! ((m_lastSample < m_valMid) ^ (m_sample >= m_valMid)); // use XOR for speed
// If this is a transition
// -----------------------
if (transition)
{
// Calculate samples since last trans
// ----------------------------------
int32_t fractional_1024 = (int32_t)(((m_sample - m_valMid)*1024) / (m_sample - m_lastSample));
if (fractional_1024 < 0) { fractional_1024 = -fractional_1024; }
samplePos_1024 = (m_sampleNo<<10)-fractional_1024;
len_1024 = samplePos_1024 - m_lastTransPos_1024;
m_lastTransPos_1024 = samplePos_1024;
// If symbol is large enough to be valid
// -------------------------------------
if (len_1024 > m_minSymSamples_1024)
{
// Check for shortest good transition
// ----------------------------------
if ((len_1024 < m_shortestGoodTrans_1024) &&
(m_goodTransitions < BAUD_STABLE)) // detect change of symbol size
{
int32_t fractionOfShortest_1024 = (len_1024<<10) / m_shortestGoodTrans_1024;
// If currently at half the baud rate
// ----------------------------------
if ((fractionOfShortest_1024 > 410) && (fractionOfShortest_1024 < 614)) // 0.4 and 0.6
{
m_averageSymbolLen_1024 /= 2;
m_shortestGoodTrans_1024 = len_1024;
}
// If currently at the wrong baud rate
// -----------------------------------
else if (fractionOfShortest_1024 < 768) // 0.75
{
m_averageSymbolLen_1024 = len_1024;
m_shortestGoodTrans_1024 = len_1024;
m_goodTransitions = 0;
m_lastSingleBitPos_1024 = samplePos_1024 - len_1024;
}
}
// Calc the number of bits since events
// ------------------------------------
int32_t halfSymbol_1024 = m_averageSymbolLen_1024 / 2;
int bitsSinceLastTrans = max((uint32_t)1, (len_1024+halfSymbol_1024) / m_averageSymbolLen_1024 );
int bitsSinceLastSingle = (((m_sampleNo<<10)-m_lastSingleBitPos_1024) + halfSymbol_1024) / m_averageSymbolLen_1024;
// Check for single bit
// --------------------
if (bitsSinceLastTrans == 1)
{
m_lastSingleBitPos_1024 = samplePos_1024;
}
// If too long since last transition
// ---------------------------------
if (bitsSinceLastTrans > MAX_CONSEC_SAME)
{
resetVals();
}
// If too long sice last single bit
// --------------------------------
else if (bitsSinceLastSingle > MAX_WITHOUT_SINGLE)
{
resetVals();
}
else
{
// If this is a good transition
// ----------------------------
int32_t offsetFromExtectedTransition_1024 = len_1024 - (bitsSinceLastTrans*m_averageSymbolLen_1024);
if (offsetFromExtectedTransition_1024 < 0) { offsetFromExtectedTransition_1024 = -offsetFromExtectedTransition_1024; }
if (offsetFromExtectedTransition_1024 < ((int32_t)m_averageSymbolLen_1024 / 4)) // Has to be within 1/4 of symbol to be good
{
++m_goodTransitions;
uint32_t bitsCount = min((uint32_t)BAUD_STABLE, m_goodTransitions);
uint32_t propFromPrevious = m_averageSymbolLen_1024*bitsCount;
uint32_t propFromCurrent = (len_1024 / bitsSinceLastTrans);
m_averageSymbolLen_1024 = (propFromPrevious + propFromCurrent) / (bitsCount + 1);
m_badTransitions = 0;
//if ( len < m_shortestGoodTrans ){m_shortestGoodTrans = len;}
// Store the old symbol size
if (m_goodTransitions >= BAUD_STABLE)
{
m_lastStableSymbolLen_1024 = m_averageSymbolLen_1024;
}
}
}
// Set the point of the last bit if not yet stable
// -----------------------------------------------
if ((m_goodTransitions < BAUD_STABLE) || (m_badTransitions > 0))
{
m_lastBitPos_1024 = samplePos_1024 - (m_averageSymbolLen_1024 / 2);
}
// Calculate the exact positiom of the next bit
// --------------------------------------------
int32_t thisPlusHalfsymbol_1024 = samplePos_1024 + (m_averageSymbolLen_1024/2);
int32_t lastPlusSymbol = m_lastBitPos_1024 + m_averageSymbolLen_1024;
m_nextBitPos_1024 = lastPlusSymbol + ((thisPlusHalfsymbol_1024 - lastPlusSymbol) / 16);
// Check for bad pos error
// -----------------------
if (m_nextBitPos_1024 < samplePos_1024) m_nextBitPos_1024 += m_averageSymbolLen_1024;
// Calculate integer sample after next bit
// ---------------------------------------
m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1;
} // symbol is large enough to be valid
else
{
// Bad transition, so reset the counts
// -----------------------------------
++m_badTransitions;
if (m_badTransitions > MAX_BAD_TRANS)
{
resetVals();
}
}
} // end of if transition
// Reached the point of the next bit
// ---------------------------------
if (m_sampleNo >= m_nextBitPosInt)
{
// Everything is good so extract a bit
// -----------------------------------
if (m_goodTransitions > 20)
{
// Store value at the center of bit
// --------------------------------
storeBit();
}
// Check for long 1 or zero
// ------------------------
uint32_t bitsSinceLastTrans = ((m_sampleNo<<10) - m_lastTransPos_1024) / m_averageSymbolLen_1024;
if (bitsSinceLastTrans > m_maxRunOfSameValue)
{
resetVals();
}
// Store the point of the last bit
// -------------------------------
m_lastBitPos_1024 = m_nextBitPos_1024;
// Calculate the exact point of the next bit
// -----------------------------------------
m_nextBitPos_1024 += m_averageSymbolLen_1024;
// Look for the bit after the next bit pos
// ---------------------------------------
m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1;
} // Reached the point of the next bit
m_lastSample = m_sample;
} // Loop through the block of data
return getNoOfBits();
initFrameExtraction();
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::storeBit()
{
if (++m_bitsStart >= BIT_BUF_SIZE) { m_bitsStart = 0; }
int POCSAGProcessor::processDemodulatedSamples(float* sampleBuff, int noOfSamples) {
bool transition = false;
uint32_t samplePos_1024 = 0;
uint32_t len_1024 = 0;
// Calculate the bit value
float sample = (m_sample + m_lastSample) / 2;
//int32_t sample_1024 = m_sample_1024;
bool bit = sample > m_valMid;
// Loop through the block of data
// ------------------------------
for (int pos = 0; pos < noOfSamples; ++pos) {
m_sample = sampleBuff[pos];
m_valMid += (m_sample - m_valMid) / 1024.0f;
// If buffer not full
if (m_bitsStart != m_bitsEnd)
{
// Decide on output val
if (bit)
{
m_bits[m_bitsStart] = 0;
}
else
{
m_bits[m_bitsStart] = 1;
}
}
// Throw away bits if the buffer is full
else
{
if (--m_bitsStart <= -1)
{
m_bitsStart = BIT_BUF_SIZE - 1;
}
}
++m_sampleNo;
// Detect Transition
// -----------------
transition = !((m_lastSample < m_valMid) ^ (m_sample >= m_valMid)); // use XOR for speed
// If this is a transition
// -----------------------
if (transition) {
// Calculate samples since last trans
// ----------------------------------
int32_t fractional_1024 = (int32_t)(((m_sample - m_valMid) * 1024) / (m_sample - m_lastSample));
if (fractional_1024 < 0) {
fractional_1024 = -fractional_1024;
}
samplePos_1024 = (m_sampleNo << 10) - fractional_1024;
len_1024 = samplePos_1024 - m_lastTransPos_1024;
m_lastTransPos_1024 = samplePos_1024;
// If symbol is large enough to be valid
// -------------------------------------
if (len_1024 > m_minSymSamples_1024) {
// Check for shortest good transition
// ----------------------------------
if ((len_1024 < m_shortestGoodTrans_1024) &&
(m_goodTransitions < BAUD_STABLE)) // detect change of symbol size
{
int32_t fractionOfShortest_1024 = (len_1024 << 10) / m_shortestGoodTrans_1024;
// If currently at half the baud rate
// ----------------------------------
if ((fractionOfShortest_1024 > 410) && (fractionOfShortest_1024 < 614)) // 0.4 and 0.6
{
m_averageSymbolLen_1024 /= 2;
m_shortestGoodTrans_1024 = len_1024;
}
// If currently at the wrong baud rate
// -----------------------------------
else if (fractionOfShortest_1024 < 768) // 0.75
{
m_averageSymbolLen_1024 = len_1024;
m_shortestGoodTrans_1024 = len_1024;
m_goodTransitions = 0;
m_lastSingleBitPos_1024 = samplePos_1024 - len_1024;
}
}
// Calc the number of bits since events
// ------------------------------------
int32_t halfSymbol_1024 = m_averageSymbolLen_1024 / 2;
int bitsSinceLastTrans = max((uint32_t)1, (len_1024 + halfSymbol_1024) / m_averageSymbolLen_1024);
int bitsSinceLastSingle = (((m_sampleNo << 10) - m_lastSingleBitPos_1024) + halfSymbol_1024) / m_averageSymbolLen_1024;
// Check for single bit
// --------------------
if (bitsSinceLastTrans == 1) {
m_lastSingleBitPos_1024 = samplePos_1024;
}
// If too long since last transition
// ---------------------------------
if (bitsSinceLastTrans > MAX_CONSEC_SAME) {
resetVals();
}
// If too long sice last single bit
// --------------------------------
else if (bitsSinceLastSingle > MAX_WITHOUT_SINGLE) {
resetVals();
} else {
// If this is a good transition
// ----------------------------
int32_t offsetFromExtectedTransition_1024 = len_1024 - (bitsSinceLastTrans * m_averageSymbolLen_1024);
if (offsetFromExtectedTransition_1024 < 0) {
offsetFromExtectedTransition_1024 = -offsetFromExtectedTransition_1024;
}
if (offsetFromExtectedTransition_1024 < ((int32_t)m_averageSymbolLen_1024 / 4)) // Has to be within 1/4 of symbol to be good
{
++m_goodTransitions;
uint32_t bitsCount = min((uint32_t)BAUD_STABLE, m_goodTransitions);
uint32_t propFromPrevious = m_averageSymbolLen_1024 * bitsCount;
uint32_t propFromCurrent = (len_1024 / bitsSinceLastTrans);
m_averageSymbolLen_1024 = (propFromPrevious + propFromCurrent) / (bitsCount + 1);
m_badTransitions = 0;
// if ( len < m_shortestGoodTrans ){m_shortestGoodTrans = len;}
// Store the old symbol size
if (m_goodTransitions >= BAUD_STABLE) {
m_lastStableSymbolLen_1024 = m_averageSymbolLen_1024;
}
}
}
// Set the point of the last bit if not yet stable
// -----------------------------------------------
if ((m_goodTransitions < BAUD_STABLE) || (m_badTransitions > 0)) {
m_lastBitPos_1024 = samplePos_1024 - (m_averageSymbolLen_1024 / 2);
}
// Calculate the exact positiom of the next bit
// --------------------------------------------
int32_t thisPlusHalfsymbol_1024 = samplePos_1024 + (m_averageSymbolLen_1024 / 2);
int32_t lastPlusSymbol = m_lastBitPos_1024 + m_averageSymbolLen_1024;
m_nextBitPos_1024 = lastPlusSymbol + ((thisPlusHalfsymbol_1024 - lastPlusSymbol) / 16);
// Check for bad pos error
// -----------------------
if (m_nextBitPos_1024 < samplePos_1024) m_nextBitPos_1024 += m_averageSymbolLen_1024;
// Calculate integer sample after next bit
// ---------------------------------------
m_nextBitPosInt = (m_nextBitPos_1024 >> 10) + 1;
} // symbol is large enough to be valid
else {
// Bad transition, so reset the counts
// -----------------------------------
++m_badTransitions;
if (m_badTransitions > MAX_BAD_TRANS) {
resetVals();
}
}
} // end of if transition
// Reached the point of the next bit
// ---------------------------------
if (m_sampleNo >= m_nextBitPosInt) {
// Everything is good so extract a bit
// -----------------------------------
if (m_goodTransitions > 20) {
// Store value at the center of bit
// --------------------------------
storeBit();
}
// Check for long 1 or zero
// ------------------------
uint32_t bitsSinceLastTrans = ((m_sampleNo << 10) - m_lastTransPos_1024) / m_averageSymbolLen_1024;
if (bitsSinceLastTrans > m_maxRunOfSameValue) {
resetVals();
}
// Store the point of the last bit
// -------------------------------
m_lastBitPos_1024 = m_nextBitPos_1024;
// Calculate the exact point of the next bit
// -----------------------------------------
m_nextBitPos_1024 += m_averageSymbolLen_1024;
// Look for the bit after the next bit pos
// ---------------------------------------
m_nextBitPosInt = (m_nextBitPos_1024 >> 10) + 1;
} // Reached the point of the next bit
m_lastSample = m_sample;
} // Loop through the block of data
return getNoOfBits();
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::extractFrames()
{
int msgCnt = 0;
// While there is unread data in the bits buffer
//----------------------------------------------
while (getNoOfBits() > 0)
{
m_fifo.codeword = (m_fifo.codeword << 1) + getBit();
m_fifo.numBits++;
void POCSAGProcessor::storeBit() {
if (++m_bitsStart >= BIT_BUF_SIZE) {
m_bitsStart = 0;
}
// If number of bits in fifo equals 32
//------------------------------------
if (m_fifo.numBits >= 32)
{
// Not got sync
// ------------
if (!m_gotSync)
{
if (bitsDiff(m_fifo.codeword, M_SYNC) <= 2)
{
m_inverted = false;
m_gotSync = true;
m_numCode = -1;
m_fifo.numBits = 0;
}
else if (bitsDiff(m_fifo.codeword, M_NOTSYNC) <= 2)
{
m_inverted = true;
m_gotSync = true;
m_numCode = -1;
m_fifo.numBits = 0;
}
else
{
// Cause it to load one more bit
m_fifo.numBits = 31;
}
} // Not got sync
else
{
// Increment the word count
// ------------------------
++m_numCode; // It got set to -1 when a sync was found, now count the 16 words
uint32_t val = m_inverted ? ~m_fifo.codeword : m_fifo.codeword;
OnDataWord(val, m_numCode);
// Calculate the bit value
float sample = (m_sample + m_lastSample) / 2;
// int32_t sample_1024 = m_sample_1024;
bool bit = sample > m_valMid;
// If at the end of a 16 word block
// --------------------------------
if (m_numCode >= 15)
{
msgCnt += OnDataFrame(m_numCode+1, (m_samplesPerSec<<10) / m_lastStableSymbolLen_1024);
m_gotSync = false;
m_numCode = -1;
}
m_fifo.numBits = 0;
}
} // If number of bits in fifo equals 32
} // While there is unread data in the bits buffer
return msgCnt;
} // extractFrames
// ====================================================================
//
// ====================================================================
short POCSAGProcessor::getBit()
{
if (m_bitsEnd != m_bitsStart)
{
if (++m_bitsEnd >= BIT_BUF_SIZE)
{
m_bitsEnd = 0;
}
return m_bits[m_bitsEnd];
}
else
{
return -1;
}
// If buffer not full
if (m_bitsStart != m_bitsEnd) {
// Decide on output val
if (bit) {
m_bits[m_bitsStart] = 0;
} else {
m_bits[m_bitsStart] = 1;
}
}
// Throw away bits if the buffer is full
else {
if (--m_bitsStart <= -1) {
m_bitsStart = BIT_BUF_SIZE - 1;
}
}
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::getNoOfBits()
{
int bits = m_bitsEnd - m_bitsStart;
if (bits < 0) { bits += BIT_BUF_SIZE; }
return bits;
int POCSAGProcessor::extractFrames() {
int msgCnt = 0;
// While there is unread data in the bits buffer
//----------------------------------------------
while (getNoOfBits() > 0) {
m_fifo.codeword = (m_fifo.codeword << 1) + getBit();
m_fifo.numBits++;
// If number of bits in fifo equals 32
//------------------------------------
if (m_fifo.numBits >= 32) {
// Not got sync
// ------------
if (!m_gotSync) {
if (bitsDiff(m_fifo.codeword, M_SYNC) <= 2) {
m_inverted = false;
m_gotSync = true;
m_numCode = -1;
m_fifo.numBits = 0;
} else if (bitsDiff(m_fifo.codeword, M_NOTSYNC) <= 2) {
m_inverted = true;
m_gotSync = true;
m_numCode = -1;
m_fifo.numBits = 0;
} else {
// Cause it to load one more bit
m_fifo.numBits = 31;
}
} // Not got sync
else {
// Increment the word count
// ------------------------
++m_numCode; // It got set to -1 when a sync was found, now count the 16 words
uint32_t val = m_inverted ? ~m_fifo.codeword : m_fifo.codeword;
OnDataWord(val, m_numCode);
// If at the end of a 16 word block
// --------------------------------
if (m_numCode >= 15) {
msgCnt += OnDataFrame(m_numCode + 1, (m_samplesPerSec << 10) / m_lastStableSymbolLen_1024);
m_gotSync = false;
m_numCode = -1;
}
m_fifo.numBits = 0;
}
} // If number of bits in fifo equals 32
} // While there is unread data in the bits buffer
return msgCnt;
} // extractFrames
// ====================================================================
//
// ====================================================================
short POCSAGProcessor::getBit() {
if (m_bitsEnd != m_bitsStart) {
if (++m_bitsEnd >= BIT_BUF_SIZE) {
m_bitsEnd = 0;
}
return m_bits[m_bitsEnd];
} else {
return -1;
}
}
// ====================================================================
//
// ====================================================================
uint32_t POCSAGProcessor::getRate()
{
return ((m_samplesPerSec<<10)+512) / m_lastStableSymbolLen_1024;
int POCSAGProcessor::getNoOfBits() {
int bits = m_bitsEnd - m_bitsStart;
if (bits < 0) {
bits += BIT_BUF_SIZE;
}
return bits;
}
// ====================================================================
//
// ====================================================================
uint32_t POCSAGProcessor::getRate() {
return ((m_samplesPerSec << 10) + 512) / m_lastStableSymbolLen_1024;
}
// ====================================================================
//
// ====================================================================
int main() {
EventDispatcher event_dispatcher { std::make_unique<POCSAGProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<POCSAGProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@@ -40,196 +40,185 @@
#include "portapack_shared_memory.hpp"
#include <cstdint>
#include <bitset>
#include <bitset>
using namespace std;
// Class used to smooth demodulated waveform prior to decoding
// -----------------------------------------------------------
template <class ValType, class CalcType>
class SmoothVals
{
protected:
ValType * m_lastVals; // Previoius N values
int m_size; // The size N
CalcType m_sumVal; // Running sum of lastVals
int m_pos; // Current position in last vals ring buffer
int m_count; //
public:
SmoothVals() : m_lastVals(NULL), m_size(1), m_sumVal(0), m_pos(0), m_count(0)
{
m_lastVals = new ValType[m_size];
}
// --------------------------------------------------
// --------------------------------------------------
virtual ~SmoothVals()
{
delete[] m_lastVals;
}
SmoothVals(const SmoothVals<float, float>&)
{
class SmoothVals {
protected:
ValType* m_lastVals; // Previoius N values
int m_size; // The size N
CalcType m_sumVal; // Running sum of lastVals
int m_pos; // Current position in last vals ring buffer
int m_count; //
public:
SmoothVals()
: m_lastVals(NULL), m_size(1), m_sumVal(0), m_pos(0), m_count(0) {
m_lastVals = new ValType[m_size];
}
SmoothVals & operator=(const SmoothVals<float, float>&)
{
return *this ;
// --------------------------------------------------
// --------------------------------------------------
virtual ~SmoothVals() {
delete[] m_lastVals;
}
// --------------------------------------------------
// Set size of smoothing
// --------------------------------------------------
void SetSize(int size)
{
m_size = std::max(size, 1);
m_pos = 0;
delete[] m_lastVals;
m_lastVals = new ValType[m_size];
m_sumVal = 0;
}
SmoothVals(const SmoothVals<float, float>&) {
}
// --------------------------------------------------
// Get size of smoothing
// --------------------------------------------------
int Size() { return m_size; }
SmoothVals& operator=(const SmoothVals<float, float>&) {
return *this;
}
// --------------------------------------------------
// In place processing
// --------------------------------------------------
void Process(ValType * valBuff, int numVals)
{
ValType tmpVal;
// --------------------------------------------------
// Set size of smoothing
// --------------------------------------------------
void SetSize(int size) {
m_size = std::max(size, 1);
m_pos = 0;
delete[] m_lastVals;
m_lastVals = new ValType[m_size];
m_sumVal = 0;
}
if (m_count > (1024*10))
{
// Recalculate the sum value occasionaly, stops accumulated errors when using float
m_count = 0;
m_sumVal = 0;
for (int i = 0; i < m_size; ++i) { m_sumVal += (CalcType)m_lastVals[i]; }
}
// --------------------------------------------------
// Get size of smoothing
// --------------------------------------------------
int Size() { return m_size; }
// Use a rolling smoothed value while processing the buffer
for (int buffPos = 0; buffPos < numVals; ++buffPos)
{
m_pos = (m_pos + 1); // Increment the position in the stored values
if (m_pos >= m_size) { m_pos = 0; } // loop if reached the end of the stored values
// --------------------------------------------------
// In place processing
// --------------------------------------------------
void Process(ValType* valBuff, int numVals) {
ValType tmpVal;
m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value
m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value
m_sumVal += (CalcType)m_lastVals[m_pos]; // Add on the new value
if (m_count > (1024 * 10)) {
// Recalculate the sum value occasionaly, stops accumulated errors when using float
m_count = 0;
m_sumVal = 0;
for (int i = 0; i < m_size; ++i) {
m_sumVal += (CalcType)m_lastVals[i];
}
}
tmpVal = (ValType)(m_sumVal / m_size); // Scale by number of values smoothed
valBuff[buffPos] = tmpVal;
}
// Use a rolling smoothed value while processing the buffer
for (int buffPos = 0; buffPos < numVals; ++buffPos) {
m_pos = (m_pos + 1); // Increment the position in the stored values
if (m_pos >= m_size) {
m_pos = 0;
} // loop if reached the end of the stored values
m_count += numVals;
}
m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value
m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value
m_sumVal += (CalcType)m_lastVals[m_pos]; // Add on the new value
tmpVal = (ValType)(m_sumVal / m_size); // Scale by number of values smoothed
valBuff[buffPos] = tmpVal;
}
m_count += numVals;
}
};
// --------------------------------------------------
// Class to process base band data to pocsag frames
// --------------------------------------------------
class POCSAGProcessor : public BasebandProcessor{
public:
class POCSAGProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
void on_message(const Message* const message) override;
int OnDataFrame(int len, int baud);
int OnDataWord(uint32_t word, int pos);
int OnDataFrame(int len, int baud);
int OnDataWord(uint32_t word, int pos);
private:
static constexpr size_t baseband_fs = 3072000;
private:
static constexpr size_t baseband_fs = 3072000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<float, 32> audio { };
const buffer_f32_t audio_buffer {
audio.data(),
audio.size()
};
BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive};
RSSIThread rssi_thread{NORMALPRIO + 10};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
dsp::demodulate::FM demod { };
SmoothVals<float, float> smooth = { };
AudioOutput audio_output { };
std::array<complex16_t, 512> dst{};
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
std::array<float, 32> audio{};
const buffer_f32_t audio_buffer{
audio.data(),
audio.size()};
bool configured = false;
pocsag::POCSAGPacket packet { };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
dsp::decimate::FIRAndDecimateComplex channel_filter{};
dsp::demodulate::FM demod{};
SmoothVals<float, float> smooth = {};
void configure();
AudioOutput audio_output{};
// ----------------------------------------
// Frame extractraction methods and members
// ----------------------------------------
private:
void initFrameExtraction();
struct FIFOStruct {
unsigned long codeword;
int numBits;
};
bool configured = false;
pocsag::POCSAGPacket packet{};
#define BIT_BUF_SIZE (64)
void configure();
void resetVals();
void setFrameExtractParams(long a_samplesPerSec, long a_maxBaud = 8000, long a_minBaud = 200, long maxRunOfSameValue = 32);
// ----------------------------------------
// Frame extractraction methods and members
// ----------------------------------------
private:
void initFrameExtraction();
struct FIFOStruct {
unsigned long codeword;
int numBits;
};
int processDemodulatedSamples(float * sampleBuff, int noOfSamples);
int extractFrames();
#define BIT_BUF_SIZE (64)
void storeBit();
short getBit();
void resetVals();
void setFrameExtractParams(long a_samplesPerSec, long a_maxBaud = 8000, long a_minBaud = 200, long maxRunOfSameValue = 32);
int getNoOfBits();
uint32_t getRate();
int processDemodulatedSamples(float* sampleBuff, int noOfSamples);
int extractFrames();
uint32_t m_averageSymbolLen_1024{0};
uint32_t m_lastStableSymbolLen_1024{0};
void storeBit();
short getBit();
uint32_t m_samplesPerSec{0};
uint32_t m_goodTransitions{0};
uint32_t m_badTransitions{0};
int getNoOfBits();
uint32_t getRate();
uint32_t m_sampleNo{0};
float m_sample{0};
float m_valMid{0.0f};
float m_lastSample{0.0f};
uint32_t m_averageSymbolLen_1024{0};
uint32_t m_lastStableSymbolLen_1024{0};
uint32_t m_lastTransPos_1024{0};
uint32_t m_lastSingleBitPos_1024{0};
uint32_t m_samplesPerSec{0};
uint32_t m_goodTransitions{0};
uint32_t m_badTransitions{0};
uint32_t m_nextBitPosInt{0}; // Integer rounded up version to save on ops
uint32_t m_nextBitPos_1024{0};
uint32_t m_lastBitPos_1024{0};
uint32_t m_sampleNo{0};
float m_sample{0};
float m_valMid{0.0f};
float m_lastSample{0.0f};
uint32_t m_shortestGoodTrans_1024{0};
uint32_t m_minSymSamples_1024{0};
uint32_t m_maxSymSamples_1024{0};
uint32_t m_maxRunOfSameValue{0};
uint32_t m_lastTransPos_1024{0};
uint32_t m_lastSingleBitPos_1024{0};
bitset<(size_t)BIT_BUF_SIZE> m_bits{0};
long m_bitsStart{0};
long m_bitsEnd{0};
uint32_t m_nextBitPosInt{0}; // Integer rounded up version to save on ops
uint32_t m_nextBitPos_1024{0};
uint32_t m_lastBitPos_1024{0};
FIFOStruct m_fifo{0,0};
bool m_gotSync{false};
int m_numCode{0};
bool m_inverted{false};
uint32_t m_shortestGoodTrans_1024{0};
uint32_t m_minSymSamples_1024{0};
uint32_t m_maxSymSamples_1024{0};
uint32_t m_maxRunOfSameValue{0};
bitset<(size_t)BIT_BUF_SIZE> m_bits{0};
long m_bitsStart{0};
long m_bitsEnd{0};
FIFOStruct m_fifo{0, 0};
bool m_gotSync{false};
int m_numCode{0};
bool m_inverted{false};
};
#endif/*__PROC_POCSAG_H__*/
#endif /*__PROC_POCSAG_H__*/

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
@@ -28,84 +28,86 @@
#include <cstdint>
void RDSProcessor::execute(const buffer_c8_t& buffer) {
for (size_t i = 0; i < buffer.count; i++) {
// Sample generation at 2.28M / 10 = 228kHz
if (s >= 9) {
s = 0;
if (sample_count >= SAMPLES_PER_BIT) {
if (bit_pos >= message_length) {
bit_pos = 0;
cur_output = 0;
}
cur_bit = (rdsdata[(bit_pos / 26) & 127] >> (25 - (bit_pos % 26))) & 1;
prev_output = cur_output;
cur_output = prev_output ^ cur_bit;
for (size_t i = 0; i < buffer.count; i++) {
// Sample generation at 2.28M / 10 = 228kHz
if (s >= 9) {
s = 0;
if (sample_count >= SAMPLES_PER_BIT) {
if (bit_pos >= message_length) {
bit_pos = 0;
cur_output = 0;
}
const int32_t * src = waveform_biphase;
int idx = in_sample_index;
cur_bit = (rdsdata[(bit_pos / 26) & 127] >> (25 - (bit_pos % 26))) & 1;
prev_output = cur_output;
cur_output = prev_output ^ cur_bit;
for (int j = 0; j < FILTER_SIZE; j++) {
val = (*src++);
if (cur_output) val = -val;
sample_buffer[idx++] += val;
if (idx >= SAMPLE_BUFFER_SIZE) idx = 0;
}
const int32_t* src = waveform_biphase;
int idx = in_sample_index;
in_sample_index += SAMPLES_PER_BIT;
if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE;
bit_pos++;
sample_count = 0;
}
sample = sample_buffer[out_sample_index];
sample_buffer[out_sample_index] = 0;
out_sample_index++;
if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0;
// AM @ 228k/4 = 57kHz
// 0, sample, 0, -sample...
switch (mphase & 3) {
case 0:
case 2: sample = 0; break;
case 1: break;
case 3: sample = -sample; // break;
}
mphase++;
sample_count++;
} else {
s++;
}
// FM
delta = (sample >> 16) * 386760; // ?
phase += delta;
sphase = phase + (64 << 18);
re = (sine_table_i8[(sphase & 0x03FF0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FF0000) >> 18]);
buffer.p[i] = {re, im};
}
for (int j = 0; j < FILTER_SIZE; j++) {
val = (*src++);
if (cur_output) val = -val;
sample_buffer[idx++] += val;
if (idx >= SAMPLE_BUFFER_SIZE) idx = 0;
}
in_sample_index += SAMPLES_PER_BIT;
if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE;
bit_pos++;
sample_count = 0;
}
sample = sample_buffer[out_sample_index];
sample_buffer[out_sample_index] = 0;
out_sample_index++;
if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0;
// AM @ 228k/4 = 57kHz
// 0, sample, 0, -sample...
switch (mphase & 3) {
case 0:
case 2:
sample = 0;
break;
case 1:
break;
case 3:
sample = -sample; // break;
}
mphase++;
sample_count++;
} else {
s++;
}
// FM
delta = (sample >> 16) * 386760; // ?
phase += delta;
sphase = phase + (64 << 18);
re = (sine_table_i8[(sphase & 0x03FF0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FF0000) >> 18]);
buffer.p[i] = {re, im};
}
}
void RDSProcessor::on_message(const Message* const msg) {
if (msg->id == Message::ID::RDSConfigure) {
const auto message = *reinterpret_cast<const RDSConfigureMessage*>(msg);
rdsdata = (uint32_t*)shared_memory.bb_data.data;
message_length = message.length;
configured = true;
}
if (msg->id == Message::ID::RDSConfigure) {
const auto message = *reinterpret_cast<const RDSConfigureMessage*>(msg);
rdsdata = (uint32_t*)shared_memory.bb_data.data;
message_length = message.length;
configured = true;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<RDSProcessor>() };
event_dispatcher.run();
return 0;
EventDispatcher event_dispatcher{std::make_unique<RDSProcessor>()};
event_dispatcher.run();
return 0;
}

Some files were not shown because too many files have changed in this diff Show More