mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-23 06:18:08 +00:00
Merged remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -125,13 +125,23 @@ CSRC = $(PORTSRC) \
|
||||
CPPSRC = main.cpp \
|
||||
message_queue.cpp \
|
||||
event_m4.cpp \
|
||||
irq_ipc_m4.cpp \
|
||||
gpdma.cpp \
|
||||
baseband_dma.cpp \
|
||||
portapack_shared_memory.cpp \
|
||||
baseband_processor.cpp \
|
||||
channel_decimator.cpp \
|
||||
dsp_decimate.cpp \
|
||||
dsp_demodulate.cpp \
|
||||
matched_filter.cpp \
|
||||
proc_am_audio.cpp \
|
||||
proc_nfm_audio.cpp \
|
||||
proc_wfm_audio.cpp \
|
||||
proc_ais.cpp \
|
||||
proc_wideband_spectrum.cpp \
|
||||
proc_tpms.cpp \
|
||||
dsp_squelch.cpp \
|
||||
clock_recovery.cpp \
|
||||
access_code_correlator.cpp \
|
||||
packet_builder.cpp \
|
||||
dsp_fft.cpp \
|
||||
dsp_fir_taps.cpp \
|
||||
@@ -142,6 +152,7 @@ CPPSRC = main.cpp \
|
||||
audio_dma.cpp \
|
||||
touch_dma.cpp \
|
||||
../common/utility.cpp \
|
||||
../common/chibios_cpp.cpp \
|
||||
../common/debug.cpp \
|
||||
../common/gcc.cpp
|
||||
|
||||
@@ -222,7 +233,8 @@ CPPWARN = -Wall -Wextra
|
||||
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
|
||||
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
|
||||
DDEFS = -DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE \
|
||||
-DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0
|
||||
-DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 \
|
||||
-DGIT_REVISION=\"$(GIT_REVISION)\"
|
||||
|
||||
# List all default ASM defines here, like -D_DEBUG=1
|
||||
DADEFS =
|
||||
|
122
firmware/baseband/baseband_processor.cpp
Normal file
122
firmware/baseband/baseband_processor.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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_processor.hpp"
|
||||
|
||||
#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) {
|
||||
channel_stats.feed(
|
||||
channel,
|
||||
[this](const ChannelStatistics statistics) {
|
||||
this->post_channel_stats_message(statistics);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
76
firmware/baseband/baseband_processor.hpp
Normal file
76
firmware/baseband/baseband_processor.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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_PROCESSOR_H__
|
||||
#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>
|
||||
|
||||
class BasebandProcessor {
|
||||
public:
|
||||
virtual ~BasebandProcessor() = default;
|
||||
|
||||
virtual void execute(buffer_c8_t buffer) = 0;
|
||||
|
||||
void update_spectrum();
|
||||
|
||||
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 };
|
||||
|
||||
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__*/
|
@@ -26,13 +26,25 @@
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "utility_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#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 }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void process(buffer_c8_t buffer, Callback callback) {
|
||||
samples += buffer.count;
|
||||
@@ -40,11 +52,21 @@ public:
|
||||
const size_t report_samples = buffer.sampling_rate * report_interval;
|
||||
const auto report_delta = samples - samples_last_report;
|
||||
if( report_delta >= report_samples ) {
|
||||
const auto idle_ticks = chSysGetIdleThread()->total_ticks;
|
||||
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 baseband_ticks = chThdSelf()->total_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;
|
||||
|
||||
@@ -59,10 +81,15 @@ public:
|
||||
|
||||
private:
|
||||
static constexpr float report_interval { 1.0f };
|
||||
BasebandStatistics statistics;
|
||||
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 };
|
||||
};
|
||||
|
||||
|
@@ -24,7 +24,9 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
template<size_t N>
|
||||
|
84
firmware/baseband/channel_decimator.cpp
Normal file
84
firmware/baseband/channel_decimator.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 "channel_decimator.hpp"
|
||||
|
||||
buffer_c16_t ChannelDecimator::execute_decimation(buffer_c8_t buffer) {
|
||||
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)
|
||||
};
|
||||
|
||||
/* 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] */
|
||||
const auto stage_0_out = translate.execute(buffer, work_baseband_buffer);
|
||||
|
||||
//if( fs_over_4_downconvert ) {
|
||||
// // TODO:
|
||||
//} else {
|
||||
// Won't work until cic_0 will accept input type of buffer_c8_t.
|
||||
// stage_0_out = cic_0.execute(buffer, work_baseband_buffer);
|
||||
//}
|
||||
|
||||
/* 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 8
|
||||
* -> 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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
return cic_4_out;
|
||||
}
|
79
firmware/baseband/channel_decimator.hpp
Normal file
79
firmware/baseband/channel_decimator.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 __CHANNEL_DECIMATOR_H__
|
||||
#define __CHANNEL_DECIMATOR_H__
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
class ChannelDecimator {
|
||||
public:
|
||||
enum class DecimationFactor {
|
||||
By4,
|
||||
By8,
|
||||
By16,
|
||||
By32,
|
||||
};
|
||||
|
||||
constexpr ChannelDecimator(
|
||||
) : decimation_factor { DecimationFactor::By32 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ChannelDecimator(
|
||||
const DecimationFactor decimation_factor
|
||||
) : decimation_factor { decimation_factor }
|
||||
{
|
||||
}
|
||||
|
||||
void set_decimation_factor(const DecimationFactor f) {
|
||||
decimation_factor = f;
|
||||
}
|
||||
|
||||
buffer_c16_t execute(buffer_c8_t buffer) {
|
||||
auto decimated = execute_decimation(buffer);
|
||||
|
||||
return decimated;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<complex16_t, 1024> work_baseband;
|
||||
|
||||
//const bool fs_over_4_downconvert = true;
|
||||
|
||||
dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate;
|
||||
//dsp::decimate::DecimateBy2CIC3 cic_0;
|
||||
dsp::decimate::DecimateBy2CIC3 cic_1;
|
||||
dsp::decimate::DecimateBy2CIC3 cic_2;
|
||||
dsp::decimate::DecimateBy2CIC3 cic_3;
|
||||
dsp::decimate::DecimateBy2CIC3 cic_4;
|
||||
|
||||
DecimationFactor decimation_factor;
|
||||
|
||||
buffer_c16_t execute_decimation(buffer_c8_t buffer);
|
||||
};
|
||||
|
||||
#endif/*__CHANNEL_DECIMATOR_H__*/
|
@@ -29,6 +29,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
class ChannelStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
@@ -47,13 +49,8 @@ public:
|
||||
|
||||
if( count >= samples_per_update ) {
|
||||
const float max_squared_f = max_squared;
|
||||
const float max_db_f = complex16_mag_squared_to_dbv_norm(max_squared_f);
|
||||
const int32_t max_db = max_db_f;
|
||||
const ChannelStatistics statistics {
|
||||
.max_db = max_db,
|
||||
.count = count,
|
||||
};
|
||||
callback(statistics);
|
||||
const int32_t max_db = complex16_mag_squared_to_dbv_norm(max_squared_f);
|
||||
callback({ max_db, count });
|
||||
|
||||
max_squared = 0;
|
||||
count = 0;
|
||||
|
@@ -20,12 +20,3 @@
|
||||
*/
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
|
||||
void ClockRecovery::configure(
|
||||
const uint32_t symbol_rate,
|
||||
const uint32_t sampling_rate
|
||||
) {
|
||||
phase_increment = phase_increment_u32(
|
||||
fractional_symbol_rate(symbol_rate, sampling_rate)
|
||||
);
|
||||
}
|
||||
|
@@ -22,65 +22,157 @@
|
||||
#ifndef __CLOCK_RECOVERY_H__
|
||||
#define __CLOCK_RECOVERY_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
class ClockRecovery {
|
||||
#include "linear_resampler.hpp"
|
||||
|
||||
namespace clock_recovery {
|
||||
|
||||
class GardnerTimingErrorDetector {
|
||||
public:
|
||||
void configure(
|
||||
const uint32_t symbol_rate,
|
||||
const uint32_t sampling_rate
|
||||
);
|
||||
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 execute(
|
||||
void operator()(
|
||||
const float in,
|
||||
SymbolHandler symbol_handler
|
||||
) {
|
||||
const bool phase_0 = (phase_last >> 31) & (!(phase >> 31));
|
||||
const bool phase_180 = (!(phase_last >> 31)) & (phase >> 31);
|
||||
phase_last = phase;
|
||||
phase += phase_increment + phase_adjustment;
|
||||
/* 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( phase_0 || phase_180 ) {
|
||||
t2 = t1;
|
||||
t1 = t0;
|
||||
t0 = in;
|
||||
if( symbol_phase == 0 ) {
|
||||
const auto symbol = t[0];
|
||||
const float lateness = (t[0] - t[2]) * t[1];
|
||||
symbol_handler(symbol, lateness);
|
||||
}
|
||||
|
||||
if( phase_0 ) {
|
||||
symbol_handler(t0);
|
||||
|
||||
const float error = (t0 - t2) * t1;
|
||||
// + error == late == decrease/slow phase
|
||||
// - error == early == increase/fast phase
|
||||
|
||||
error_filtered = 0.75f * error_filtered + 0.25f * error;
|
||||
|
||||
// Correct phase (don't change frequency!)
|
||||
phase_adjustment = -phase_increment * error_filtered / 200.0f;
|
||||
}
|
||||
symbol_phase = (symbol_phase + 1) % samples_per_symbol;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t phase { 0 };
|
||||
uint32_t phase_last { 0 };
|
||||
uint32_t phase_adjustment { 0 };
|
||||
uint32_t phase_increment { 0 };
|
||||
float t0 { 0 };
|
||||
float t1 { 0 };
|
||||
float t2 { 0 };
|
||||
float error_filtered { 0 };
|
||||
std::array<float, 3> t { { 0.0f, 0.0f, 0.0f } };
|
||||
size_t symbol_phase { 0 };
|
||||
};
|
||||
|
||||
static constexpr float fractional_symbol_rate(
|
||||
const uint32_t symbol_rate,
|
||||
const uint32_t sampling_rate
|
||||
) {
|
||||
return float(symbol_rate) / float(sampling_rate);
|
||||
class LinearErrorFilter {
|
||||
public:
|
||||
LinearErrorFilter(
|
||||
const float filter_alpha = 0.95f,
|
||||
const float error_weight = -1.0f
|
||||
) : filter_alpha { filter_alpha },
|
||||
error_weight { error_weight }
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr uint32_t phase_increment_u32(const float fractional_symbol_rate) {
|
||||
return 4294967296.0f * fractional_symbol_rate;
|
||||
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 };
|
||||
};
|
||||
|
||||
class FixedErrorFilter {
|
||||
public:
|
||||
FixedErrorFilter(
|
||||
) {
|
||||
}
|
||||
|
||||
FixedErrorFilter(
|
||||
const float weight
|
||||
) : weight_ { weight }
|
||||
{
|
||||
}
|
||||
|
||||
float operator()(
|
||||
const float lateness
|
||||
) const {
|
||||
return (lateness < 0.0f) ? weight() : -weight();
|
||||
}
|
||||
|
||||
float weight() const {
|
||||
return weight_;
|
||||
}
|
||||
|
||||
private:
|
||||
float weight_ { 1.0f / 16.0f };
|
||||
};
|
||||
|
||||
template<typename ErrorFilter>
|
||||
class ClockRecovery {
|
||||
public:
|
||||
ClockRecovery(
|
||||
const float sampling_rate,
|
||||
const float symbol_rate,
|
||||
ErrorFilter error_filter,
|
||||
std::function<void(const float)> symbol_handler
|
||||
) : symbol_handler { symbol_handler }
|
||||
{
|
||||
configure(sampling_rate, symbol_rate, error_filter);
|
||||
}
|
||||
|
||||
ClockRecovery(
|
||||
std::function<void(const float)> symbol_handler
|
||||
) : symbol_handler { 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 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;
|
||||
std::function<void(const float)> 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 symbol_callback(const float symbol, const float lateness) {
|
||||
symbol_handler(symbol);
|
||||
|
||||
const float adjustment = error_filter(lateness);
|
||||
resampler.advance(adjustment);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace clock_recovery */
|
||||
|
||||
#endif/*__CLOCK_RECOVERY_H__*/
|
||||
|
@@ -196,120 +196,38 @@ buffer_s16_t FIR64AndDecimateBy2Real::execute(
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
#if 0
|
||||
size_t fir_and_decimate_by_2_complex(
|
||||
const complex16_t* const src_start,
|
||||
const size_t src_count,
|
||||
complex16_t* const dst_start,
|
||||
complex16_t* const z,
|
||||
const complex16_t* const taps,
|
||||
const size_t taps_count
|
||||
|
||||
buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
buffer_c16_t src,
|
||||
buffer_c16_t dst
|
||||
) {
|
||||
/* int16_t input (sample count "n" must be multiple of 4)
|
||||
* -> int16_t output, decimated by 2.
|
||||
/* int16_t input (sample count "n" must be multiple of decimation_factor)
|
||||
* -> int16_t output, decimated by decimation_factor.
|
||||
* taps are normalized to 1 << 16 == 1.0.
|
||||
*/
|
||||
auto src_p = src_start;
|
||||
const auto src_end = &src_start[src_count];
|
||||
auto dst_p = dst_start;
|
||||
const auto output_sampling_rate = src.sampling_rate / decimation_factor_;
|
||||
const size_t output_samples = src.count / decimation_factor_;
|
||||
|
||||
sample_t* dst_p = dst.p;
|
||||
const buffer_c16_t result { dst.p, output_samples, output_sampling_rate };
|
||||
|
||||
auto z_p = &z[0];
|
||||
const sample_t* src_p = src.p;
|
||||
size_t outer_count = output_samples;
|
||||
while(outer_count > 0) {
|
||||
/* Put new samples into delay buffer */
|
||||
auto z_new_p = &samples_[taps_count_ - decimation_factor_];
|
||||
for(size_t i=0; i<decimation_factor_; i++) {
|
||||
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
|
||||
}
|
||||
|
||||
while(src_p < src_end) {
|
||||
/* Put two new samples into delay buffer */
|
||||
*__SIMD32(z_p)++ = *__SIMD32(src_p)++;
|
||||
*__SIMD32(z_p)++ = *__SIMD32(src_p)++;
|
||||
size_t loop_count = taps_count_ / 8;
|
||||
auto t_p = &taps_reversed_[0];
|
||||
auto z_p = &samples_[0];
|
||||
|
||||
int64_t t_real = 0;
|
||||
int64_t t_imag = 0;
|
||||
|
||||
auto t_p = &taps[0];
|
||||
|
||||
const auto z_end = &z[taps_count];
|
||||
while(z_p < z_end) {
|
||||
const auto tap0 = *__SIMD32(t_p)++;
|
||||
const auto sample0 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample0, tap0, t_real);
|
||||
t_imag = __SMLALDX(sample0, tap0, t_imag);
|
||||
|
||||
const auto tap1 = *__SIMD32(t_p)++;
|
||||
const auto sample1 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample1, tap1, t_real);
|
||||
t_imag = __SMLALDX(sample1, tap1, t_imag);
|
||||
}
|
||||
|
||||
z_p = &z[0];
|
||||
|
||||
const auto t_end = &taps[taps_count];
|
||||
while(t_p < t_end) {
|
||||
const auto tap0 = *__SIMD32(t_p)++;
|
||||
const auto sample0 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample0, tap0, t_real);
|
||||
t_imag = __SMLALDX(sample0, tap0, t_imag);
|
||||
|
||||
const auto tap1 = *__SIMD32(t_p)++;
|
||||
const auto sample1 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample1, tap1, t_real);
|
||||
t_imag = __SMLALDX(sample1, tap1, t_imag);
|
||||
}
|
||||
|
||||
if( z_p == z_end ) {
|
||||
z_p = &z[0];
|
||||
}
|
||||
|
||||
/* TODO: No rounding taking place here, so might be adding a bit of
|
||||
* noise. Enough to be significant?
|
||||
*/
|
||||
*__SIMD32(dst_p)++ = __PKHBT(
|
||||
t_real / 131072,
|
||||
t_imag / 131072,
|
||||
16
|
||||
);
|
||||
/*
|
||||
*__SIMD32(dst_p)++ = __PKHBT(
|
||||
__SSAT((t_real / 131072), 16),
|
||||
__SSAT((t_imag / 131072), 16),
|
||||
16
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
return src_count / 2;
|
||||
}
|
||||
#endif
|
||||
size_t fir_and_decimate_by_2_complex_fast(
|
||||
const complex16_t* const src_start,
|
||||
const size_t src_count,
|
||||
complex16_t* const dst_start,
|
||||
complex16_t* const z,
|
||||
const complex16_t* const taps,
|
||||
const size_t taps_count
|
||||
) {
|
||||
/* int16_t input (sample count "n" must be multiple of 4)
|
||||
* -> int16_t output, decimated by 2.
|
||||
* taps are normalized to 1 << 16 == 1.0.
|
||||
*/
|
||||
auto src_p = src_start;
|
||||
auto dst_p = dst_start;
|
||||
auto z_new_p = &z[0];
|
||||
auto t_p = &taps[taps_count * 2];
|
||||
|
||||
while(src_p < &src_start[src_count]) {
|
||||
/* Put two new samples into delay buffer */
|
||||
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
|
||||
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
|
||||
|
||||
t_p -= (taps_count + 2);
|
||||
if( z_new_p == &z[taps_count] ) {
|
||||
z_new_p = &z[0];
|
||||
t_p = &taps[taps_count];
|
||||
}
|
||||
|
||||
int64_t t_real = 0;
|
||||
int64_t t_imag = 0;
|
||||
|
||||
auto z_p = &z[0];
|
||||
while(z_p < &z[taps_count]) {
|
||||
while(loop_count > 0) {
|
||||
const auto tap0 = *__SIMD32(t_p)++;
|
||||
const auto sample0 = *__SIMD32(z_p)++;
|
||||
const auto tap1 = *__SIMD32(t_p)++;
|
||||
@@ -345,6 +263,8 @@ size_t fir_and_decimate_by_2_complex_fast(
|
||||
t_imag = __SMLALDX(sample6, tap6, t_imag);
|
||||
t_real = __SMLSLD(sample7, tap7, t_real);
|
||||
t_imag = __SMLALDX(sample7, tap7, t_imag);
|
||||
|
||||
loop_count--;
|
||||
}
|
||||
|
||||
/* TODO: Re-evaluate whether saturation is performed, normalization,
|
||||
@@ -359,9 +279,32 @@ size_t fir_and_decimate_by_2_complex_fast(
|
||||
i_sat,
|
||||
16
|
||||
);
|
||||
|
||||
/* Shift sample buffer left/down by decimation factor. */
|
||||
const size_t unroll_factor = 4;
|
||||
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
|
||||
|
||||
sample_t* t = &samples_[0];
|
||||
const sample_t* s = &samples_[decimation_factor_];
|
||||
|
||||
while(shift_count > 0) {
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
|
||||
while(shift_count > 0) {
|
||||
*(t++) = *(s++);
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
outer_count--;
|
||||
}
|
||||
|
||||
return src_count / 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer_s16_t DecimateBy2CIC4Real::execute(
|
||||
|
@@ -24,6 +24,10 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
@@ -74,50 +78,46 @@ private:
|
||||
const std::array<int16_t, taps_count>& taps;
|
||||
};
|
||||
|
||||
size_t fir_and_decimate_by_2_complex(
|
||||
const complex16_t* const src_start,
|
||||
const size_t src_count,
|
||||
complex16_t* const dst_start,
|
||||
complex16_t* const z,
|
||||
const complex16_t* const taps,
|
||||
const size_t taps_count
|
||||
);
|
||||
|
||||
size_t fir_and_decimate_by_2_complex_fast(
|
||||
const complex16_t* const src_start,
|
||||
const size_t src_count,
|
||||
complex16_t* const dst_start,
|
||||
complex16_t* const z,
|
||||
const complex16_t* const taps,
|
||||
const size_t taps_count
|
||||
);
|
||||
|
||||
template<size_t taps_count>
|
||||
class FIRAndDecimateBy2Complex {
|
||||
class FIRAndDecimateComplex {
|
||||
public:
|
||||
using sample_t = complex16_t;
|
||||
using tap_t = complex16_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.
|
||||
*/
|
||||
FIRAndDecimateBy2Complex(
|
||||
const std::array<int16_t, taps_count>& real_taps
|
||||
FIRAndDecimateComplex(
|
||||
) : taps_count_ { 0 },
|
||||
decimation_factor_ { 1 }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void configure(
|
||||
const T& taps,
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
for(size_t i=0; i<taps_count; i++) {
|
||||
taps[ i] = real_taps[i];
|
||||
taps[taps_count + i] = real_taps[i];
|
||||
}
|
||||
samples_ = std::make_unique<samples_t>(taps.size());
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps.size());
|
||||
taps_count_ = taps.size();
|
||||
decimation_factor_ = decimation_factor;
|
||||
std::reverse_copy(taps.cbegin(), taps.cend(), &taps_reversed_[0]);
|
||||
}
|
||||
|
||||
buffer_c16_t execute(
|
||||
buffer_c16_t src,
|
||||
buffer_c16_t dst
|
||||
) {
|
||||
const auto dst_count = fir_and_decimate_by_2_complex_fast(src.p, src.count, dst.p, z.data(), taps.data(), taps_count);
|
||||
return { dst.p, dst_count, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<complex16_t, taps_count * 2> taps;
|
||||
std::array<complex16_t, taps_count> z;
|
||||
using samples_t = sample_t[];
|
||||
|
||||
std::unique_ptr<samples_t> samples_;
|
||||
std::unique_ptr<taps_t> taps_reversed_;
|
||||
size_t taps_count_;
|
||||
size_t decimation_factor_;
|
||||
};
|
||||
|
||||
class DecimateBy2CIC4Real {
|
||||
|
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "complex.hpp"
|
||||
#include "fxpt_atan2.hpp"
|
||||
#include "utility_m4.hpp"
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
|
@@ -55,9 +55,13 @@ public:
|
||||
buffer_s16_t dst
|
||||
);
|
||||
|
||||
void configure(const float sampling_rate, const float deviation_hz) {
|
||||
k = static_cast<float>(32767.0f / (2.0 * pi * deviation_hz / sampling_rate));
|
||||
}
|
||||
|
||||
private:
|
||||
complex16_t::rep_type z_;
|
||||
const float k;
|
||||
float k;
|
||||
};
|
||||
|
||||
} /* namespace demodulate */
|
||||
|
37
firmware/baseband/dsp_iir_config.hpp
Normal file
37
firmware/baseband/dsp_iir_config.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 __DSP_IIR_CONFIG_H__
|
||||
#define __DSP_IIR_CONFIG_H__
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
|
||||
constexpr iir_biquad_config_t audio_hpf_config {
|
||||
{ 0.93346032f, -1.86687724f, 0.93346032f },
|
||||
{ 1.0f , -1.97730264f, 0.97773668f }
|
||||
};
|
||||
|
||||
constexpr iir_biquad_config_t non_audio_hpf_config {
|
||||
{ 0.51891061f, -0.95714180f, 0.51891061f },
|
||||
{ 1.0f , -0.79878302f, 0.43960231f }
|
||||
};
|
||||
|
||||
#endif/*__DSP_IIR_CONFIG_H__*/
|
45
firmware/baseband/dsp_squelch.cpp
Normal file
45
firmware/baseband/dsp_squelch.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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_squelch.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
bool FMSquelch::execute(buffer_s16_t audio) {
|
||||
// TODO: No hard-coded array size.
|
||||
std::array<int16_t, N> squelch_energy_buffer;
|
||||
const buffer_s16_t squelch_energy {
|
||||
squelch_energy_buffer.data(),
|
||||
squelch_energy_buffer.size()
|
||||
};
|
||||
non_audio_hpf.execute(audio, squelch_energy);
|
||||
|
||||
uint64_t 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;
|
||||
}
|
||||
}
|
||||
|
||||
return (max_squared < (threshold * threshold));
|
||||
}
|
45
firmware/baseband/dsp_squelch.hpp
Normal file
45
firmware/baseband/dsp_squelch.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 __DSP_SQUELCH_H__
|
||||
#define __DSP_SQUELCH_H__
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class FMSquelch {
|
||||
public:
|
||||
bool execute(buffer_s16_t audio);
|
||||
|
||||
private:
|
||||
static constexpr size_t N = 32;
|
||||
static constexpr int16_t threshold = 3072;
|
||||
|
||||
// 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 };
|
||||
};
|
||||
|
||||
#endif/*__DSP_SQUELCH_H__*/
|
@@ -19,31 +19,36 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __ACCESS_CODE_CORRELATOR_H__
|
||||
#define __ACCESS_CODE_CORRELATOR_H__
|
||||
#include "irq_ipc_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
class AccessCodeCorrelator {
|
||||
public:
|
||||
void configure(
|
||||
const uint32_t new_code,
|
||||
const size_t new_code_length,
|
||||
const size_t new_maximum_hamming_distance
|
||||
);
|
||||
#include "event_m4.hpp"
|
||||
|
||||
bool execute(const uint_fast8_t in);
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
private:
|
||||
uint32_t code { 0 };
|
||||
uint32_t mask { 0 };
|
||||
uint32_t history { 0 };
|
||||
size_t maximum_hamming_distance { 0 };
|
||||
void m0apptxevent_interrupt_enable() {
|
||||
nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
static constexpr uint32_t mask_value(const size_t n) {
|
||||
return static_cast<uint32_t>((1ULL << n) - 1ULL);
|
||||
}
|
||||
};
|
||||
void m0apptxevent_interrupt_disable() {
|
||||
nvicDisableVector(M0CORE_IRQn);
|
||||
}
|
||||
|
||||
#endif/*__ACCESS_CODE_CORRELATOR_H__*/
|
||||
extern "C" {
|
||||
|
||||
CH_IRQ_HANDLER(MAPP_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
chSysLockFromIsr();
|
||||
events_flag_isr(EVT_MASK_BASEBAND);
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
creg::m0apptxevent::clear();
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
}
|
@@ -19,26 +19,10 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "access_code_correlator.hpp"
|
||||
#ifndef __IRQ_IPC_M4_H__
|
||||
#define __IRQ_IPC_M4_H__
|
||||
|
||||
void AccessCodeCorrelator::configure(
|
||||
const uint32_t new_code,
|
||||
const size_t new_code_length,
|
||||
const size_t new_maximum_hamming_distance
|
||||
) {
|
||||
if( new_code_length <= 32 ) {
|
||||
code = new_code;
|
||||
mask = mask_value(new_code_length);
|
||||
maximum_hamming_distance = new_maximum_hamming_distance;
|
||||
}
|
||||
}
|
||||
void m0apptxevent_interrupt_enable();
|
||||
void m0apptxevent_interrupt_disable();
|
||||
|
||||
bool AccessCodeCorrelator::execute(
|
||||
const uint_fast8_t in
|
||||
) {
|
||||
history = (history << 1) | (in & 1);
|
||||
const auto delta_bits = (history ^ code) & mask;
|
||||
//const size_t count = __builtin_popcountll(delta_bits);
|
||||
const size_t count = __builtin_popcountl(delta_bits);
|
||||
return (count <= maximum_hamming_distance);
|
||||
}
|
||||
#endif/*__IRQ_IPC_M4_H__*/
|
69
firmware/baseband/linear_resampler.hpp
Normal file
69
firmware/baseband/linear_resampler.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 __LINEAR_RESAMPLER_H__
|
||||
#define __LINEAR_RESAMPLER_H__
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace interpolation */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__LINEAR_RESAMPLER_H__*/
|
File diff suppressed because it is too large
Load Diff
89
firmware/baseband/matched_filter.cpp
Normal file
89
firmware/baseband/matched_filter.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 "matched_filter.hpp"
|
||||
|
||||
namespace dsp {
|
||||
namespace matched_filter {
|
||||
|
||||
bool MatchedFilter::execute_once(
|
||||
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];
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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--;
|
||||
}
|
||||
|
||||
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
|
||||
while(shift_count > 0) {
|
||||
*t++ = *s++;
|
||||
shift_count--;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace matched_filter */
|
||||
} /* namespace dsp */
|
101
firmware/baseband/matched_filter.hpp
Normal file
101
firmware/baseband/matched_filter.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 __MATCHED_FILTER_H__
|
||||
#define __MATCHED_FILTER_H__
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <complex>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace dsp {
|
||||
namespace matched_filter {
|
||||
|
||||
// This filter contains "magic" (optimizations) that expect the taps to
|
||||
// combine a low-pass filter with a complex sinusoid that performs shifting of
|
||||
// the input signal to 0Hz/DC. This also means that the taps length must be
|
||||
// a multiple of the complex sinusoid period.
|
||||
|
||||
class MatchedFilter {
|
||||
public:
|
||||
using sample_t = std::complex<float>;
|
||||
using tap_t = std::complex<float>;
|
||||
|
||||
using taps_t = tap_t[];
|
||||
|
||||
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
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps.size());
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps.size());
|
||||
taps_count_ = taps.size();
|
||||
decimation_factor_ = decimation_factor;
|
||||
std::reverse_copy(taps.cbegin(), taps.cend(), &taps_reversed_[0]);
|
||||
}
|
||||
|
||||
bool execute_once(const sample_t input);
|
||||
|
||||
float get_output() const {
|
||||
return output;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
void shift_by_decimation_factor();
|
||||
|
||||
void advance_decimation_phase() {
|
||||
decimation_phase = (decimation_phase + 1) % decimation_factor_;
|
||||
}
|
||||
|
||||
bool is_new_decimation_cycle() const {
|
||||
return (decimation_phase == 0);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace matched_filter */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__MATCHED_FILTER_H__*/
|
@@ -41,3 +41,7 @@
|
||||
//#define LPC_ADC1_IRQ_PRIORITY 4
|
||||
|
||||
#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4
|
||||
|
||||
/* M4 is initialized by M0, which has already started PLL1 */
|
||||
#define LPC43XX_M4_CLK 200000000
|
||||
#define LPC43XX_M4_CLK_SRC 0x09
|
@@ -20,15 +20,3 @@
|
||||
*/
|
||||
|
||||
#include "packet_builder.hpp"
|
||||
|
||||
void PacketBuilder::configure(size_t new_payload_length) {
|
||||
if( new_payload_length <= payload.size() ) {
|
||||
payload_length = new_payload_length;
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
|
||||
void PacketBuilder::reset_state() {
|
||||
bits_received = 0;
|
||||
state = State::AccessCodeSearch;
|
||||
}
|
||||
|
@@ -25,30 +25,62 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
|
||||
#include "bit_pattern.hpp"
|
||||
|
||||
template<typename PreambleMatcher, typename UnstuffMatcher, typename EndMatcher>
|
||||
class PacketBuilder {
|
||||
public:
|
||||
void configure(size_t new_payload_length);
|
||||
using PayloadType = std::bitset<1024>;
|
||||
using PayloadHandlerFunc = std::function<void(const PayloadType& payload, const size_t bits_received)>;
|
||||
|
||||
template<typename PayloadHandler>
|
||||
void execute(
|
||||
const uint_fast8_t symbol,
|
||||
const bool access_code_found,
|
||||
PayloadHandler payload_handler
|
||||
PacketBuilder(
|
||||
const PreambleMatcher preamble_matcher,
|
||||
const UnstuffMatcher unstuff_matcher,
|
||||
const EndMatcher end_matcher,
|
||||
const PayloadHandlerFunc payload_handler
|
||||
) : payload_handler { 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;
|
||||
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void execute(
|
||||
const uint_fast8_t symbol
|
||||
) {
|
||||
bit_history.add(symbol);
|
||||
|
||||
switch(state) {
|
||||
case State::AccessCodeSearch:
|
||||
if( access_code_found ) {
|
||||
case State::Preamble:
|
||||
if( preamble(bit_history, bits_received) ) {
|
||||
state = State::Payload;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Payload:
|
||||
if( bits_received < payload_length ) {
|
||||
if( !unstuff(bit_history, bits_received) ) {
|
||||
payload[bits_received++] = symbol;
|
||||
} else {
|
||||
}
|
||||
|
||||
if( end(bit_history, bits_received) ) {
|
||||
payload_handler(payload, bits_received);
|
||||
reset_state();
|
||||
} else {
|
||||
if( packet_truncated() ) {
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -60,16 +92,29 @@ public:
|
||||
|
||||
private:
|
||||
enum State {
|
||||
AccessCodeSearch,
|
||||
Preamble,
|
||||
Payload,
|
||||
};
|
||||
|
||||
size_t payload_length { 0 };
|
||||
size_t bits_received { 0 };
|
||||
State state { State::AccessCodeSearch };
|
||||
std::bitset<256> payload;
|
||||
bool packet_truncated() const {
|
||||
return bits_received >= payload.size();
|
||||
}
|
||||
|
||||
void reset_state();
|
||||
const PayloadHandlerFunc payload_handler;
|
||||
|
||||
BitHistory bit_history;
|
||||
PreambleMatcher preamble;
|
||||
UnstuffMatcher unstuff;
|
||||
EndMatcher end;
|
||||
|
||||
size_t bits_received { 0 };
|
||||
State state { State::Preamble };
|
||||
PayloadType payload;
|
||||
|
||||
void reset_state() {
|
||||
bits_received = 0;
|
||||
state = State::Preamble;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__PACKET_BUILDER_H__*/
|
||||
|
75
firmware/baseband/proc_ais.cpp
Normal file
75
firmware/baseband/proc_ais.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 "proc_ais.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "i2s.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
void AISProcessor::execute(buffer_c8_t buffer) {
|
||||
/* 2.4576MHz, 2048 samples */
|
||||
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
/* 76.8kHz, 64 samples */
|
||||
feed_channel_stats(decimator_out);
|
||||
/* No spectrum display while AIS decoding.
|
||||
feed_channel_spectrum(
|
||||
channel,
|
||||
decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized,
|
||||
decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized
|
||||
);
|
||||
*/
|
||||
|
||||
for(size_t i=0; i<decimator_out.count; i++) {
|
||||
// TODO: No idea why implicit cast int16_t->float is not allowed.
|
||||
const std::complex<float> sample {
|
||||
static_cast<float>(decimator_out.p[i].real()),
|
||||
static_cast<float>(decimator_out.p[i].imag())
|
||||
};
|
||||
if( mf.execute_once(sample) ) {
|
||||
clock_recovery(mf.get_output());
|
||||
}
|
||||
}
|
||||
|
||||
i2s::i2s0::tx_mute();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
packet_builder.execute(decoded_symbol);
|
||||
}
|
||||
|
||||
void AISProcessor::payload_handler(
|
||||
const std::bitset<1024>& payload,
|
||||
const size_t bits_received
|
||||
) {
|
||||
AISPacketMessage message;
|
||||
message.packet.payload = payload;
|
||||
message.packet.bits_received = bits_received;
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
70
firmware/baseband/proc_ais.hpp
Normal file
70
firmware/baseband/proc_ais.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 __PROC_AIS_H__
|
||||
#define __PROC_AIS_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "matched_filter.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "symbol_coding.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
#include "ais_baseband.hpp"
|
||||
|
||||
class AISProcessor : public BasebandProcessor {
|
||||
public:
|
||||
using payload_t = std::bitset<1024>;
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator { ChannelDecimator::DecimationFactor::By32 };
|
||||
dsp::matched_filter::MatchedFilter mf { baseband::ais::rrc_taps_76k8_4t_p, 4 };
|
||||
|
||||
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 payload_t& payload, const size_t bits_received) {
|
||||
this->payload_handler(payload, bits_received);
|
||||
}
|
||||
};
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void payload_handler(const payload_t& payload, const size_t bits_received);
|
||||
};
|
||||
|
||||
#endif/*__PROC_AIS_H__*/
|
59
firmware/baseband/proc_am_audio.cpp
Normal file
59
firmware/baseband/proc_am_audio.cpp
Normal file
@@ -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 "proc_am_audio.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void NarrowbandAMAudio::execute(buffer_c8_t buffer) {
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
const buffer_c16_t work_baseband_buffer {
|
||||
(complex16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 96kHz complex<int16_t>[64]
|
||||
* -> FIR filter, <?kHz (0.???fs) pass, gain 1.0
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto channel = channel_filter.execute(decimator_out, work_baseband_buffer);
|
||||
|
||||
// TODO: Feed channel_stats post-decimation data?
|
||||
feed_channel_stats(channel);
|
||||
feed_channel_spectrum(
|
||||
channel,
|
||||
decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized,
|
||||
decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized
|
||||
);
|
||||
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 48kHz complex<int16_t>[32]
|
||||
* -> AM demodulation
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto audio = demod.execute(channel, work_audio_buffer);
|
||||
|
||||
audio_hpf.execute_in_place(audio);
|
||||
fill_audio_buffer(audio);
|
||||
}
|
51
firmware/baseband/proc_am_audio.hpp
Normal file
51
firmware/baseband/proc_am_audio.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 __PROC_AM_AUDIO_H__
|
||||
#define __PROC_AM_AUDIO_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
|
||||
class NarrowbandAMAudio : public BasebandProcessor {
|
||||
public:
|
||||
NarrowbandAMAudio() {
|
||||
decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By32);
|
||||
channel_filter.configure(channel_filter_taps.taps, 2);
|
||||
}
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator;
|
||||
const fir_taps_real<64>& channel_filter_taps = taps_64_lp_031_070_tfilter;
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
dsp::demodulate::AM demod;
|
||||
IIRBiquadFilter audio_hpf { audio_hpf_config };
|
||||
};
|
||||
|
||||
#endif/*__PROC_AM_AUDIO_H__*/
|
74
firmware/baseband/proc_nfm_audio.cpp
Normal file
74
firmware/baseband/proc_nfm_audio.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 "proc_nfm_audio.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
void NarrowbandFMAudio::execute(buffer_c8_t buffer) {
|
||||
/* Called every 2048/3072000 second -- 1500Hz. */
|
||||
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
const buffer_c16_t work_baseband_buffer {
|
||||
(complex16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 96kHz complex<int16_t>[64]
|
||||
* -> FIR filter, <6kHz (0.063fs) pass, gain 1.0
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto channel = channel_filter.execute(decimator_out, work_baseband_buffer);
|
||||
|
||||
// TODO: Feed channel_stats post-decimation data?
|
||||
feed_channel_stats(channel);
|
||||
feed_channel_spectrum(
|
||||
channel,
|
||||
decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized,
|
||||
decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized
|
||||
);
|
||||
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 48kHz complex<int16_t>[32]
|
||||
* -> FM demodulation
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto audio = demod.execute(channel, work_audio_buffer);
|
||||
|
||||
static uint64_t audio_present_history = 0;
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
const bool audio_present = (audio_present_history != 0);
|
||||
|
||||
if( !audio_present ) {
|
||||
// Zero audio buffer.
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
audio_hpf.execute_in_place(audio);
|
||||
fill_audio_buffer(audio);
|
||||
}
|
54
firmware/baseband/proc_nfm_audio.hpp
Normal file
54
firmware/baseband/proc_nfm_audio.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 __PROC_NFM_AUDIO_H__
|
||||
#define __PROC_NFM_AUDIO_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
class NarrowbandFMAudio : public BasebandProcessor {
|
||||
public:
|
||||
NarrowbandFMAudio() {
|
||||
decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By32);
|
||||
channel_filter.configure(channel_filter_taps.taps, 2);
|
||||
}
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator;
|
||||
const fir_taps_real<64>& channel_filter_taps = taps_64_lp_042_078_tfilter;
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
dsp::demodulate::FM demod { 48000, 7500 };
|
||||
|
||||
IIRBiquadFilter audio_hpf { audio_hpf_config };
|
||||
FMSquelch squelch;
|
||||
};
|
||||
|
||||
#endif/*__PROC_NFM_AUDIO_H__*/
|
73
firmware/baseband/proc_tpms.cpp
Normal file
73
firmware/baseband/proc_tpms.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 "proc_tpms.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "i2s.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
void TPMSProcessor::execute(buffer_c8_t buffer) {
|
||||
/* 2.4576MHz, 2048 samples */
|
||||
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
/* 76.8kHz, 64 samples */
|
||||
feed_channel_stats(decimator_out);
|
||||
/* No spectrum display while FSK decoding.
|
||||
feed_channel_spectrum(
|
||||
channel,
|
||||
decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized,
|
||||
decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized
|
||||
);
|
||||
*/
|
||||
|
||||
for(size_t i=0; i<decimator_out.count; i++) {
|
||||
// TODO: No idea why implicit cast int16_t->float is not allowed.
|
||||
const std::complex<float> sample {
|
||||
static_cast<float>(decimator_out.p[i].real()),
|
||||
static_cast<float>(decimator_out.p[i].imag())
|
||||
};
|
||||
if( mf.execute_once(sample) ) {
|
||||
clock_recovery(mf.get_output());
|
||||
}
|
||||
}
|
||||
|
||||
i2s::i2s0::tx_mute();
|
||||
}
|
||||
|
||||
void TPMSProcessor::consume_symbol(
|
||||
const float raw_symbol
|
||||
) {
|
||||
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
|
||||
packet_builder.execute(sliced_symbol);
|
||||
}
|
||||
|
||||
void TPMSProcessor::payload_handler(
|
||||
const std::bitset<1024>& payload,
|
||||
const size_t bits_received
|
||||
) {
|
||||
TPMSPacketMessage message;
|
||||
message.packet.payload = payload;
|
||||
message.packet.bits_received = bits_received;
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
91
firmware/baseband/proc_tpms.hpp
Normal file
91
firmware/baseband/proc_tpms.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 __PROC_TPMS_H__
|
||||
#define __PROC_TPMS_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "matched_filter.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "symbol_coding.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
struct NeverMatch {
|
||||
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;
|
||||
}
|
||||
|
||||
const size_t length;
|
||||
};
|
||||
|
||||
// Translate+rectangular filter
|
||||
// sample=153.6k, deviation=38400, symbol=19200
|
||||
// Length: 8 taps, 1 symbols, 2 cycles of sinusoid
|
||||
constexpr std::array<std::complex<float>, 8> rect_taps_153k6_1t_p { {
|
||||
{ 1.2500000000e-01f, 0.0000000000e+00f }, { 7.6540424947e-18f, 1.2500000000e-01f },
|
||||
{ -1.2500000000e-01f, 1.5308084989e-17f }, { -2.2962127484e-17f, -1.2500000000e-01f },
|
||||
{ 1.2500000000e-01f, -3.0616169979e-17f }, { 3.8270212473e-17f, 1.2500000000e-01f },
|
||||
{ -1.2500000000e-01f, 4.5924254968e-17f }, { -5.3578297463e-17f, -1.2500000000e-01f },
|
||||
} };
|
||||
|
||||
class TPMSProcessor : public BasebandProcessor {
|
||||
public:
|
||||
using payload_t = std::bitset<1024>;
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator { ChannelDecimator::DecimationFactor::By16 };
|
||||
dsp::matched_filter::MatchedFilter mf { rect_taps_153k6_1t_p, 4 };
|
||||
|
||||
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
|
||||
38400, 19200, { 0.0555f },
|
||||
[this](const float symbol) { this->consume_symbol(symbol); }
|
||||
};
|
||||
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
|
||||
{ 0b010101010101010101010101010110, 30, 1 },
|
||||
{ },
|
||||
{ 256 },
|
||||
[this](const payload_t& payload, const size_t bits_received) {
|
||||
this->payload_handler(payload, bits_received);
|
||||
}
|
||||
};
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void payload_handler(const payload_t& payload, const size_t bits_received);
|
||||
};
|
||||
|
||||
#endif/*__PROC_TPMS_H__*/
|
72
firmware/baseband/proc_wfm_audio.cpp
Normal file
72
firmware/baseband/proc_wfm_audio.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 "proc_wfm_audio.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void WidebandFMAudio::execute(buffer_c8_t buffer) {
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
auto channel = decimator_out;
|
||||
|
||||
// TODO: Feed channel_stats post-decimation data?
|
||||
feed_channel_stats(channel);
|
||||
//feed_channel_spectrum(channel);
|
||||
|
||||
/* 768kHz complex<int16_t>[512]
|
||||
* -> FM demodulation
|
||||
* -> 768kHz int16_t[512] */
|
||||
/* TODO: To improve adjacent channel rejection, implement complex channel filter:
|
||||
* pass < +/- 100kHz, stop > +/- 200kHz
|
||||
*/
|
||||
|
||||
auto audio_oversampled = demod.execute(decimator_out, work_audio_buffer);
|
||||
|
||||
/* 768kHz int16_t[512]
|
||||
* -> 4th order CIC decimation by 2, gain of 1
|
||||
* -> 384kHz int16_t[256] */
|
||||
auto audio_8fs = audio_dec_1.execute(audio_oversampled, work_audio_buffer);
|
||||
|
||||
/* 384kHz int16_t[256]
|
||||
* -> 4th order CIC decimation by 2, gain of 1
|
||||
* -> 192kHz int16_t[128] */
|
||||
auto audio_4fs = audio_dec_2.execute(audio_8fs, work_audio_buffer);
|
||||
|
||||
/* 192kHz int16_t[128]
|
||||
* -> 4th order CIC decimation by 2, gain of 1
|
||||
* -> 96kHz int16_t[64] */
|
||||
auto audio_2fs = audio_dec_3.execute(audio_4fs, work_audio_buffer);
|
||||
|
||||
/* 96kHz int16_t[64]
|
||||
* -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto audio = audio_filter.execute(audio_2fs, work_audio_buffer);
|
||||
|
||||
/* -> 48kHz int16_t[32] */
|
||||
audio_hpf.execute_in_place(audio);
|
||||
fill_audio_buffer(audio);
|
||||
}
|
55
firmware/baseband/proc_wfm_audio.hpp
Normal file
55
firmware/baseband/proc_wfm_audio.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 __PROC_WFM_AUDIO_H__
|
||||
#define __PROC_WFM_AUDIO_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
|
||||
class WidebandFMAudio : public BasebandProcessor {
|
||||
public:
|
||||
WidebandFMAudio() {
|
||||
decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By4);
|
||||
}
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator;
|
||||
|
||||
dsp::demodulate::FM demod { 768000, 75000 };
|
||||
dsp::decimate::DecimateBy2CIC4Real audio_dec_1;
|
||||
dsp::decimate::DecimateBy2CIC4Real audio_dec_2;
|
||||
dsp::decimate::DecimateBy2CIC4Real audio_dec_3;
|
||||
const fir_taps_real<64>& audio_filter_taps = taps_64_lp_156_198;
|
||||
dsp::decimate::FIR64AndDecimateBy2Real audio_filter { audio_filter_taps.taps };
|
||||
|
||||
IIRBiquadFilter audio_hpf { audio_hpf_config };
|
||||
};
|
||||
|
||||
#endif/*__PROC_WFM_AUDIO_H__*/
|
69
firmware/baseband/proc_wideband_spectrum.cpp
Normal file
69
firmware/baseband/proc_wideband_spectrum.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 "proc_wideband_spectrum.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "i2s.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "dsp_fft.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <array>
|
||||
|
||||
void WidebandSpectrum::execute(buffer_c8_t buffer) {
|
||||
// 2048 complex8_t samples per buffer.
|
||||
// 102.4us per buffer. 20480 instruction cycles per buffer.
|
||||
|
||||
static int phase = 0;
|
||||
|
||||
if( phase == 0 ) {
|
||||
std::fill(spectrum.begin(), spectrum.end(), 0);
|
||||
}
|
||||
|
||||
if( (phase & 7) == 0 ) {
|
||||
// TODO: Removed window-presum windowing, due to lack of available code RAM.
|
||||
// TODO: Apply window to improve spectrum bin sidelobes.
|
||||
for(size_t i=0; i<channel_spectrum.size(); i++) {
|
||||
spectrum[i] += std::complex<float> { buffer.p[i].real(), buffer.p[i].imag() };
|
||||
}
|
||||
}
|
||||
|
||||
if( phase == 23 ) {
|
||||
if( channel_spectrum_request_update == false ) {
|
||||
fft_swap(spectrum, channel_spectrum);
|
||||
channel_spectrum_sampling_rate = buffer.sampling_rate;
|
||||
channel_filter_pass_frequency = 0;
|
||||
channel_filter_stop_frequency = 0;
|
||||
channel_spectrum_request_update = true;
|
||||
events_flag(EVT_MASK_SPECTRUM);
|
||||
phase = 0;
|
||||
}
|
||||
} else {
|
||||
phase++;
|
||||
}
|
||||
|
||||
i2s::i2s0::tx_mute();
|
||||
}
|
41
firmware/baseband/proc_wideband_spectrum.hpp
Normal file
41
firmware/baseband/proc_wideband_spectrum.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 __PROC_WIDEBAND_SPECTRUM_H__
|
||||
#define __PROC_WIDEBAND_SPECTRUM_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <complex>
|
||||
|
||||
class WidebandSpectrum : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
|
||||
private:
|
||||
size_t sample_count = 0;
|
||||
|
||||
std::array<std::complex<float>, 256> spectrum;
|
||||
};
|
||||
|
||||
#endif/*__PROC_WIDEBAND_SPECTRUM_H__*/
|
@@ -37,6 +37,12 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if( statistics.count == 0 ) {
|
||||
const auto value_0 = *p;
|
||||
statistics.min = value_0;
|
||||
statistics.max = value_0;
|
||||
}
|
||||
|
||||
const auto end = &p[buffer.count];
|
||||
while(p < end) {
|
||||
const uint32_t value = *(p++);
|
||||
@@ -58,9 +64,6 @@ public:
|
||||
callback(statistics);
|
||||
statistics.accumulator = 0;
|
||||
statistics.count = 0;
|
||||
const auto value_0 = *p;
|
||||
statistics.min = value_0;
|
||||
statistics.max = value_0;
|
||||
}
|
||||
}
|
||||
|
||||
|
44
firmware/baseband/symbol_coding.hpp
Normal file
44
firmware/baseband/symbol_coding.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 __SYMBOL_CODING_H__
|
||||
#define __SYMBOL_CODING_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace symbol_coding {
|
||||
|
||||
class NRZIDecoder {
|
||||
public:
|
||||
uint_fast8_t operator()(const uint_fast8_t symbol) {
|
||||
const auto out = (~(symbol ^ last)) & 1;
|
||||
last = symbol;
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
uint_fast8_t last { 0 };
|
||||
};
|
||||
|
||||
} /* namespace symbol_coding */
|
||||
|
||||
#endif/*__SYMBOL_CODING_H__*/
|
Reference in New Issue
Block a user