mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-12 01:29:29 +00:00
Merge fixing, commit to catch up on recent files
This commit is contained in:
@@ -124,16 +124,20 @@ CSRC = $(PORTSRC) \
|
||||
# setting.
|
||||
CPPSRC = main.cpp \
|
||||
message_queue.cpp \
|
||||
event.cpp \
|
||||
event_m4.cpp \
|
||||
irq_ipc_m4.cpp \
|
||||
gpdma.cpp \
|
||||
baseband_dma.cpp \
|
||||
baseband_sgpio.cpp \
|
||||
portapack_shared_memory.cpp \
|
||||
baseband_thread.cpp \
|
||||
baseband_processor.cpp \
|
||||
channel_decimator.cpp \
|
||||
baseband_stats_collector.cpp \
|
||||
dsp_decimate.cpp \
|
||||
dsp_demodulate.cpp \
|
||||
matched_filter.cpp \
|
||||
spectrum_collector.cpp \
|
||||
proc_rds.cpp \
|
||||
proc_jammer.cpp \
|
||||
proc_fsk_lcr.cpp \
|
||||
proc_xylos.cpp \
|
||||
@@ -144,11 +148,15 @@ CPPSRC = main.cpp \
|
||||
packet_builder.cpp \
|
||||
dsp_fft.cpp \
|
||||
dsp_fir_taps.cpp \
|
||||
dsp_iir.cpp \
|
||||
fxpt_atan2.cpp \
|
||||
rssi.cpp \
|
||||
rssi_dma.cpp \
|
||||
rssi_thread.cpp \
|
||||
audio.cpp \
|
||||
audio_output.cpp \
|
||||
audio_dma.cpp \
|
||||
audio_stats_collector.cpp \
|
||||
touch_dma.cpp \
|
||||
../common/utility.cpp \
|
||||
../common/chibios_cpp.cpp \
|
||||
|
||||
@@ -121,7 +121,7 @@ constexpr gpdma::channel::Config config_rx() {
|
||||
|
||||
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
|
||||
|
||||
constexpr size_t buffer_samples_log2n = 8; // Bumped to 8, to allow filling at 750Hz
|
||||
constexpr size_t buffer_samples_log2n = 7;
|
||||
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
|
||||
constexpr size_t transfers_per_buffer_log2n = 2;
|
||||
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
|
||||
@@ -208,8 +208,8 @@ void enable() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_i2s0_tx.disable_force();
|
||||
gpdma_channel_i2s0_rx.disable_force();
|
||||
gpdma_channel_i2s0_tx.disable();
|
||||
gpdma_channel_i2s0_rx.disable();
|
||||
}
|
||||
|
||||
buffer_t tx_empty_buffer() {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
write(buffer_f32_t {
|
||||
audio_f.data(),
|
||||
audio.count,
|
||||
audio.sampling_rate
|
||||
});
|
||||
}
|
||||
|
||||
void AudioOutput::write(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
|
||||
hpf.execute_in_place(audio);
|
||||
deemph.execute_in_place(audio);
|
||||
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
const bool audio_present = (audio_present_history != 0);
|
||||
|
||||
if( audio_present ) {
|
||||
i2s::i2s0::tx_unmute();
|
||||
} else {
|
||||
i2s::i2s0::tx_mute();
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fill_audio_buffer(audio);
|
||||
}
|
||||
|
||||
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio) {
|
||||
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];
|
||||
const int32_t sample_saturated = __SSAT(sample_int, 16);
|
||||
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_OUTPUT_H__
|
||||
#define __AUDIO_OUTPUT_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class AudioOutput {
|
||||
public:
|
||||
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 write(const buffer_s16_t& audio);
|
||||
void write(const buffer_f32_t& audio);
|
||||
|
||||
private:
|
||||
IIRBiquadFilter hpf;
|
||||
IIRBiquadFilter deemph;
|
||||
FMSquelch squelch;
|
||||
|
||||
AudioStatsCollector audio_stats;
|
||||
|
||||
uint64_t audio_present_history = 0;
|
||||
|
||||
void fill_audio_buffer(const buffer_f32_t& audio);
|
||||
void feed_audio_stats(const buffer_f32_t& audio);
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_OUTPUT_H__*/
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t sampling_rate) {
|
||||
count += sample_count;
|
||||
|
||||
const size_t samples_per_update = sampling_rate * update_interval;
|
||||
|
||||
if( count >= samples_per_update ) {
|
||||
statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_sum / count);
|
||||
statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared);
|
||||
statistics.count = count;
|
||||
|
||||
squared_sum = 0;
|
||||
max_squared = 0;
|
||||
count = 0;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioStatsCollector::feed(const buffer_f32_t& src) {
|
||||
consume_audio_buffer(src);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -22,9 +22,8 @@
|
||||
#ifndef __AUDIO_STATS_COLLECTOR_H__
|
||||
#define __AUDIO_STATS_COLLECTOR_H__
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@@ -32,64 +31,33 @@
|
||||
class AudioStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
void feed(buffer_s16_t src, Callback callback) {
|
||||
consume_audio_buffer(src);
|
||||
|
||||
if( update_stats(src.count, src.sampling_rate) ) {
|
||||
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( update_stats(sample_count, sampling_rate) ) {
|
||||
if( mute(sample_count, sampling_rate) ) {
|
||||
callback(statistics);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float update_interval { 0.1f };
|
||||
uint64_t squared_sum { 0 };
|
||||
uint32_t max_squared { 0 };
|
||||
float squared_sum { 0 };
|
||||
float max_squared { 0 };
|
||||
size_t count { 0 };
|
||||
|
||||
AudioStatistics statistics;
|
||||
|
||||
void consume_audio_buffer(buffer_s16_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 uint64_t sample_squared = sample * sample;
|
||||
squared_sum += sample_squared;
|
||||
if( sample_squared > max_squared ) {
|
||||
max_squared = sample_squared;
|
||||
}
|
||||
}
|
||||
}
|
||||
void consume_audio_buffer(const buffer_f32_t& src);
|
||||
|
||||
bool update_stats(const size_t sample_count, const size_t sampling_rate) {
|
||||
count += sample_count;
|
||||
bool update_stats(const size_t sample_count, const size_t sampling_rate);
|
||||
|
||||
const size_t samples_per_update = sampling_rate * update_interval;
|
||||
|
||||
if( count >= samples_per_update ) {
|
||||
const float squared_sum_f = squared_sum;
|
||||
const float max_squared_f = max_squared;
|
||||
const float squared_avg_f = squared_sum_f / count;
|
||||
statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_avg_f);
|
||||
statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared_f);
|
||||
statistics.count = count;
|
||||
|
||||
squared_sum = 0;
|
||||
max_squared = 0;
|
||||
count = 0;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool feed(const buffer_f32_t& src);
|
||||
bool mute(const size_t sample_count, const size_t sampling_rate);
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_STATS_COLLECTOR_H__*/
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#include "baseband_dma.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@@ -35,8 +34,6 @@ using namespace lpc43xx;
|
||||
|
||||
namespace baseband {
|
||||
namespace dma {
|
||||
|
||||
int quitt = 0;
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_sgpio = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 1;
|
||||
@@ -102,17 +99,12 @@ constexpr size_t msg_count = transfers_per_buffer - 1;
|
||||
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_loop;
|
||||
static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number];
|
||||
|
||||
//static Mailbox mailbox;
|
||||
//static std::array<msg_t, msg_count> messages;
|
||||
static Semaphore semaphore;
|
||||
|
||||
static volatile const gpdma::channel::LLI* next_lli = nullptr;
|
||||
|
||||
void transfer_complete() {
|
||||
static void transfer_complete() {
|
||||
next_lli = gpdma_channel_sgpio.next_lli();
|
||||
quitt = 0;
|
||||
/* TODO: Is Mailbox the proper synchronization mechanism for this? */
|
||||
//chMBPostI(&mailbox, 0);
|
||||
chSemSignalI(&semaphore);
|
||||
}
|
||||
|
||||
@@ -121,7 +113,6 @@ static void dma_error() {
|
||||
}
|
||||
|
||||
void init() {
|
||||
//chMBInit(&mailbox, messages.data(), messages.size());
|
||||
chSemInit(&semaphore, 0);
|
||||
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
|
||||
|
||||
@@ -148,7 +139,6 @@ void enable(const baseband::Direction direction) {
|
||||
const auto gpdma_config = config(direction);
|
||||
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
|
||||
|
||||
//chMBReset(&mailbox);
|
||||
chSemReset(&semaphore, 0);
|
||||
|
||||
gpdma_channel_sgpio.enable();
|
||||
@@ -159,14 +149,11 @@ bool is_enabled() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_sgpio.disable_force();
|
||||
gpdma_channel_sgpio.disable();
|
||||
}
|
||||
|
||||
baseband::buffer_t wait_for_rx_buffer() {
|
||||
//msg_t msg;
|
||||
//const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE);
|
||||
const auto status = chSemWait(&semaphore);
|
||||
if (quitt) return { nullptr, 0 };
|
||||
if( status == RDY_OK ) {
|
||||
const auto next = next_lli;
|
||||
if( next ) {
|
||||
@@ -174,28 +161,10 @@ baseband::buffer_t wait_for_rx_buffer() {
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
return { reinterpret_cast<sample_t*>(lli_loop[free_index].destaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
return { };
|
||||
}
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
baseband::buffer_t wait_for_tx_buffer() {
|
||||
//msg_t msg;
|
||||
//const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE);
|
||||
const auto status = chSemWait(&semaphore);
|
||||
if( status == RDY_OK ) {
|
||||
const auto next = next_lli;
|
||||
if( next ) {
|
||||
const size_t next_index = next - &lli_loop[0];
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
return { reinterpret_cast<sample_t*>(lli_loop[free_index].srcaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,104 +23,14 @@
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "dsp_fft.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
|
||||
void BasebandProcessor::update_spectrum() {
|
||||
// Called from idle thread (after EVT_MASK_SPECTRUM is flagged)
|
||||
if( channel_spectrum_request_update ) {
|
||||
/* Decimated buffer is full. Compute spectrum. */
|
||||
channel_spectrum_request_update = false;
|
||||
fft_c_preswapped(channel_spectrum);
|
||||
|
||||
ChannelSpectrumMessage spectrum_message;
|
||||
for(size_t i=0; i<spectrum_message.spectrum.db.size(); i++) {
|
||||
const auto mag2 = magnitude_squared(channel_spectrum[i]);
|
||||
const float db = complex16_mag_squared_to_dbv_norm(mag2);
|
||||
constexpr float mag_scale = 5.0f;
|
||||
const unsigned int v = (db * mag_scale) + 255.0f;
|
||||
spectrum_message.spectrum.db[i] = std::max(0U, std::min(255U, v));
|
||||
}
|
||||
|
||||
/* TODO: Rename .db -> .magnitude, or something more (less!) accurate. */
|
||||
spectrum_message.spectrum.db_count = spectrum_message.spectrum.db.size();
|
||||
spectrum_message.spectrum.sampling_rate = channel_spectrum_sampling_rate;
|
||||
spectrum_message.spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency;
|
||||
spectrum_message.spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency;
|
||||
shared_memory.application_queue.push(spectrum_message);
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandProcessor::feed_channel_stats(const buffer_c16_t channel) {
|
||||
void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) {
|
||||
channel_stats.feed(
|
||||
channel,
|
||||
[this](const ChannelStatistics statistics) {
|
||||
this->post_channel_stats_message(statistics);
|
||||
[](const ChannelStatistics& statistics) {
|
||||
const ChannelStatisticsMessage channel_stats_message { statistics };
|
||||
shared_memory.application_queue.push(channel_stats_message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void BasebandProcessor::feed_channel_spectrum(
|
||||
const buffer_c16_t channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
) {
|
||||
channel_filter_pass_frequency = filter_pass_frequency;
|
||||
channel_filter_stop_frequency = filter_stop_frequency;
|
||||
channel_spectrum_decimator.feed(
|
||||
channel,
|
||||
[this](const buffer_c16_t data) {
|
||||
this->post_channel_spectrum_message(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void BasebandProcessor::fill_audio_buffer(const buffer_s16_t audio) {
|
||||
auto audio_buffer = audio::dma::tx_empty_buffer();;
|
||||
for(size_t i=0; i<audio_buffer.count; i++) {
|
||||
audio_buffer.p[i].left = audio_buffer.p[i].right = audio.p[i];
|
||||
}
|
||||
i2s::i2s0::tx_unmute();
|
||||
|
||||
feed_audio_stats(audio);
|
||||
}
|
||||
|
||||
void BasebandProcessor::post_channel_stats_message(const ChannelStatistics statistics) {
|
||||
channel_stats_message.statistics = statistics;
|
||||
shared_memory.application_queue.push(channel_stats_message);
|
||||
}
|
||||
|
||||
void BasebandProcessor::post_channel_spectrum_message(const buffer_c16_t data) {
|
||||
if( !channel_spectrum_request_update ) {
|
||||
fft_swap(data, channel_spectrum);
|
||||
channel_spectrum_sampling_rate = data.sampling_rate;
|
||||
channel_spectrum_request_update = true;
|
||||
events_flag(EVT_MASK_SPECTRUM);
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandProcessor::feed_audio_stats(const buffer_s16_t audio) {
|
||||
audio_stats.feed(
|
||||
audio,
|
||||
[this](const AudioStatistics statistics) {
|
||||
this->post_audio_stats_message(statistics);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void BasebandProcessor::post_audio_stats_message(const AudioStatistics statistics) {
|
||||
audio_stats_message.statistics = statistics;
|
||||
shared_memory.application_queue.push(audio_stats_message);
|
||||
}
|
||||
|
||||
void BasebandProcessor::fill_buffer(int8_t * inptr) {
|
||||
(void)inptr;
|
||||
}
|
||||
|
||||
@@ -23,55 +23,24 @@
|
||||
#define __BASEBAND_PROCESSOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
#include "block_decimator.hpp"
|
||||
#include "channel_stats_collector.hpp"
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <complex>
|
||||
#include "message.hpp"
|
||||
|
||||
class BasebandProcessor {
|
||||
public:
|
||||
virtual ~BasebandProcessor() = default;
|
||||
|
||||
virtual void execute(buffer_c8_t buffer) = 0;
|
||||
virtual void fill_buffer(int8_t * inptr);
|
||||
virtual void execute(const buffer_c8_t& buffer) = 0;
|
||||
|
||||
void update_spectrum();
|
||||
virtual void on_message(const Message* const) { };
|
||||
|
||||
protected:
|
||||
void feed_channel_stats(const buffer_c16_t channel);
|
||||
|
||||
void feed_channel_spectrum(
|
||||
const buffer_c16_t channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
);
|
||||
|
||||
void fill_audio_buffer(const buffer_s16_t audio);
|
||||
|
||||
volatile bool channel_spectrum_request_update { false };
|
||||
std::array<std::complex<float>, 256> channel_spectrum;
|
||||
uint32_t channel_spectrum_sampling_rate { 0 };
|
||||
uint32_t channel_filter_pass_frequency { 0 };
|
||||
uint32_t channel_filter_stop_frequency { 0 };
|
||||
void feed_channel_stats(const buffer_c16_t& channel);
|
||||
|
||||
private:
|
||||
BlockDecimator<256> channel_spectrum_decimator { 4 };
|
||||
|
||||
ChannelStatsCollector channel_stats;
|
||||
ChannelStatisticsMessage channel_stats_message;
|
||||
|
||||
AudioStatsCollector audio_stats;
|
||||
AudioStatisticsMessage audio_stats_message;
|
||||
|
||||
void post_channel_stats_message(const ChannelStatistics statistics);
|
||||
void post_channel_spectrum_message(const buffer_c16_t data);
|
||||
void feed_audio_stats(const buffer_s16_t audio);
|
||||
void post_audio_stats_message(const AudioStatistics statistics);
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_PROCESSOR_H__*/
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "baseband_stats_collector.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
bool BasebandStatsCollector::process(const buffer_c8_t& buffer) {
|
||||
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;
|
||||
}
|
||||
|
||||
BasebandStatistics BasebandStatsCollector::capture_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 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 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();
|
||||
|
||||
samples_last_report = samples;
|
||||
|
||||
return statistics;
|
||||
}
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
#include "utility_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@@ -46,36 +45,9 @@ public:
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void process(buffer_c8_t buffer, Callback callback) {
|
||||
samples += buffer.count;
|
||||
|
||||
const size_t report_samples = buffer.sampling_rate * report_interval;
|
||||
const auto report_delta = samples - samples_last_report;
|
||||
if( report_delta >= report_samples ) {
|
||||
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 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 baseband_ticks = thread_baseband->total_ticks;
|
||||
statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks);
|
||||
last_baseband_ticks = baseband_ticks;
|
||||
|
||||
statistics.saturation = m4_flag_saturation();
|
||||
clear_m4_flag_saturation();
|
||||
|
||||
callback(statistics);
|
||||
|
||||
samples_last_report = samples;
|
||||
void process(const buffer_c8_t& buffer, Callback callback) {
|
||||
if( process(buffer) ) {
|
||||
callback(capture_statistics());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +63,9 @@ private:
|
||||
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();
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_STATS_COLLECTOR_H__*/
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "baseband.hpp"
|
||||
#include "baseband_stats_collector.hpp"
|
||||
#include "baseband_sgpio.hpp"
|
||||
#include "baseband_dma.hpp"
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "i2s.hpp"
|
||||
|
||||
#include "proc_xylos.hpp"
|
||||
#include "proc_fsk_lcr.hpp"
|
||||
#include "proc_jammer.hpp"
|
||||
#include "proc_rds.hpp"
|
||||
#include "proc_playaudio.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
static baseband::SGPIO baseband_sgpio;
|
||||
|
||||
WORKING_AREA(baseband_thread_wa, 4096);
|
||||
|
||||
Thread* BasebandThread::start(const tprio_t priority) {
|
||||
return chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
void BasebandThread::set_configuration(const BasebandConfiguration& new_configuration) {
|
||||
if( new_configuration.mode != baseband_configuration.mode ) {
|
||||
disable();
|
||||
|
||||
// TODO: Timing problem around disabling DMA and nulling and deleting old processor
|
||||
auto old_p = baseband_processor;
|
||||
baseband_processor = nullptr;
|
||||
delete old_p;
|
||||
|
||||
baseband_processor = create_processor(new_configuration.mode);
|
||||
|
||||
enable();
|
||||
}
|
||||
|
||||
baseband_configuration = new_configuration;
|
||||
}
|
||||
|
||||
void BasebandThread::on_message(const Message* const message) {
|
||||
if( message->id == Message::ID::BasebandConfiguration ) {
|
||||
set_configuration(reinterpret_cast<const BasebandConfigurationMessage*>(message)->configuration);
|
||||
} else {
|
||||
if( baseband_processor ) {
|
||||
baseband_processor->on_message(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandThread::run() {
|
||||
baseband_sgpio.init();
|
||||
baseband::dma::init();
|
||||
|
||||
const auto baseband_buffer = new std::array<baseband::sample_t, 8192>();
|
||||
baseband::dma::configure(
|
||||
baseband_buffer->data(),
|
||||
direction()
|
||||
);
|
||||
//baseband::dma::allocate(4, 2048);
|
||||
|
||||
BasebandStatsCollector stats {
|
||||
chSysGetIdleThread(),
|
||||
thread_main,
|
||||
thread_rssi,
|
||||
chThdSelf()
|
||||
};
|
||||
|
||||
while(true) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = baseband::dma::wait_for_rx_buffer();
|
||||
if( buffer_tmp ) {
|
||||
buffer_c8_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate
|
||||
};
|
||||
|
||||
if( baseband_processor ) {
|
||||
baseband_processor->execute(buffer);
|
||||
}
|
||||
|
||||
stats.process(buffer,
|
||||
[](const BasebandStatistics& statistics) {
|
||||
const BasebandStatisticsMessage message { statistics };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delete baseband_buffer;
|
||||
}
|
||||
|
||||
BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
|
||||
switch(mode) {
|
||||
case 0: return new RDSProcessor();
|
||||
case 1: return new LCRFSKProcessor();
|
||||
case 2: return nullptr; //new ToneProcessor();
|
||||
case 3: return new JammerProcessor();
|
||||
case 4: return new XylosProcessor();
|
||||
case 5: return new PlayAudioProcessor();
|
||||
case 6: return nullptr; //new AFSKRXProcessor();
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandThread::disable() {
|
||||
if( baseband_processor ) {
|
||||
i2s::i2s0::tx_mute();
|
||||
baseband::dma::disable();
|
||||
baseband_sgpio.streaming_disable();
|
||||
rf::rssi::stop();
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandThread::enable() {
|
||||
if( baseband_processor ) {
|
||||
if( direction() == baseband::Direction::Receive ) {
|
||||
rf::rssi::start();
|
||||
}
|
||||
baseband_sgpio.configure(direction());
|
||||
baseband::dma::enable(direction());
|
||||
baseband_sgpio.streaming_enable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BASEBAND_THREAD_H__
|
||||
#define __BASEBAND_THREAD_H__
|
||||
|
||||
#include "thread_base.hpp"
|
||||
#include "message.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
class BasebandThread : public ThreadBase {
|
||||
public:
|
||||
BasebandThread(
|
||||
) : ThreadBase { "baseband" }
|
||||
{
|
||||
}
|
||||
|
||||
Thread* start(const tprio_t priority);
|
||||
|
||||
void on_message(const Message* const message);
|
||||
|
||||
// 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 baseband::Direction::Receive;
|
||||
}
|
||||
|
||||
Thread* thread_main { nullptr };
|
||||
Thread* thread_rssi { nullptr };
|
||||
BasebandProcessor* baseband_processor { nullptr };
|
||||
|
||||
private:
|
||||
BasebandConfiguration baseband_configuration;
|
||||
|
||||
void run() override;
|
||||
|
||||
BasebandProcessor* create_processor(const int32_t mode);
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
|
||||
void set_configuration(const BasebandConfiguration& new_configuration);
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_THREAD_H__*/
|
||||
@@ -1 +1 @@
|
||||
More specific stuff for testing :o
|
||||
More or less experimental stuff, mainly TX.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
void IIRBiquadFilter::configure(const iir_biquad_config_t& new_config) {
|
||||
config = new_config;
|
||||
}
|
||||
|
||||
void IIRBiquadFilter::execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out) {
|
||||
const auto a_ = config.a;
|
||||
const auto b_ = config.b;
|
||||
|
||||
auto x_ = x;
|
||||
auto y_ = y;
|
||||
|
||||
// TODO: Assert that buffer_out.count == buffer_in.count.
|
||||
for(size_t i=0; i<buffer_out.count; i++) {
|
||||
x_[0] = x_[1];
|
||||
x_[1] = x_[2];
|
||||
x_[2] = buffer_in.p[i];
|
||||
|
||||
y_[0] = y_[1];
|
||||
y_[1] = y_[2];
|
||||
y_[2] = b_[0] * x_[2] + b_[1] * x_[1] + b_[2] * x_[0]
|
||||
- a_[1] * y_[1] - a_[2] * y_[0];
|
||||
|
||||
buffer_out.p[i] = y_[2];
|
||||
}
|
||||
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
|
||||
void IIRBiquadFilter::execute_in_place(const buffer_f32_t& buffer) {
|
||||
execute(buffer, buffer);
|
||||
}
|
||||
@@ -27,13 +27,27 @@
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
struct iir_biquad_config_t {
|
||||
const std::array<float, 3> b;
|
||||
const std::array<float, 3> a;
|
||||
std::array<float, 3> b;
|
||||
std::array<float, 3> a;
|
||||
};
|
||||
|
||||
constexpr iir_biquad_config_t iir_config_passthrough {
|
||||
{ { 1.0f, 0.0f, 0.0f } },
|
||||
{ { 0.0f, 0.0f, 0.0f } },
|
||||
};
|
||||
|
||||
constexpr iir_biquad_config_t iir_config_no_pass {
|
||||
{ { 0.0f, 0.0f, 0.0f } },
|
||||
{ { 0.0f, 0.0f, 0.0f } },
|
||||
};
|
||||
|
||||
class IIRBiquadFilter {
|
||||
public:
|
||||
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
constexpr IIRBiquadFilter(
|
||||
) : IIRBiquadFilter(iir_config_no_pass)
|
||||
{
|
||||
}
|
||||
|
||||
// Assume all coefficients are normalized so that a0=1.0
|
||||
constexpr IIRBiquadFilter(
|
||||
@@ -42,34 +56,15 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void execute(buffer_s16_t buffer_in, buffer_s16_t buffer_out) {
|
||||
// TODO: Assert that buffer_out.count == buffer_in.count.
|
||||
for(size_t i=0; i<buffer_out.count; i++) {
|
||||
buffer_out.p[i] = execute_sample(buffer_in.p[i]);
|
||||
}
|
||||
}
|
||||
void configure(const iir_biquad_config_t& new_config);
|
||||
|
||||
void execute_in_place(buffer_s16_t buffer) {
|
||||
execute(buffer, buffer);
|
||||
}
|
||||
void execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out);
|
||||
void execute_in_place(const buffer_f32_t& buffer);
|
||||
|
||||
private:
|
||||
const iir_biquad_config_t config;
|
||||
iir_biquad_config_t config;
|
||||
std::array<float, 3> x { { 0.0f, 0.0f, 0.0f } };
|
||||
std::array<float, 3> y { { 0.0f, 0.0f, 0.0f } };
|
||||
|
||||
float execute_sample(const float in) {
|
||||
x[0] = x[1];
|
||||
x[1] = x[2];
|
||||
x[2] = in;
|
||||
|
||||
y[0] = y[1];
|
||||
y[1] = y[2];
|
||||
y[2] = config.b[0] * x[2] + config.b[1] * x[1] + config.b[2] * x[0]
|
||||
- config.a[1] * y[1] - config.a[2] * y[0];
|
||||
|
||||
return y[2];
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__DSP_IIR_H__*/
|
||||
|
||||
@@ -24,22 +24,30 @@
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
bool FMSquelch::execute(buffer_s16_t audio) {
|
||||
bool FMSquelch::execute(const buffer_f32_t& audio) {
|
||||
if( threshold_squared == 0.0f ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: No hard-coded array size.
|
||||
std::array<int16_t, N> squelch_energy_buffer;
|
||||
const buffer_s16_t squelch_energy {
|
||||
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);
|
||||
|
||||
uint64_t max_squared = 0;
|
||||
float non_audio_max_squared = 0;
|
||||
for(const auto sample : squelch_energy_buffer) {
|
||||
const uint64_t sample_squared = sample * sample;
|
||||
if( sample_squared > max_squared ) {
|
||||
max_squared = sample_squared;
|
||||
const float sample_squared = sample * sample;
|
||||
if( sample_squared > non_audio_max_squared ) {
|
||||
non_audio_max_squared = sample_squared;
|
||||
}
|
||||
}
|
||||
|
||||
return (max_squared < (threshold * threshold));
|
||||
return (non_audio_max_squared < threshold_squared);
|
||||
}
|
||||
|
||||
void FMSquelch::set_threshold(const float new_value) {
|
||||
threshold_squared = new_value * new_value;
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
|
||||
class FMSquelch {
|
||||
public:
|
||||
bool execute(buffer_s16_t audio);
|
||||
bool execute(const buffer_f32_t& audio);
|
||||
|
||||
void set_threshold(const float new_value);
|
||||
|
||||
private:
|
||||
static constexpr size_t N = 32;
|
||||
static constexpr int16_t threshold = 3072;
|
||||
float threshold_squared { 0.0f };
|
||||
|
||||
// nyquist = 48000 / 2.0
|
||||
// scipy.signal.iirdesign(wp=8000 / nyquist, ws= 4000 / nyquist, gpass=1, gstop=18, ftype='ellip')
|
||||
IIRBiquadFilter non_audio_hpf { non_audio_hpf_config };
|
||||
};
|
||||
|
||||
|
||||
@@ -21,10 +21,99 @@
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "message_queue.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
Thread* thread_event_loop = nullptr;
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
void events_initialize(Thread* const event_loop_thread) {
|
||||
thread_event_loop = event_loop_thread;
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
extern "C" {
|
||||
|
||||
CH_IRQ_HANDLER(MAPP_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
chSysLockFromIsr();
|
||||
EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND);
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
creg::m0apptxevent::clear();
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Thread* EventDispatcher::thread_event_loop = nullptr;
|
||||
|
||||
void EventDispatcher::run() {
|
||||
thread_event_loop = chThdSelf();
|
||||
lpc43xx::creg::m0apptxevent::enable();
|
||||
|
||||
baseband_thread.thread_main = chThdSelf();
|
||||
baseband_thread.thread_rssi = rssi_thread.start(NORMALPRIO + 10);
|
||||
baseband_thread.start(NORMALPRIO + 20);
|
||||
|
||||
while(is_running) {
|
||||
const auto events = wait();
|
||||
dispatch(events);
|
||||
}
|
||||
|
||||
lpc43xx::creg::m0apptxevent::disable();
|
||||
}
|
||||
|
||||
void EventDispatcher::request_stop() {
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
eventmask_t EventDispatcher::wait() {
|
||||
return chEvtWaitAny(ALL_EVENTS);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
if( events & EVT_MASK_BASEBAND ) {
|
||||
handle_baseband_queue();
|
||||
}
|
||||
|
||||
if( events & EVT_MASK_SPECTRUM ) {
|
||||
handle_spectrum();
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_baseband_queue() {
|
||||
std::array<uint8_t, Message::MAX_SIZE> message_buffer;
|
||||
while(Message* const message = shared_memory.baseband_queue.peek(message_buffer)) {
|
||||
on_message(message);
|
||||
shared_memory.baseband_queue.skip();
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::Shutdown:
|
||||
on_message_shutdown(*reinterpret_cast<const ShutdownMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
on_message_default(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message_shutdown(const ShutdownMessage&) {
|
||||
request_stop();
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message_default(const Message* const message) {
|
||||
baseband_thread.on_message(message);
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_spectrum() {
|
||||
const UpdateSpectrumMessage message;
|
||||
baseband_thread.on_message(&message);
|
||||
}
|
||||
|
||||
@@ -22,25 +22,54 @@
|
||||
#ifndef __EVENT_M4_H__
|
||||
#define __EVENT_M4_H__
|
||||
|
||||
#include "event.hpp"
|
||||
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0);
|
||||
constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1);
|
||||
|
||||
void events_initialize(Thread* const event_loop_thread);
|
||||
class EventDispatcher {
|
||||
public:
|
||||
void run();
|
||||
void request_stop();
|
||||
|
||||
extern Thread* thread_event_loop;
|
||||
|
||||
inline void events_flag(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
chEvtSignal(thread_event_loop, events);
|
||||
static inline void events_flag(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
chEvtSignal(thread_event_loop, events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void events_flag_isr(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
chEvtSignalI(thread_event_loop, events);
|
||||
static inline void events_flag_isr(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
chEvtSignalI(thread_event_loop, events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static Thread* thread_event_loop;
|
||||
|
||||
BasebandThread baseband_thread;
|
||||
RSSIThread rssi_thread;
|
||||
|
||||
bool is_running = true;
|
||||
|
||||
eventmask_t wait();
|
||||
|
||||
void dispatch(const eventmask_t events);
|
||||
|
||||
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 handle_spectrum();
|
||||
};
|
||||
|
||||
#endif/*__EVENT_M4_H__*/
|
||||
|
||||
+72
-371
@@ -15,13 +15,11 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "test.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
@@ -30,34 +28,13 @@
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
#include "baseband.hpp"
|
||||
#include "baseband_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "irq_ipc_m4.hpp"
|
||||
|
||||
#include "touch_dma.hpp"
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_fft.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
#include "proc_fsk_lcr.hpp"
|
||||
#include "proc_jammer.hpp"
|
||||
#include "proc_xylos.hpp"
|
||||
#include "proc_playaudio.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
|
||||
#include "message_queue.hpp"
|
||||
|
||||
@@ -73,55 +50,87 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <math.h>
|
||||
|
||||
static baseband::Direction direction = baseband::Direction::Receive;
|
||||
extern "C" {
|
||||
|
||||
class ThreadBase {
|
||||
public:
|
||||
constexpr ThreadBase(
|
||||
const char* const name
|
||||
) : name { name }
|
||||
{
|
||||
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();
|
||||
|
||||
/* 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() {
|
||||
i2s::i2s0::configure(
|
||||
audio::i2s0_config_tx,
|
||||
audio::i2s0_config_rx,
|
||||
audio::i2s0_config_dma
|
||||
);
|
||||
|
||||
audio::dma::init();
|
||||
audio::dma::configure();
|
||||
audio::dma::enable();
|
||||
|
||||
i2s::i2s0::tx_start();
|
||||
i2s::i2s0::rx_start();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
static void halt() {
|
||||
port_disable();
|
||||
while(true) {
|
||||
port_wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
static msg_t fn(void* arg) {
|
||||
auto obj = static_cast<ThreadBase*>(arg);
|
||||
chRegSetThreadName(obj->name);
|
||||
obj->run();
|
||||
static void shutdown() {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
chSysDisable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
systick_stop();
|
||||
|
||||
virtual void run() = 0;
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.application_queue.push(shutdown_message);
|
||||
|
||||
private:
|
||||
const char* const name;
|
||||
};
|
||||
halt();
|
||||
}
|
||||
|
||||
class BasebandThread : public ThreadBase {
|
||||
public:
|
||||
BasebandThread(
|
||||
) : ThreadBase { "baseband" }
|
||||
{
|
||||
}
|
||||
int main(void) {
|
||||
init();
|
||||
|
||||
Thread* start(const tprio_t priority) {
|
||||
return chThdCreateStatic(wa, sizeof(wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
|
||||
|
||||
Thread* thread_main { nullptr };
|
||||
BasebandProcessor* baseband_processor { nullptr };
|
||||
BasebandConfiguration baseband_configuration;
|
||||
EventDispatcher event_dispatcher;
|
||||
event_dispatcher.run();
|
||||
|
||||
private:
|
||||
WORKING_AREA(wa, 2048);
|
||||
shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void run() override {
|
||||
while(true) {
|
||||
if (direction == baseband::Direction::Transmit) {
|
||||
@@ -149,167 +158,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
#define SAMPLES_PER_BIT 192
|
||||
#define FILTER_SIZE 576
|
||||
#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE
|
||||
|
||||
static int32_t waveform_biphase[] = {
|
||||
165,167,168,168,167,166,163,160,
|
||||
157,152,147,141,134,126,118,109,
|
||||
99,88,77,66,53,41,27,14,
|
||||
0,-14,-29,-44,-59,-74,-89,-105,
|
||||
-120,-135,-150,-165,-179,-193,-206,-218,
|
||||
-231,-242,-252,-262,-271,-279,-286,-291,
|
||||
-296,-299,-301,-302,-302,-300,-297,-292,
|
||||
-286,-278,-269,-259,-247,-233,-219,-202,
|
||||
-185,-166,-145,-124,-101,-77,-52,-26,
|
||||
0,27,56,85,114,144,175,205,
|
||||
236,266,296,326,356,384,412,439,
|
||||
465,490,513,535,555,574,590,604,
|
||||
616,626,633,637,639,638,633,626,
|
||||
616,602,586,565,542,515,485,451,
|
||||
414,373,329,282,232,178,121,62,
|
||||
0,-65,-132,-202,-274,-347,-423,-500,
|
||||
-578,-656,-736,-815,-894,-973,-1051,-1128,
|
||||
-1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648,
|
||||
-1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835,
|
||||
-1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437,
|
||||
-1334,-1217,-1087,-943,-785,-611,-423,-219,
|
||||
0,235,487,755,1040,1341,1659,1994,
|
||||
2346,2715,3101,3504,3923,4359,4811,5280,
|
||||
5764,6264,6780,7310,7856,8415,8987,9573,
|
||||
10172,10782,11404,12036,12678,13329,13989,14656,
|
||||
15330,16009,16694,17382,18074,18767,19461,20155,
|
||||
20848,21539,22226,22909,23586,24256,24918,25571,
|
||||
26214,26845,27464,28068,28658,29231,29787,30325,
|
||||
30842,31339,31814,32266,32694,33097,33473,33823,
|
||||
34144,34437,34699,34931,35131,35299,35434,35535,
|
||||
35602,35634,35630,35591,35515,35402,35252,35065,
|
||||
34841,34579,34279,33941,33566,33153,32702,32214,
|
||||
31689,31128,30530,29897,29228,28525,27788,27017,
|
||||
26214,25379,24513,23617,22693,21740,20761,19755,
|
||||
18725,17672,16597,15501,14385,13251,12101,10935,
|
||||
9755,8563,7360,6148,4927,3701,2470,1235,
|
||||
0,-1235,-2470,-3701,-4927,-6148,-7360,-8563,
|
||||
-9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672,
|
||||
-18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379,
|
||||
-26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128,
|
||||
-31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579,
|
||||
-34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634,
|
||||
-35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437,
|
||||
-34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339,
|
||||
-30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845,
|
||||
-26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539,
|
||||
-20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009,
|
||||
-15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782,
|
||||
-10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264,
|
||||
-5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715,
|
||||
-2346,-1994,-1659,-1341,-1040,-755,-487,-235,
|
||||
0,219,423,611,785,943,1087,1217,
|
||||
1334,1437,1527,1605,1670,1724,1767,1800,
|
||||
1822,1835,1838,1833,1820,1799,1771,1736,
|
||||
1695,1648,1596,1540,1479,1415,1347,1276,
|
||||
1203,1128,1051,973,894,815,736,656,
|
||||
578,500,423,347,274,202,132,65,
|
||||
0,-62,-121,-178,-232,-282,-329,-373,
|
||||
-414,-451,-485,-515,-542,-565,-586,-602,
|
||||
-616,-626,-633,-638,-639,-637,-633,-626,
|
||||
-616,-604,-590,-574,-555,-535,-513,-490,
|
||||
-465,-439,-412,-384,-356,-326,-296,-266,
|
||||
-236,-205,-175,-144,-114,-85,-56,-27,
|
||||
0,26,52,77,101,124,145,166,
|
||||
185,202,219,233,247,259,269,278,
|
||||
286,292,297,300,302,302,301,299,
|
||||
296,291,286,279,271,262,252,242,
|
||||
231,218,206,193,179,165,150,135,
|
||||
120,105,89,74,59,44,29,14,
|
||||
0,-14,-27,-41,-53,-66,-77,-88,
|
||||
-99,-109,-118,-126,-134,-141,-147,-152,
|
||||
-157,-160,-163,-166,-167,-168,-168,-167
|
||||
};
|
||||
|
||||
class RDSProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override {
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10=228kHz
|
||||
if(s >= 9) {
|
||||
s = 0;
|
||||
if(sample_count >= SAMPLES_PER_BIT) {
|
||||
cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1;
|
||||
prev_output = cur_output;
|
||||
cur_output = prev_output ^ cur_bit;
|
||||
|
||||
int32_t *src = waveform_biphase;
|
||||
int idx = in_sample_index;
|
||||
|
||||
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
|
||||
switch (mphase) {
|
||||
case 0:
|
||||
case 2: sample = 0; break;
|
||||
case 1: break;
|
||||
case 3: sample = -sample; break;
|
||||
}
|
||||
mphase++;
|
||||
if (mphase >= 4) mphase = 0;
|
||||
|
||||
sample_count++;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
|
||||
//FM
|
||||
frq = (sample>>16) * 386760;
|
||||
|
||||
phase = (phase + frq);
|
||||
sphase = phase + (256<<16);
|
||||
|
||||
//re = sintab[(sphase & 0x03FF0000)>>16];
|
||||
//im = sintab[(phase & 0x03FF0000)>>16];
|
||||
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int8_t re, im;
|
||||
uint8_t mphase, s;
|
||||
uint32_t bit_pos;
|
||||
int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0};
|
||||
int32_t val;
|
||||
uint8_t prev_output = 0;
|
||||
uint8_t cur_output = 0;
|
||||
uint8_t cur_bit = 0;
|
||||
int sample_count = SAMPLES_PER_BIT;
|
||||
int in_sample_index = 0;
|
||||
int32_t sample;
|
||||
int out_sample_index = SAMPLE_BUFFER_SIZE-1;
|
||||
uint32_t phase, sphase;
|
||||
int32_t sig, frq, frq_im, rdsc;
|
||||
int32_t k;
|
||||
};
|
||||
|
||||
class ToneProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override {
|
||||
@@ -348,130 +196,6 @@ private:
|
||||
int32_t sample, sig, frq;
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
/* 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 BasebandThread baseband_thread;
|
||||
|
||||
static void init() {
|
||||
i2s::i2s0::configure(
|
||||
audio::i2s0_config_tx,
|
||||
audio::i2s0_config_rx,
|
||||
audio::i2s0_config_dma
|
||||
);
|
||||
|
||||
audio::dma::init();
|
||||
audio::dma::configure();
|
||||
audio::dma::enable();
|
||||
|
||||
i2s::i2s0::tx_start();
|
||||
i2s::i2s0::rx_start();
|
||||
|
||||
LPC_CREG->DMAMUX = portapack::gpdma_mux;
|
||||
gpdma::controller.enable();
|
||||
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
|
||||
|
||||
baseband::dma::init();
|
||||
|
||||
touch::dma::init();
|
||||
|
||||
const auto thread_main = chThdSelf();
|
||||
|
||||
baseband_thread.thread_main = thread_main;
|
||||
|
||||
baseband_thread.start(NORMALPRIO + 20);
|
||||
}
|
||||
|
||||
static void shutdown() {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
m0apptxevent_interrupt_disable();
|
||||
|
||||
chSysDisable();
|
||||
|
||||
systick_stop();
|
||||
}
|
||||
|
||||
static void halt() {
|
||||
port_disable();
|
||||
while(true) {
|
||||
port_wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
MessageHandlerMap& message_handlers() {
|
||||
return message_map;
|
||||
}
|
||||
|
||||
void run() {
|
||||
while(is_running) {
|
||||
const auto events = wait();
|
||||
dispatch(events);
|
||||
}
|
||||
}
|
||||
|
||||
void request_stop() {
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
private:
|
||||
MessageHandlerMap message_map;
|
||||
|
||||
bool is_running = true;
|
||||
|
||||
eventmask_t wait() {
|
||||
return chEvtWaitAny(ALL_EVENTS);
|
||||
}
|
||||
|
||||
void dispatch(const eventmask_t events) {
|
||||
if( events & EVT_MASK_BASEBAND ) {
|
||||
handle_baseband_queue();
|
||||
}
|
||||
|
||||
if( events & EVT_MASK_SPECTRUM ) {
|
||||
handle_spectrum();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_baseband_queue() {
|
||||
std::array<uint8_t, Message::MAX_SIZE> message_buffer;
|
||||
while(Message* const message = shared_memory.baseband_queue.pop(message_buffer)) {
|
||||
message_map.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_spectrum() {
|
||||
if( baseband_thread.baseband_processor ) {
|
||||
baseband_thread.baseband_processor->update_spectrum();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto baseband_buffer =
|
||||
new std::array<baseband::sample_t, 8192>();
|
||||
|
||||
char ram_loop[32];
|
||||
typedef int (*fn_ptr)(void);
|
||||
fn_ptr loop_ptr;
|
||||
@@ -586,27 +310,4 @@ int main(void) {
|
||||
event_dispatcher.request_stop();
|
||||
}
|
||||
);
|
||||
|
||||
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
|
||||
|
||||
touch::dma::allocate();
|
||||
touch::dma::enable();
|
||||
|
||||
baseband::dma::configure(
|
||||
baseband_buffer->data(),
|
||||
direction
|
||||
);
|
||||
|
||||
//baseband::dma::allocate(4, 2048);
|
||||
|
||||
event_dispatcher.run();
|
||||
|
||||
shutdown();
|
||||
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.application_queue.push(shutdown_message);
|
||||
|
||||
halt();
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1 +1 @@
|
||||
Second module
|
||||
Experimental
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void AudioTXProcessor::execute(buffer_c8_t buffer) {
|
||||
void AudioTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
class AudioTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
|
||||
private:
|
||||
int8_t audio_fifo[SAMPLERATE];
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void LCRFSKProcessor::execute(buffer_c8_t buffer) {
|
||||
void LCRFSKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
class LCRFSKProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int8_t re, im;
|
||||
|
||||
@@ -27,33 +27,9 @@
|
||||
|
||||
#define POLY_MASK_32 0xB4BCD35C
|
||||
|
||||
|
||||
void JammerProcessor::execute(buffer_c8_t buffer) {
|
||||
void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
/*if (s > 3000000) {
|
||||
s = 0;
|
||||
feedback = lfsr & 1;
|
||||
lfsr >>= 1;
|
||||
if (feedback == 1)
|
||||
lfsr ^= POLY_MASK_32;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
|
||||
aphase += lfsr;*/
|
||||
|
||||
/*if (s >= 10) {
|
||||
s = 0;
|
||||
aphase += 353205; // DEBUG
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
|
||||
sample = sintab[(aphase & 0x03FF0000)>>16];*/
|
||||
|
||||
// Duration timer
|
||||
//
|
||||
if (s >= 10000) { //shared_memory.jammer_ranges[ir].duration
|
||||
s = 0;
|
||||
for (;;) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
class JammerProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int32_t lfsr32 = 0xABCDE;
|
||||
|
||||
@@ -33,7 +33,7 @@ void PlayAudioProcessor::fill_buffer(int8_t * inptr) {
|
||||
asked = false;
|
||||
}
|
||||
|
||||
void PlayAudioProcessor::execute(buffer_c8_t buffer) {
|
||||
void PlayAudioProcessor::execute(const buffer_c8_t& buffer){
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
|
||||
@@ -69,5 +69,5 @@ void PlayAudioProcessor::execute(buffer_c8_t buffer) {
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
}
|
||||
|
||||
fill_audio_buffer(preview_audio_buffer);
|
||||
//fill_audio_buffer(preview_audio_buffer);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
class PlayAudioProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void fill_buffer(int8_t * inptr);
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "proc_rds.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void RDSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10=228kHz
|
||||
if(s >= 9) {
|
||||
s = 0;
|
||||
if(sample_count >= SAMPLES_PER_BIT) {
|
||||
cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1;
|
||||
prev_output = cur_output;
|
||||
cur_output = prev_output ^ cur_bit;
|
||||
|
||||
int32_t *src = waveform_biphase;
|
||||
int idx = in_sample_index;
|
||||
|
||||
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
|
||||
switch (mphase) {
|
||||
case 0:
|
||||
case 2: sample = 0; break;
|
||||
case 1: break;
|
||||
case 3: sample = -sample; break;
|
||||
}
|
||||
mphase++;
|
||||
if (mphase >= 4) mphase = 0;
|
||||
|
||||
sample_count++;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
|
||||
//FM
|
||||
frq = (sample>>16) * 386760;
|
||||
|
||||
phase = (phase + frq);
|
||||
sphase = phase + (256<<16);
|
||||
|
||||
//re = sintab[(sphase & 0x03FF0000)>>16];
|
||||
//im = sintab[(phase & 0x03FF0000)>>16];
|
||||
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PROC_RDS_H__
|
||||
#define __PROC_RDS_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#define SAMPLES_PER_BIT 192
|
||||
#define FILTER_SIZE 576
|
||||
#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE
|
||||
|
||||
class RDSProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int8_t re, im;
|
||||
uint8_t mphase, s;
|
||||
uint32_t bit_pos;
|
||||
int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0};
|
||||
int32_t val;
|
||||
uint8_t prev_output = 0;
|
||||
uint8_t cur_output = 0;
|
||||
uint8_t cur_bit = 0;
|
||||
int sample_count = SAMPLES_PER_BIT;
|
||||
int in_sample_index = 0;
|
||||
int32_t sample;
|
||||
int out_sample_index = SAMPLE_BUFFER_SIZE-1;
|
||||
uint32_t phase, sphase;
|
||||
int32_t sig, frq, frq_im, rdsc;
|
||||
int32_t k;
|
||||
|
||||
int32_t waveform_biphase[576] = {
|
||||
165,167,168,168,167,166,163,160,
|
||||
157,152,147,141,134,126,118,109,
|
||||
99,88,77,66,53,41,27,14,
|
||||
0,-14,-29,-44,-59,-74,-89,-105,
|
||||
-120,-135,-150,-165,-179,-193,-206,-218,
|
||||
-231,-242,-252,-262,-271,-279,-286,-291,
|
||||
-296,-299,-301,-302,-302,-300,-297,-292,
|
||||
-286,-278,-269,-259,-247,-233,-219,-202,
|
||||
-185,-166,-145,-124,-101,-77,-52,-26,
|
||||
0,27,56,85,114,144,175,205,
|
||||
236,266,296,326,356,384,412,439,
|
||||
465,490,513,535,555,574,590,604,
|
||||
616,626,633,637,639,638,633,626,
|
||||
616,602,586,565,542,515,485,451,
|
||||
414,373,329,282,232,178,121,62,
|
||||
0,-65,-132,-202,-274,-347,-423,-500,
|
||||
-578,-656,-736,-815,-894,-973,-1051,-1128,
|
||||
-1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648,
|
||||
-1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835,
|
||||
-1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437,
|
||||
-1334,-1217,-1087,-943,-785,-611,-423,-219,
|
||||
0,235,487,755,1040,1341,1659,1994,
|
||||
2346,2715,3101,3504,3923,4359,4811,5280,
|
||||
5764,6264,6780,7310,7856,8415,8987,9573,
|
||||
10172,10782,11404,12036,12678,13329,13989,14656,
|
||||
15330,16009,16694,17382,18074,18767,19461,20155,
|
||||
20848,21539,22226,22909,23586,24256,24918,25571,
|
||||
26214,26845,27464,28068,28658,29231,29787,30325,
|
||||
30842,31339,31814,32266,32694,33097,33473,33823,
|
||||
34144,34437,34699,34931,35131,35299,35434,35535,
|
||||
35602,35634,35630,35591,35515,35402,35252,35065,
|
||||
34841,34579,34279,33941,33566,33153,32702,32214,
|
||||
31689,31128,30530,29897,29228,28525,27788,27017,
|
||||
26214,25379,24513,23617,22693,21740,20761,19755,
|
||||
18725,17672,16597,15501,14385,13251,12101,10935,
|
||||
9755,8563,7360,6148,4927,3701,2470,1235,
|
||||
0,-1235,-2470,-3701,-4927,-6148,-7360,-8563,
|
||||
-9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672,
|
||||
-18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379,
|
||||
-26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128,
|
||||
-31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579,
|
||||
-34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634,
|
||||
-35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437,
|
||||
-34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339,
|
||||
-30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845,
|
||||
-26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539,
|
||||
-20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009,
|
||||
-15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782,
|
||||
-10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264,
|
||||
-5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715,
|
||||
-2346,-1994,-1659,-1341,-1040,-755,-487,-235,
|
||||
0,219,423,611,785,943,1087,1217,
|
||||
1334,1437,1527,1605,1670,1724,1767,1800,
|
||||
1822,1835,1838,1833,1820,1799,1771,1736,
|
||||
1695,1648,1596,1540,1479,1415,1347,1276,
|
||||
1203,1128,1051,973,894,815,736,656,
|
||||
578,500,423,347,274,202,132,65,
|
||||
0,-62,-121,-178,-232,-282,-329,-373,
|
||||
-414,-451,-485,-515,-542,-565,-586,-602,
|
||||
-616,-626,-633,-638,-639,-637,-633,-626,
|
||||
-616,-604,-590,-574,-555,-535,-513,-490,
|
||||
-465,-439,-412,-384,-356,-326,-296,-266,
|
||||
-236,-205,-175,-144,-114,-85,-56,-27,
|
||||
0,26,52,77,101,124,145,166,
|
||||
185,202,219,233,247,259,269,278,
|
||||
286,292,297,300,302,302,301,299,
|
||||
296,291,286,279,271,262,252,242,
|
||||
231,218,206,193,179,165,150,135,
|
||||
120,105,89,74,59,44,29,14,
|
||||
0,-14,-27,-41,-53,-66,-77,-88,
|
||||
-99,-109,-118,-126,-134,-141,-147,-152,
|
||||
-157,-160,-163,-166,-167,-168,-168,-167
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -21,6 +21,10 @@
|
||||
*/
|
||||
|
||||
#include "proc_xylos.hpp"
|
||||
|
||||
#include "dsp_iir_config.hpp"
|
||||
//#include "audio_output.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
@@ -31,7 +35,7 @@
|
||||
// 14 13 12 11:
|
||||
// 2108 989 2259 931
|
||||
|
||||
void XylosProcessor::execute(buffer_c8_t buffer) {
|
||||
void XylosProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
|
||||
@@ -88,5 +92,5 @@ void XylosProcessor::execute(buffer_c8_t buffer) {
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
}
|
||||
|
||||
fill_audio_buffer(preview_audio_buffer);
|
||||
//audio_output.write(preview_audio_buffer);
|
||||
}
|
||||
|
||||
@@ -25,12 +25,18 @@
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
|
||||
//#include "audio_output.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#define CCIR_TONELENGTH 15360-1 // 1536000/10/10
|
||||
#define PHASEV 436.91 // (65536*1024)/1536000*10
|
||||
|
||||
class XylosProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int16_t audio_data[64];
|
||||
@@ -67,6 +73,8 @@ private:
|
||||
uint32_t aphase, phase, sphase;
|
||||
int32_t sample, frq;
|
||||
TXDoneMessage message;
|
||||
|
||||
//AudioOutput audio_output;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -157,7 +157,7 @@ bool is_enabled() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel.disable_force();
|
||||
gpdma_channel.disable();
|
||||
}
|
||||
|
||||
rf::rssi::buffer_t wait_for_buffer() {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "rssi_dma.hpp"
|
||||
#include "rssi_stats_collector.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
WORKING_AREA(rssi_thread_wa, 128);
|
||||
|
||||
Thread* RSSIThread::start(const tprio_t priority) {
|
||||
return chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
void RSSIThread::run() {
|
||||
rf::rssi::init();
|
||||
rf::rssi::dma::allocate(4, 400);
|
||||
|
||||
RSSIStatisticsCollector stats;
|
||||
|
||||
while(true) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = rf::rssi::dma::wait_for_buffer();
|
||||
const rf::rssi::buffer_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, sampling_rate
|
||||
};
|
||||
|
||||
stats.process(
|
||||
buffer,
|
||||
[](const RSSIStatistics& statistics) {
|
||||
const RSSIStatisticsMessage message { statistics };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
rf::rssi::dma::free();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __RSSI_THREAD_H__
|
||||
#define __RSSI_THREAD_H__
|
||||
|
||||
#include "thread_base.hpp"
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class RSSIThread : public ThreadBase {
|
||||
public:
|
||||
RSSIThread(
|
||||
) : ThreadBase { "rssi" }
|
||||
{
|
||||
}
|
||||
|
||||
Thread* start(const tprio_t priority);
|
||||
|
||||
private:
|
||||
void run() override;
|
||||
|
||||
const uint32_t sampling_rate { 400000 };
|
||||
};
|
||||
|
||||
#endif/*__RSSI_THREAD_H__*/
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include "dsp_fft.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
void SpectrumCollector::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
update();
|
||||
break;
|
||||
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
set_state(*reinterpret_cast<const SpectrumStreamingConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCollector::set_state(const SpectrumStreamingConfigMessage& message) {
|
||||
if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) {
|
||||
start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCollector::start() {
|
||||
streaming = true;
|
||||
ChannelSpectrumConfigMessage message { &fifo };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void SpectrumCollector::stop() {
|
||||
streaming = false;
|
||||
fifo.reset_in();
|
||||
}
|
||||
|
||||
void SpectrumCollector::set_decimation_factor(
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
channel_spectrum_decimator.set_factor(decimation_factor);
|
||||
}
|
||||
|
||||
/* TODO: Refactor to register task with idle thread?
|
||||
* It's sad that the idle thread has to call all the way back here just to
|
||||
* perform the deferred task on the buffer of data we prepared.
|
||||
*/
|
||||
|
||||
void SpectrumCollector::feed(
|
||||
const buffer_c16_t& channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
) {
|
||||
// Called from baseband processing thread.
|
||||
channel_filter_pass_frequency = filter_pass_frequency;
|
||||
channel_filter_stop_frequency = filter_stop_frequency;
|
||||
|
||||
channel_spectrum_decimator.feed(
|
||||
channel,
|
||||
[this](const buffer_c16_t& data) {
|
||||
this->post_message(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SpectrumCollector::post_message(const buffer_c16_t& data) {
|
||||
// Called from baseband processing thread.
|
||||
if( streaming && !channel_spectrum_request_update ) {
|
||||
fft_swap(data, channel_spectrum);
|
||||
channel_spectrum_sampling_rate = data.sampling_rate;
|
||||
channel_spectrum_request_update = true;
|
||||
EventDispatcher::events_flag(EVT_MASK_SPECTRUM);
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCollector::update() {
|
||||
// Called from idle thread (after EVT_MASK_SPECTRUM is flagged)
|
||||
if( streaming && channel_spectrum_request_update ) {
|
||||
/* Decimated buffer is full. Compute spectrum. */
|
||||
fft_c_preswapped(channel_spectrum);
|
||||
|
||||
ChannelSpectrum spectrum;
|
||||
spectrum.sampling_rate = channel_spectrum_sampling_rate;
|
||||
spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency;
|
||||
spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency;
|
||||
for(size_t i=0; i<spectrum.db.size(); i++) {
|
||||
// Three point Hamming window.
|
||||
const auto corrected_sample = channel_spectrum[i] * 0.54f
|
||||
+ (channel_spectrum[(i-1) & 0xff] + channel_spectrum[(i+1) & 0xff]) * -0.23f;
|
||||
const auto mag2 = magnitude_squared(corrected_sample);
|
||||
const float db = complex16_mag_squared_to_dbv_norm(mag2);
|
||||
constexpr float mag_scale = 5.0f;
|
||||
const unsigned int v = (db * mag_scale) + 255.0f;
|
||||
spectrum.db[i] = std::max(0U, std::min(255U, v));
|
||||
}
|
||||
fifo.in(spectrum);
|
||||
}
|
||||
|
||||
channel_spectrum_request_update = false;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPECTRUM_COLLECTOR_H__
|
||||
#define __SPECTRUM_COLLECTOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
#include "block_decimator.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
class SpectrumCollector {
|
||||
public:
|
||||
constexpr SpectrumCollector(
|
||||
) : channel_spectrum_decimator { 1 }
|
||||
{
|
||||
}
|
||||
|
||||
void on_message(const Message* const message);
|
||||
|
||||
void set_decimation_factor(const size_t decimation_factor);
|
||||
|
||||
void feed(
|
||||
const buffer_c16_t& channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
);
|
||||
|
||||
private:
|
||||
BlockDecimator<256> channel_spectrum_decimator;
|
||||
ChannelSpectrumFIFO fifo;
|
||||
|
||||
volatile bool channel_spectrum_request_update { false };
|
||||
bool streaming { false };
|
||||
std::array<std::complex<float>, 256> channel_spectrum;
|
||||
uint32_t channel_spectrum_sampling_rate { 0 };
|
||||
uint32_t channel_filter_pass_frequency { 0 };
|
||||
uint32_t channel_filter_stop_frequency { 0 };
|
||||
|
||||
void post_message(const buffer_c16_t& data);
|
||||
|
||||
void set_state(const SpectrumStreamingConfigMessage& message);
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
#endif/*__SPECTRUM_COLLECTOR_H__*/
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __THREAD_BASE_H__
|
||||
#define __THREAD_BASE_H__
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
class ThreadBase {
|
||||
public:
|
||||
constexpr ThreadBase(
|
||||
const char* const name
|
||||
) : name { name }
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
static msg_t fn(void* arg) {
|
||||
auto obj = static_cast<ThreadBase*>(arg);
|
||||
chRegSetThreadName(obj->name);
|
||||
obj->run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* const name;
|
||||
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
#endif/*__THREAD_BASE_H__*/
|
||||
@@ -122,7 +122,7 @@ bool is_enabled() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel.disable_force();
|
||||
gpdma_channel.disable();
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
|
||||
Reference in New Issue
Block a user