Add software
This commit is contained in:
544
Software/portapack-mayhem/firmware/baseband/CMakeLists.txt
Normal file
544
Software/portapack-mayhem/firmware/baseband/CMakeLists.txt
Normal file
@ -0,0 +1,544 @@
|
||||
#
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
# Build global options
|
||||
# NOTE: Can be overridden externally.
|
||||
#
|
||||
|
||||
enable_language(C CXX ASM)
|
||||
|
||||
project(baseband_shared)
|
||||
|
||||
# Compiler options here.
|
||||
set(USE_OPT "-O3 -g -falign-functions=16 -fno-math-errno --specs=nano.specs")
|
||||
|
||||
# C specific options here (added to USE_OPT).
|
||||
set(USE_COPT "-std=gnu99")
|
||||
|
||||
# C++ specific options here (added to USE_OPT).
|
||||
set(USE_CPPOPT "-std=c++17 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized")
|
||||
|
||||
# Enable this if you want the linker to remove unused code and data
|
||||
set(USE_LINK_GC yes)
|
||||
|
||||
# Linker extra options here.
|
||||
set(USE_LDOPT)
|
||||
|
||||
# Enable this if you want link time optimizations (LTO)
|
||||
set(USE_LTO no)
|
||||
|
||||
# If enabled, this option allows to compile the application in THUMB mode.
|
||||
set(USE_THUMB yes)
|
||||
|
||||
# Enable this if you want to see the full log while compiling.
|
||||
set(USE_VERBOSE_COMPILE no)
|
||||
|
||||
#
|
||||
# Build global options
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Architecture or project specific options
|
||||
#
|
||||
|
||||
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
|
||||
set(USE_FPU hard)
|
||||
|
||||
#
|
||||
# Architecture or project specific options
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Project, sources and paths
|
||||
#
|
||||
|
||||
# Imported source files and paths
|
||||
include(${CHIBIOS_PORTAPACK}/boards/PORTAPACK_BASEBAND/board.cmake)
|
||||
include(${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M4/platform.cmake)
|
||||
include(${CHIBIOS}/os/hal/hal.cmake)
|
||||
include(${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M4/port.cmake)
|
||||
include(${CHIBIOS}/os/kernel/kernel.cmake)
|
||||
|
||||
include(${CHIBIOS}/test/test.cmake)
|
||||
|
||||
# Define linker script file here
|
||||
set(LDSCRIPT ${PORTLD}/LPC43xx_M4.ld)
|
||||
|
||||
# C sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
# setting.
|
||||
set(CSRC
|
||||
${PORTSRC}
|
||||
${KERNSRC}
|
||||
${TESTSRC}
|
||||
${HALSRC}
|
||||
${PLATFORMSRC}
|
||||
${BOARDSRC}
|
||||
)
|
||||
|
||||
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
# setting.
|
||||
set(CPPSRC
|
||||
baseband.cpp
|
||||
${COMMON}/message_queue.cpp
|
||||
${COMMON}/event.cpp
|
||||
event_m4.cpp
|
||||
${COMMON}/thread_wait.cpp
|
||||
${COMMON}/gpdma.cpp
|
||||
baseband_dma.cpp
|
||||
${COMMON}/baseband_sgpio.cpp
|
||||
${COMMON}/portapack_shared_memory.cpp
|
||||
${COMMON}/buffer.cpp
|
||||
baseband_thread.cpp
|
||||
baseband_processor.cpp
|
||||
baseband_stats_collector.cpp
|
||||
dsp_decimate.cpp
|
||||
dsp_demodulate.cpp
|
||||
dsp_hilbert.cpp
|
||||
dsp_modulate.cpp
|
||||
dsp_goertzel.cpp
|
||||
matched_filter.cpp
|
||||
spectrum_collector.cpp
|
||||
tv_collector.cpp
|
||||
stream_input.cpp
|
||||
stream_output.cpp
|
||||
dsp_squelch.cpp
|
||||
clock_recovery.cpp
|
||||
packet_builder.cpp
|
||||
${COMMON}/dsp_fft.cpp
|
||||
${COMMON}/dsp_fir_taps.cpp
|
||||
${COMMON}/dsp_iir.cpp
|
||||
${COMMON}/dsp_sos.cpp
|
||||
fxpt_atan2.cpp
|
||||
rssi.cpp
|
||||
rssi_dma.cpp
|
||||
rssi_thread.cpp
|
||||
audio_compressor.cpp
|
||||
audio_output.cpp
|
||||
audio_input.cpp
|
||||
audio_dma.cpp
|
||||
audio_stats_collector.cpp
|
||||
${COMMON}/utility.cpp
|
||||
${COMMON}/chibios_cpp.cpp
|
||||
${COMMON}/debug.cpp
|
||||
${COMMON}/gcc.cpp
|
||||
tone_gen.cpp
|
||||
)
|
||||
|
||||
# C sources to be compiled in ARM mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(ACSRC)
|
||||
|
||||
# C++ sources to be compiled in ARM mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(ACPPSRC)
|
||||
|
||||
# C sources to be compiled in THUMB mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(TCSRC)
|
||||
|
||||
# C sources to be compiled in THUMB mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
set(TCPPSRC)
|
||||
|
||||
# List ASM source files here
|
||||
set(ASMSRC ${PORTASM})
|
||||
|
||||
set(INCDIR ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC}
|
||||
${HALINC} ${PLATFORMINC} ${BOARDINC}
|
||||
${CHIBIOS}/os/various
|
||||
)
|
||||
|
||||
#
|
||||
# Project, sources and paths
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Compiler settings
|
||||
#
|
||||
|
||||
set(MCU cortex-m4)
|
||||
|
||||
# ARM-specific options here
|
||||
set(AOPT)
|
||||
|
||||
# THUMB-specific options here
|
||||
set(TOPT "-mthumb -DTHUMB")
|
||||
|
||||
# Define C warning options here
|
||||
set(CWARN "-Wall -Wextra -Wstrict-prototypes")
|
||||
|
||||
# Define C++ warning options here
|
||||
set(CPPWARN "-Wall -Wextra")
|
||||
|
||||
#
|
||||
# Compiler settings
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Start of default section
|
||||
#
|
||||
|
||||
# List all default C defines here, like -D_DEBUG=1
|
||||
# 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
|
||||
set(DDEFS "-DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"'")
|
||||
|
||||
# List all default ASM defines here, like -D_DEBUG=1
|
||||
set(DADEFS)
|
||||
|
||||
# List all default directories to look for include files here
|
||||
set(DINCDIR)
|
||||
|
||||
# List the default directory to look for the libraries here
|
||||
set(DLIBDIR)
|
||||
|
||||
# List all default libraries here
|
||||
set(DLIBS)
|
||||
|
||||
#
|
||||
# End of default section
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Start of user section
|
||||
#
|
||||
|
||||
# List all user C define here, like -D_DEBUG=1
|
||||
set(UDEFS)
|
||||
|
||||
# Define ASM defines here
|
||||
set(UADEFS)
|
||||
|
||||
# List all user directories here
|
||||
set(UINCDIR)
|
||||
|
||||
# List the user directory to look for the libraries here
|
||||
set(ULIBDIR)
|
||||
|
||||
# List all user libraries here
|
||||
set(ULIBS)
|
||||
|
||||
#
|
||||
# End of user defines
|
||||
##############################################################################
|
||||
|
||||
set(RULESPATH ${CHIBIOS}/os/ports/GCC/ARMCMx)
|
||||
include(${RULESPATH}/rules.cmake)
|
||||
|
||||
#######################################################################
|
||||
|
||||
add_library(${PROJECT_NAME} OBJECT ${CSRC} ${CPPSRC} ${ASMSRC})
|
||||
include_directories(. ${INCDIR})
|
||||
|
||||
#######################################################################
|
||||
|
||||
set(BASEBAND_IMAGES)
|
||||
|
||||
macro(DeclareTargets chunk_tag name)
|
||||
project("baseband_${name}")
|
||||
|
||||
include(${RULESPATH}/rules.cmake)
|
||||
add_executable(${PROJECT_NAME}.elf $<TARGET_OBJECTS:baseband_shared> ${MODE_CPPSRC})
|
||||
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
|
||||
add_definitions(${DEFS})
|
||||
include_directories(. ${INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.bin ${PROJECT_NAME}.img
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
|
||||
COMMAND ${MAKE_IMAGE_CHUNK} ${PROJECT_NAME}.bin ${chunk_tag} ${PROJECT_NAME}.img
|
||||
DEPENDS ${PROJECT_NAME}.elf ${MAKE_IMAGE_CHUNK}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||
endmacro()
|
||||
|
||||
### ACARS RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_acars.cpp
|
||||
)
|
||||
DeclareTargets(PACA acars)
|
||||
|
||||
### ADS-B RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbrx.cpp
|
||||
)
|
||||
DeclareTargets(PADR adsbrx)
|
||||
|
||||
### ADS-B TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbtx.cpp
|
||||
)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### AFSK
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_afsk.cpp
|
||||
)
|
||||
DeclareTargets(PAFT afsktx)
|
||||
|
||||
### AFSK RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_afskrx.cpp
|
||||
)
|
||||
DeclareTargets(PAFR afskrx)
|
||||
|
||||
### APRS RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_aprsrx.cpp
|
||||
)
|
||||
DeclareTargets(PAPR aprsrx)
|
||||
|
||||
### NRF RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_nrfrx.cpp
|
||||
)
|
||||
DeclareTargets(PNRR nrfrx)
|
||||
|
||||
### BTLE RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_btlerx.cpp
|
||||
)
|
||||
DeclareTargets(PBTR btlerx)
|
||||
### AIS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_ais.cpp
|
||||
)
|
||||
DeclareTargets(PAIS ais)
|
||||
|
||||
### AM Audio
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_am_audio.cpp
|
||||
)
|
||||
DeclareTargets(PAMA am_audio)
|
||||
|
||||
### AM TV
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_am_tv.cpp
|
||||
)
|
||||
DeclareTargets(PAMT am_tv)
|
||||
|
||||
### Audio transmit
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_audiotx.cpp
|
||||
)
|
||||
DeclareTargets(PATX audio_tx)
|
||||
|
||||
### Capture
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_capture.cpp
|
||||
)
|
||||
DeclareTargets(PCAP capture)
|
||||
|
||||
### ERT
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_ert.cpp
|
||||
)
|
||||
DeclareTargets(PERT ert)
|
||||
|
||||
### Radiosonde
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_sonde.cpp
|
||||
)
|
||||
DeclareTargets(PSON sonde)
|
||||
|
||||
### FSK TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_fsk.cpp
|
||||
)
|
||||
DeclareTargets(PFSK fsktx)
|
||||
|
||||
### Jammer
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_jammer.cpp
|
||||
)
|
||||
DeclareTargets(PJAM jammer)
|
||||
|
||||
### Microphone transmit
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_mictx.cpp
|
||||
)
|
||||
DeclareTargets(PMTX mic_tx)
|
||||
|
||||
### NFM Audio
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_nfm_audio.cpp
|
||||
)
|
||||
DeclareTargets(PNFM nfm_audio)
|
||||
|
||||
### No op
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_noop.cpp
|
||||
)
|
||||
DeclareTargets(PNOP no_operation)
|
||||
|
||||
### OOK
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_ook.cpp
|
||||
)
|
||||
DeclareTargets(POOK ook)
|
||||
|
||||
### POCSAG RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_pocsag.cpp
|
||||
)
|
||||
DeclareTargets(PPOC pocsag)
|
||||
|
||||
### RDS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_rds.cpp
|
||||
)
|
||||
DeclareTargets(PRDS rds)
|
||||
|
||||
### Replay
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_replay.cpp
|
||||
)
|
||||
DeclareTargets(PREP replay)
|
||||
|
||||
### GPS Simulator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_gps_sim.cpp
|
||||
)
|
||||
DeclareTargets(PGPS gps_sim)
|
||||
|
||||
### Signal generator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_siggen.cpp
|
||||
)
|
||||
DeclareTargets(PSIG siggen)
|
||||
|
||||
### SSTV TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_sstvtx.cpp
|
||||
)
|
||||
DeclareTargets(PSTX sstvtx)
|
||||
|
||||
### Test
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_test.cpp
|
||||
)
|
||||
DeclareTargets(PTST test)
|
||||
|
||||
### Tones
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_tones.cpp
|
||||
)
|
||||
DeclareTargets(PTON tones)
|
||||
|
||||
### TPMS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_tpms.cpp
|
||||
)
|
||||
DeclareTargets(PTPM tpms)
|
||||
|
||||
### Wideband Spectrum
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_wideband_spectrum.cpp
|
||||
)
|
||||
DeclareTargets(PSPE wideband_spectrum)
|
||||
|
||||
### WFM Audio
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_wfm_audio.cpp
|
||||
)
|
||||
DeclareTargets(PWFM wfm_audio)
|
||||
|
||||
### HackRF "factory" firmware
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT hackrf.img
|
||||
COMMAND ${MAKE_IMAGE_CHUNK} ${HACKRF_FIRMWARE_BIN_IMAGE} HRF1 hackrf.img 98304
|
||||
DEPENDS ${HACKRF_FIRMWARE_BIN_FILENAME} ${MAKE_IMAGE_CHUNK}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} hackrf.img)
|
||||
|
||||
### Terminator image
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT terminator.img
|
||||
COMMAND ${MAKE_IMAGE_CHUNK} terminator.img
|
||||
DEPENDS ${MAKE_IMAGE_CHUNK}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} terminator.img)
|
||||
|
||||
#######################################################################
|
||||
|
||||
project(baseband)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.img
|
||||
COMMAND cat ${BASEBAND_IMAGES} > ${PROJECT_NAME}.img
|
||||
DEPENDS ${BASEBAND_IMAGES}
|
||||
DEPENDS hackrf.img terminator.img
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
${PROJECT_NAME}
|
||||
DEPENDS ${PROJECT_NAME}.img
|
||||
)
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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_compressor.hpp"
|
||||
|
||||
float GainComputer::operator()(const float x) const {
|
||||
const auto abs_x = std::abs(x);
|
||||
const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x);
|
||||
const auto overshoot_db = db - threshold_db;
|
||||
if( knee_width_db > 0.0f ) {
|
||||
const auto w2 = knee_width_db / 2.0f;
|
||||
const auto a = w2 / (knee_width_db * knee_width_db);
|
||||
const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2);
|
||||
const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f);
|
||||
return rectified_overshoot * slope;
|
||||
} else {
|
||||
const auto rectified_overshoot = std::max(overshoot_db, 0.0f);
|
||||
return rectified_overshoot * slope;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedForwardCompressor::execute_in_place(const buffer_f32_t& buffer) {
|
||||
constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f);
|
||||
for(size_t i=0; i<buffer.count; i++) {
|
||||
buffer.p[i] = execute_once(buffer.p[i]) * makeup_gain;
|
||||
}
|
||||
}
|
||||
|
||||
float FeedForwardCompressor::execute_once(const float x) {
|
||||
const auto gain_db = gain_computer(x);
|
||||
const auto peak_db = -peak_detector(-gain_db);
|
||||
const auto gain = fast_pow2(peak_db * (3.321928094887362f / 20.0f));
|
||||
return x * gain;
|
||||
}
|
102
Software/portapack-mayhem/firmware/baseband/audio_compressor.hpp
Normal file
102
Software/portapack-mayhem/firmware/baseband/audio_compressor.hpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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_COMPRESSOR_H__
|
||||
#define __AUDIO_COMPRESSOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/* Code based on article in Journal of the Audio Engineering Society
|
||||
* Vol. 60, No. 6, 2012 June, by Dimitrios Giannoulis, Michael Massberg,
|
||||
* Joshua D. Reiss "Digital Dynamic Range Compressor Design – A Tutorial
|
||||
* and Analysis"
|
||||
*/
|
||||
|
||||
class GainComputer {
|
||||
public:
|
||||
constexpr GainComputer(
|
||||
float ratio,
|
||||
float threshold
|
||||
) : ratio { ratio },
|
||||
slope { 1.0f / ratio - 1.0f },
|
||||
threshold_db { threshold }
|
||||
{
|
||||
}
|
||||
|
||||
float operator()(const float x) const;
|
||||
|
||||
private:
|
||||
const float ratio;
|
||||
const float slope;
|
||||
const float threshold_db;
|
||||
|
||||
static constexpr float knee_width_db = 0.0f;
|
||||
|
||||
static constexpr float db_floor = -120.0f;
|
||||
static constexpr float lin_floor = std::pow(10.0f, db_floor / 20.0f);
|
||||
static constexpr float log2_db_k = 20.0f * std::log10(2.0f);
|
||||
};
|
||||
|
||||
class PeakDetectorBranchingSmooth {
|
||||
public:
|
||||
constexpr PeakDetectorBranchingSmooth(
|
||||
float att_a,
|
||||
float rel_a
|
||||
) : att_a { att_a },
|
||||
rel_a { rel_a }
|
||||
{
|
||||
}
|
||||
|
||||
float operator()(const float db) {
|
||||
const auto a = (db > state) ? att_a : rel_a;
|
||||
state = db + a * (state - db);
|
||||
return state;
|
||||
}
|
||||
|
||||
private:
|
||||
float state { 0.0f };
|
||||
const float att_a;
|
||||
const float rel_a;
|
||||
};
|
||||
|
||||
class FeedForwardCompressor {
|
||||
public:
|
||||
void execute_in_place(const buffer_f32_t& buffer);
|
||||
|
||||
private:
|
||||
static constexpr float fs = 12000.0f;
|
||||
static constexpr float ratio = 10.0f;
|
||||
static constexpr float threshold = -30.0f;
|
||||
|
||||
GainComputer gain_computer { ratio, threshold };
|
||||
PeakDetectorBranchingSmooth peak_detector { tau_alpha(0.010f, fs), tau_alpha(0.300f, fs) };
|
||||
|
||||
float execute_once(const float x);
|
||||
|
||||
static constexpr float tau_alpha(const float tau, const float fs) {
|
||||
return std::exp(-1.0f / (tau * fs));
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_COMPRESSOR_H__*/
|
239
Software/portapack-mayhem/firmware/baseband/audio_dma.cpp
Normal file
239
Software/portapack-mayhem/firmware/baseband/audio_dma.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 "audio_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "hal.h"
|
||||
#include "gpdma.hpp"
|
||||
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "portapack_dma.hpp"
|
||||
|
||||
namespace audio {
|
||||
namespace dma {
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
|
||||
|
||||
constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */
|
||||
constexpr uint32_t gpdma_rx_src_peripheral = gpdma_rx_peripheral;
|
||||
constexpr uint32_t gpdma_rx_dest_peripheral = gpdma_rx_peripheral;
|
||||
|
||||
constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */
|
||||
constexpr uint32_t gpdma_tx_src_peripheral = gpdma_tx_peripheral;
|
||||
constexpr uint32_t gpdma_tx_dest_peripheral = gpdma_tx_peripheral;
|
||||
|
||||
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
|
||||
return {
|
||||
.lm = gpdma_ahb_master_lli_fetch,
|
||||
.r = 0,
|
||||
.lli = reinterpret_cast<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control_tx(const size_t transfer_bytes) {
|
||||
return {
|
||||
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
|
||||
.sbsize = 4, /* Burst size: 32 */
|
||||
.dbsize = 4, /* Burst size: 32 */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = gpdma_ahb_master_memory,
|
||||
.d = gpdma_ahb_master_peripheral,
|
||||
.si = 1,
|
||||
.di = 0,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config_tx() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_tx_src_peripheral,
|
||||
.destperipheral = gpdma_tx_dest_peripheral,
|
||||
.flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control_rx(const size_t transfer_bytes) {
|
||||
return {
|
||||
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
|
||||
.sbsize = 4, /* Burst size: 32 */
|
||||
.dbsize = 4, /* Burst size: 32 */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = gpdma_ahb_master_peripheral,
|
||||
.d = gpdma_ahb_master_memory,
|
||||
.si = 0,
|
||||
.di = 1,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config_rx() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_rx_src_peripheral,
|
||||
.destperipheral = gpdma_rx_dest_peripheral,
|
||||
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
|
||||
|
||||
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);
|
||||
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
|
||||
constexpr size_t transfers_mask = transfers_per_buffer - 1;
|
||||
|
||||
constexpr size_t buffer_bytes = buffer_samples * sizeof(sample_t);
|
||||
constexpr size_t transfer_bytes = transfer_samples * sizeof(sample_t);
|
||||
|
||||
static std::array<sample_t, buffer_samples> buffer_tx;
|
||||
static std::array<sample_t, buffer_samples> buffer_rx;
|
||||
|
||||
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_tx_loop;
|
||||
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_rx_loop;
|
||||
|
||||
static constexpr auto& gpdma_channel_i2s0_tx = gpdma::channels[portapack::i2s0_tx_gpdma_channel_number];
|
||||
static constexpr auto& gpdma_channel_i2s0_rx = gpdma::channels[portapack::i2s0_rx_gpdma_channel_number];
|
||||
|
||||
static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
|
||||
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
|
||||
|
||||
static void tx_transfer_complete() {
|
||||
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
|
||||
}
|
||||
|
||||
static void tx_error() {
|
||||
disable();
|
||||
}
|
||||
|
||||
static void rx_transfer_complete() {
|
||||
rx_next_lli = gpdma_channel_i2s0_rx.next_lli();
|
||||
}
|
||||
|
||||
static void rx_error() {
|
||||
disable();
|
||||
}
|
||||
|
||||
void init() {
|
||||
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
|
||||
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
|
||||
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
|
||||
}
|
||||
|
||||
static void configure_tx() {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
|
||||
const auto control_value = control_tx(transfer_bytes);
|
||||
for(size_t i=0; i<lli_tx_loop.size(); i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&buffer_tx[i * transfer_samples]);
|
||||
lli_tx_loop[i].srcaddr = memory;
|
||||
lli_tx_loop[i].destaddr = peripheral;
|
||||
lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]);
|
||||
lli_tx_loop[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
static void configure_rx() {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->RXFIFO);
|
||||
const auto control_value = control_rx(transfer_bytes);
|
||||
for(size_t i=0; i<lli_rx_loop.size(); i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&buffer_rx[i * transfer_samples]);
|
||||
lli_rx_loop[i].srcaddr = peripheral;
|
||||
lli_rx_loop[i].destaddr = memory;
|
||||
lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]);
|
||||
lli_rx_loop[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
void configure() {
|
||||
configure_tx();
|
||||
configure_rx();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
const auto gpdma_config_tx = config_tx();
|
||||
const auto gpdma_config_rx = config_rx();
|
||||
|
||||
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
|
||||
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
|
||||
|
||||
gpdma_channel_i2s0_tx.enable();
|
||||
gpdma_channel_i2s0_rx.enable();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_i2s0_tx.disable();
|
||||
gpdma_channel_i2s0_rx.disable();
|
||||
}
|
||||
|
||||
buffer_t tx_empty_buffer() {
|
||||
const auto next_lli = tx_next_lli;
|
||||
if( next_lli ) {
|
||||
const size_t next_index = next_lli - &lli_tx_loop[0];
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
return { reinterpret_cast<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
buffer_t rx_empty_buffer() {
|
||||
const auto next_lli = rx_next_lli;
|
||||
if( next_lli ) {
|
||||
const size_t next_index = next_lli - &lli_rx_loop[0];
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
return { reinterpret_cast<sample_t*>(lli_rx_loop[free_index].destaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace audio */
|
57
Software/portapack-mayhem/firmware/baseband/audio_dma.hpp
Normal file
57
Software/portapack-mayhem/firmware/baseband/audio_dma.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __AUDIO_DMA_H__
|
||||
#define __AUDIO_DMA_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "buffer.hpp"
|
||||
|
||||
namespace audio {
|
||||
|
||||
struct sample_t {
|
||||
union {
|
||||
struct {
|
||||
int16_t left;
|
||||
int16_t right;
|
||||
};
|
||||
uint32_t raw;
|
||||
};
|
||||
};
|
||||
|
||||
using buffer_t = buffer_t<sample_t>;
|
||||
|
||||
namespace dma {
|
||||
|
||||
void init();
|
||||
void configure();
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
audio::buffer_t tx_empty_buffer();
|
||||
audio::buffer_t rx_empty_buffer();
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace audio */
|
||||
|
||||
#endif/*__AUDIO_DMA_H__*/
|
40
Software/portapack-mayhem/firmware/baseband/audio_input.cpp
Normal file
40
Software/portapack-mayhem/firmware/baseband/audio_input.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 "audio_input.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
void AudioInput::read_audio_buffer(buffer_s16_t& audio) {
|
||||
auto audio_buffer = audio::dma::rx_empty_buffer();
|
||||
|
||||
for (size_t i=0; i<audio_buffer.count; i++)
|
||||
audio.p[i] = audio_buffer.p[i].right;
|
||||
}
|
47
Software/portapack-mayhem/firmware/baseband/audio_input.hpp
Normal file
47
Software/portapack-mayhem/firmware/baseband/audio_input.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __AUDIO_INPUT_H__
|
||||
#define __AUDIO_INPUT_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "stream_input.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class AudioInput {
|
||||
public:
|
||||
void read_audio_buffer(buffer_s16_t& audio);
|
||||
|
||||
private:
|
||||
/*static constexpr float k = 32768.0f;
|
||||
static constexpr float ki = 1.0f / k;
|
||||
|
||||
IIRBiquadFilter hpf { };*/
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_INPUT_H__*/
|
128
Software/portapack-mayhem/firmware/baseband/audio_output.cpp
Normal file
128
Software/portapack-mayhem/firmware/baseband/audio_output.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 "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 bool do_proc
|
||||
) {
|
||||
do_processing = do_proc;
|
||||
}
|
||||
|
||||
void AudioOutput::configure(
|
||||
const iir_biquad_config_t& hpf_config,
|
||||
const iir_biquad_config_t& deemph_config,
|
||||
const float squelch_threshold
|
||||
) {
|
||||
hpf.configure(hpf_config);
|
||||
deemph.configure(deemph_config);
|
||||
squelch.set_threshold(squelch_threshold);
|
||||
}
|
||||
|
||||
void AudioOutput::write(
|
||||
const buffer_s16_t& audio
|
||||
) {
|
||||
std::array<float, 32> audio_f;
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio_f[i] = audio.p[i] * ki;
|
||||
}
|
||||
write(buffer_f32_t {
|
||||
audio_f.data(),
|
||||
audio.count,
|
||||
audio.sampling_rate
|
||||
});
|
||||
}
|
||||
|
||||
void AudioOutput::write(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
block_buffer.feed(
|
||||
audio,
|
||||
[this](const buffer_f32_t& buffer) {
|
||||
this->on_block(buffer);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void AudioOutput::on_block(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
if (do_processing) {
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
|
||||
hpf.execute_in_place(audio);
|
||||
deemph.execute_in_place(audio);
|
||||
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
audio_present = (audio_present_history != 0);
|
||||
|
||||
if( !audio_present ) {
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
} else
|
||||
audio_present = true;
|
||||
|
||||
fill_audio_buffer(audio, audio_present);
|
||||
}
|
||||
|
||||
bool AudioOutput::is_squelched() {
|
||||
return !audio_present;
|
||||
}
|
||||
|
||||
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo) {
|
||||
std::array<int16_t, 32> audio_int;
|
||||
|
||||
auto audio_buffer = audio::dma::tx_empty_buffer();
|
||||
for(size_t i=0; i<audio_buffer.count; i++) {
|
||||
const int32_t sample_int = audio.p[i] * k;
|
||||
const int32_t sample_saturated = __SSAT(sample_int, 16);
|
||||
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
|
||||
audio_int[i] = sample_saturated;
|
||||
}
|
||||
if( stream && send_to_fifo ) {
|
||||
stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
|
||||
}
|
||||
|
||||
feed_audio_stats(audio);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
81
Software/portapack-mayhem/firmware/baseband/audio_output.hpp
Normal file
81
Software/portapack-mayhem/firmware/baseband/audio_output.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __AUDIO_OUTPUT_H__
|
||||
#define __AUDIO_OUTPUT_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "stream_input.hpp"
|
||||
#include "block_decimator.hpp"
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class AudioOutput {
|
||||
public:
|
||||
void configure(const bool do_proc);
|
||||
|
||||
void configure(
|
||||
const iir_biquad_config_t& hpf_config,
|
||||
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
|
||||
const float squelch_threshold = 0.0f
|
||||
);
|
||||
|
||||
void write(const buffer_s16_t& audio);
|
||||
void write(const buffer_f32_t& audio);
|
||||
|
||||
void set_stream(std::unique_ptr<StreamInput> new_stream) {
|
||||
stream = std::move(new_stream);
|
||||
}
|
||||
|
||||
bool is_squelched();
|
||||
|
||||
private:
|
||||
static constexpr float k = 32768.0f;
|
||||
static constexpr float ki = 1.0f / k;
|
||||
|
||||
BlockDecimator<float, 32> block_buffer { 1 };
|
||||
|
||||
IIRBiquadFilter hpf { };
|
||||
IIRBiquadFilter deemph { };
|
||||
FMSquelch squelch { };
|
||||
|
||||
std::unique_ptr<StreamInput> stream { };
|
||||
|
||||
AudioStatsCollector audio_stats { };
|
||||
|
||||
uint64_t audio_present_history = 0;
|
||||
|
||||
bool audio_present = false;
|
||||
bool do_processing = true;
|
||||
|
||||
void on_block(const buffer_f32_t& audio);
|
||||
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
|
||||
void feed_audio_stats(const buffer_f32_t& audio);
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_OUTPUT_H__*/
|
@ -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 = mag2_to_dbv_norm(squared_sum / count);
|
||||
statistics.max_db = mag2_to_dbv_norm(max_squared);
|
||||
statistics.count = count;
|
||||
|
||||
squared_sum = 0;
|
||||
max_squared = 0;
|
||||
count = 0;
|
||||
|
||||
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);
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_STATS_COLLECTOR_H__
|
||||
#define __AUDIO_STATS_COLLECTOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class AudioStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
void feed(const buffer_f32_t& src, Callback callback) {
|
||||
if( feed(src) ) {
|
||||
callback(statistics);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) {
|
||||
if( mute(sample_count, sampling_rate) ) {
|
||||
callback(statistics);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float update_interval { 0.1f };
|
||||
float squared_sum { 0 };
|
||||
float max_squared { 0 };
|
||||
size_t count { 0 };
|
||||
|
||||
AudioStatistics statistics { };
|
||||
|
||||
void consume_audio_buffer(const buffer_f32_t& src);
|
||||
|
||||
bool update_stats(const size_t sample_count, const size_t sampling_rate);
|
||||
|
||||
bool feed(const buffer_f32_t& src);
|
||||
bool mute(const size_t sample_count, const size_t sampling_rate);
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_STATS_COLLECTOR_H__*/
|
87
Software/portapack-mayhem/firmware/baseband/baseband.cpp
Normal file
87
Software/portapack-mayhem/firmware/baseband/baseband.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 "ch.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_dma.hpp"
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
static void init() {
|
||||
audio::dma::init();
|
||||
audio::dma::configure();
|
||||
audio::dma::enable();
|
||||
|
||||
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
static void halt() {
|
||||
port_disable();
|
||||
while(true) {
|
||||
port_wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void __late_init(void) {
|
||||
/*
|
||||
* System initializations.
|
||||
* - HAL initialization, this also initializes the configured device drivers
|
||||
* and performs the board-specific initializations.
|
||||
* - Kernel initialization, the main() function becomes a thread and the
|
||||
* RTOS is active.
|
||||
*/
|
||||
halInit();
|
||||
|
||||
/* After this call, scheduler, systick, heap, etc. are available. */
|
||||
/* By doing chSysInit() here, it runs before C++ constructors, which may
|
||||
* require the heap.
|
||||
*/
|
||||
chSysInit();
|
||||
|
||||
/* Baseband initialization */
|
||||
init();
|
||||
}
|
||||
|
||||
void _default_exit(void) {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
chSysDisable();
|
||||
|
||||
systick_stop();
|
||||
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.application_queue.push(shutdown_message);
|
||||
|
||||
shared_memory.baseband_message = nullptr;
|
||||
|
||||
halt();
|
||||
}
|
||||
|
||||
}
|
174
Software/portapack-mayhem/firmware/baseband/baseband_dma.cpp
Normal file
174
Software/portapack-mayhem/firmware/baseband/baseband_dma.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "hal.h"
|
||||
#include "gpdma.hpp"
|
||||
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "portapack_dma.hpp"
|
||||
|
||||
#include "thread_wait.hpp"
|
||||
|
||||
namespace baseband {
|
||||
namespace dma {
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_sgpio = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 1;
|
||||
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
|
||||
|
||||
constexpr uint32_t gpdma_src_peripheral = 0x0;
|
||||
constexpr uint32_t gpdma_dest_peripheral = 0x0;
|
||||
|
||||
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
|
||||
return {
|
||||
.lm = gpdma_ahb_master_lli_fetch,
|
||||
.r = 0,
|
||||
.lli = reinterpret_cast<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control(const baseband::Direction direction, const size_t buffer_words) {
|
||||
return {
|
||||
.transfersize = buffer_words,
|
||||
.sbsize = 0, /* Burst size: 1 */
|
||||
.dbsize = 0, /* Burst size: 1 */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio,
|
||||
.d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory,
|
||||
.si = (direction == baseband::Direction::Transmit) ? 1U : 0U,
|
||||
.di = (direction == baseband::Direction::Transmit) ? 0U : 1U,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config(const baseband::Direction direction) {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_src_peripheral,
|
||||
.destperipheral = gpdma_dest_peripheral,
|
||||
.flowcntrl = (direction == baseband::Direction::Transmit)
|
||||
? gpdma::FlowControl::MemoryToPeripheral_DMAControl
|
||||
: gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr size_t buffer_samples_log2n = 13;
|
||||
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);
|
||||
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
|
||||
constexpr size_t transfers_mask = transfers_per_buffer - 1;
|
||||
|
||||
constexpr size_t buffer_bytes = buffer_samples * sizeof(baseband::sample_t);
|
||||
constexpr size_t transfer_bytes = transfer_samples * sizeof(baseband::sample_t);
|
||||
|
||||
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 ThreadWait thread_wait;
|
||||
|
||||
static void transfer_complete() {
|
||||
const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0];
|
||||
thread_wait.wake_from_interrupt(next_lli_index);
|
||||
}
|
||||
|
||||
static void dma_error() {
|
||||
thread_wait.wake_from_interrupt(-1);
|
||||
disable();
|
||||
}
|
||||
|
||||
void init() {
|
||||
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
|
||||
#if defined(PORTAPACK_BASEBAND_DMA_NO_SYNC)
|
||||
/* Disable synchronization logic to improve(?) DMA response time.
|
||||
* SGPIO (peripheral) must be on same clock as GPDMA peripheral.
|
||||
* SGPIO runs from BASE_PERIPH_CLK, which is set to PLL1 in normal
|
||||
* operation, same as the M4 and M0 cores. Memory, of course, is
|
||||
* running from the same clock as the cores.
|
||||
*/
|
||||
LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
|
||||
LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
|
||||
#endif
|
||||
}
|
||||
|
||||
void configure(
|
||||
baseband::sample_t* const buffer_base,
|
||||
const baseband::Direction direction
|
||||
) {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0]);
|
||||
const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4));
|
||||
for(size_t i=0; i<lli_loop.size(); i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&buffer_base[i * transfer_samples]);
|
||||
lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral;
|
||||
lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory;
|
||||
lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]);
|
||||
lli_loop[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
void enable(const baseband::Direction direction) {
|
||||
const auto gpdma_config = config(direction);
|
||||
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
|
||||
gpdma_channel_sgpio.enable();
|
||||
}
|
||||
|
||||
bool is_enabled() {
|
||||
return gpdma_channel_sgpio.is_enabled();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_sgpio.disable();
|
||||
}
|
||||
|
||||
baseband::buffer_t wait_for_buffer() {
|
||||
const auto next_index = thread_wait.sleep();
|
||||
|
||||
if( next_index >= 0 ) {
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
const auto src = lli_loop[free_index].srcaddr;
|
||||
const auto dst = lli_loop[free_index].destaddr;
|
||||
const auto p = (src == reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0])) ? dst : src;
|
||||
return { reinterpret_cast<sample_t*>(p), transfer_samples };
|
||||
} else {
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace baseband */
|
50
Software/portapack-mayhem/firmware/baseband/baseband_dma.hpp
Normal file
50
Software/portapack-mayhem/firmware/baseband/baseband_dma.hpp
Normal file
@ -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 __BASEBAND_DMA_H__
|
||||
#define __BASEBAND_DMA_H__
|
||||
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "complex.hpp"
|
||||
#include "baseband.hpp"
|
||||
|
||||
namespace baseband {
|
||||
namespace dma {
|
||||
|
||||
void init();
|
||||
void configure(
|
||||
baseband::sample_t* const buffer_base,
|
||||
const baseband::Direction direction
|
||||
);
|
||||
|
||||
void enable(const baseband::Direction direction);
|
||||
bool is_enabled();
|
||||
|
||||
void disable();
|
||||
|
||||
baseband::buffer_t wait_for_buffer();
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace baseband */
|
||||
|
||||
#endif/*__BASEBAND_DMA_H__*/
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 "message.hpp"
|
||||
|
||||
void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) {
|
||||
channel_stats.feed(
|
||||
channel,
|
||||
[](const ChannelStatistics& statistics) {
|
||||
const ChannelStatisticsMessage channel_stats_message { statistics };
|
||||
shared_memory.application_queue.push(channel_stats_message);
|
||||
}
|
||||
);
|
||||
}
|
@ -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 __BASEBAND_PROCESSOR_H__
|
||||
#define __BASEBAND_PROCESSOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "channel_stats_collector.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
class BasebandProcessor {
|
||||
public:
|
||||
virtual ~BasebandProcessor() = default;
|
||||
|
||||
virtual void execute(const buffer_c8_t& buffer) = 0;
|
||||
|
||||
virtual void on_message(const Message* const) { };
|
||||
|
||||
protected:
|
||||
void feed_channel_stats(const buffer_c16_t& channel);
|
||||
|
||||
private:
|
||||
ChannelStatsCollector channel_stats { };
|
||||
};
|
||||
|
||||
#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;
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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_STATS_COLLECTOR_H__
|
||||
#define __BASEBAND_STATS_COLLECTOR_H__
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.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(const buffer_c8_t& buffer, Callback callback) {
|
||||
if( process(buffer) ) {
|
||||
callback(capture_statistics());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float report_interval { 1.0f };
|
||||
size_t samples { 0 };
|
||||
size_t samples_last_report { 0 };
|
||||
const Thread* const thread_idle;
|
||||
uint32_t last_idle_ticks { 0 };
|
||||
const Thread* const thread_main;
|
||||
uint32_t last_main_ticks { 0 };
|
||||
const Thread* const thread_rssi;
|
||||
uint32_t last_rssi_ticks { 0 };
|
||||
const Thread* const thread_baseband;
|
||||
uint32_t last_baseband_ticks { 0 };
|
||||
|
||||
bool process(const buffer_c8_t& buffer);
|
||||
BasebandStatistics capture_statistics();
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_STATS_COLLECTOR_H__*/
|
103
Software/portapack-mayhem/firmware/baseband/baseband_thread.cpp
Normal file
103
Software/portapack-mayhem/firmware/baseband/baseband_thread.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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_sgpio.hpp"
|
||||
#include "baseband_dma.hpp"
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "i2s.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
static baseband::SGPIO baseband_sgpio;
|
||||
|
||||
WORKING_AREA(baseband_thread_wa, 4096);
|
||||
|
||||
Thread* BasebandThread::thread = nullptr;
|
||||
|
||||
BasebandThread::BasebandThread(
|
||||
uint32_t sampling_rate,
|
||||
BasebandProcessor* const baseband_processor,
|
||||
const tprio_t priority,
|
||||
baseband::Direction direction
|
||||
) : baseband_processor { baseband_processor },
|
||||
_direction { direction },
|
||||
sampling_rate { sampling_rate }
|
||||
{
|
||||
thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
BasebandThread::~BasebandThread() {
|
||||
chThdTerminate(thread);
|
||||
chThdWait(thread);
|
||||
thread = nullptr;
|
||||
}
|
||||
|
||||
void BasebandThread::set_sampling_rate(uint32_t new_sampling_rate) {
|
||||
sampling_rate = new_sampling_rate;
|
||||
}
|
||||
|
||||
void BasebandThread::run() {
|
||||
baseband_sgpio.init();
|
||||
baseband::dma::init();
|
||||
|
||||
const auto baseband_buffer = std::make_unique<std::array<baseband::sample_t, 8192>>();
|
||||
baseband::dma::configure(
|
||||
baseband_buffer->data(),
|
||||
direction()
|
||||
);
|
||||
//baseband::dma::allocate(4, 2048);
|
||||
|
||||
baseband_sgpio.configure(direction());
|
||||
baseband::dma::enable(direction());
|
||||
baseband_sgpio.streaming_enable();
|
||||
|
||||
while( !chThdShouldTerminate() ) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = baseband::dma::wait_for_buffer();
|
||||
if( buffer_tmp ) {
|
||||
buffer_c8_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, sampling_rate
|
||||
};
|
||||
|
||||
if( baseband_processor ) {
|
||||
baseband_processor->execute(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i2s::i2s0::tx_mute();
|
||||
baseband::dma::disable();
|
||||
baseband_sgpio.streaming_disable();
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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(
|
||||
uint32_t sampling_rate,
|
||||
BasebandProcessor* const baseband_processor,
|
||||
const tprio_t priority,
|
||||
const baseband::Direction direction = baseband::Direction::Receive
|
||||
);
|
||||
~BasebandThread();
|
||||
|
||||
BasebandThread(const BasebandThread&) = delete;
|
||||
BasebandThread(BasebandThread&&) = delete;
|
||||
BasebandThread& operator=(const BasebandThread&) = delete;
|
||||
BasebandThread& operator=(BasebandThread&&) = delete;
|
||||
|
||||
// This getter should die, it's just here to leak information to code that
|
||||
// isn't in the right place to begin with.
|
||||
baseband::Direction direction() const {
|
||||
return _direction;
|
||||
}
|
||||
|
||||
void set_sampling_rate(uint32_t new_sampling_rate);
|
||||
|
||||
private:
|
||||
static Thread* thread;
|
||||
|
||||
BasebandProcessor* baseband_processor { nullptr };
|
||||
baseband::Direction _direction { baseband::Direction::Receive };
|
||||
uint32_t sampling_rate { 0 };
|
||||
|
||||
void run() override;
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_THREAD_H__*/
|
100
Software/portapack-mayhem/firmware/baseband/block_decimator.hpp
Normal file
100
Software/portapack-mayhem/firmware/baseband/block_decimator.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 __BLOCK_DECIMATOR_H__
|
||||
#define __BLOCK_DECIMATOR_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
template<typename T, size_t N>
|
||||
class BlockDecimator {
|
||||
public:
|
||||
constexpr BlockDecimator(
|
||||
const size_t factor
|
||||
) : factor_ { factor }
|
||||
{
|
||||
}
|
||||
|
||||
void set_input_sampling_rate(const uint32_t new_sampling_rate) {
|
||||
if( new_sampling_rate != input_sampling_rate() ) {
|
||||
input_sampling_rate_ = new_sampling_rate;
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t input_sampling_rate() const {
|
||||
return input_sampling_rate_;
|
||||
}
|
||||
|
||||
void set_factor(const size_t new_factor) {
|
||||
if( new_factor != factor() ) {
|
||||
factor_ = new_factor;
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
|
||||
size_t factor() const {
|
||||
return factor_;
|
||||
}
|
||||
|
||||
uint32_t output_sampling_rate() const {
|
||||
return input_sampling_rate() / factor();
|
||||
}
|
||||
|
||||
template<typename BlockCallback>
|
||||
void feed(const buffer_t<T>& src, BlockCallback callback) {
|
||||
/* NOTE: Input block size must be >= factor */
|
||||
|
||||
set_input_sampling_rate(src.sampling_rate);
|
||||
|
||||
while( src_i < src.count ) {
|
||||
buffer[dst_i++] = src.p[src_i];
|
||||
if( dst_i == buffer.size() ) {
|
||||
callback({ buffer.data(), buffer.size(), output_sampling_rate() });
|
||||
reset_state();
|
||||
dst_i = 0;
|
||||
}
|
||||
|
||||
src_i += factor();
|
||||
}
|
||||
|
||||
src_i -= src.count;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<T, N> buffer { };
|
||||
uint32_t input_sampling_rate_ { 0 };
|
||||
size_t factor_ { 1 };
|
||||
size_t src_i { 0 };
|
||||
size_t dst_i { 0 };
|
||||
|
||||
void reset_state() {
|
||||
src_i = 0;
|
||||
dst_i = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__BLOCK_DECIMATOR_H__*/
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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(const 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] */
|
||||
auto stage_0_out = execute_stage_0(buffer, work_baseband_buffer);
|
||||
if( decimation_factor == DecimationFactor::By2 ) {
|
||||
return stage_0_out;
|
||||
}
|
||||
|
||||
/* 1.536MHz complex<int16_t>[1024], [-32768, 32512]
|
||||
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
|
||||
* -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz
|
||||
* -> gain of 1
|
||||
* -> decimation by 2
|
||||
* -> 768kHz complex<int16_t>[512], [-8192, 8128] */
|
||||
auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer);
|
||||
if( decimation_factor == DecimationFactor::By4 ) {
|
||||
return cic_1_out;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
buffer_c16_t ChannelDecimator::execute_stage_0(
|
||||
const buffer_c8_t& buffer,
|
||||
const buffer_c16_t& work_baseband_buffer
|
||||
) {
|
||||
if( fs_over_4_downconvert ) {
|
||||
return translate.execute(buffer, work_baseband_buffer);
|
||||
} else {
|
||||
return cic_0.execute(buffer, work_baseband_buffer);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 {
|
||||
By2,
|
||||
By4,
|
||||
By8,
|
||||
By16,
|
||||
By32,
|
||||
};
|
||||
|
||||
constexpr ChannelDecimator(
|
||||
const DecimationFactor decimation_factor,
|
||||
const bool fs_over_4_downconvert = true
|
||||
) : decimation_factor { decimation_factor },
|
||||
fs_over_4_downconvert { fs_over_4_downconvert }
|
||||
{
|
||||
}
|
||||
|
||||
void set_decimation_factor(const DecimationFactor f) {
|
||||
decimation_factor = f;
|
||||
}
|
||||
|
||||
buffer_c16_t execute(const buffer_c8_t& buffer) {
|
||||
auto decimated = execute_decimation(buffer);
|
||||
|
||||
return decimated;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<complex16_t, 1024> work_baseband { };
|
||||
|
||||
dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate { };
|
||||
dsp::decimate::Complex8DecimateBy2CIC3 cic_0 { };
|
||||
dsp::decimate::DecimateBy2CIC3 cic_1 { };
|
||||
dsp::decimate::DecimateBy2CIC3 cic_2 { };
|
||||
dsp::decimate::DecimateBy2CIC3 cic_3 { };
|
||||
dsp::decimate::DecimateBy2CIC3 cic_4 { };
|
||||
|
||||
DecimationFactor decimation_factor { DecimationFactor::By32 };
|
||||
const bool fs_over_4_downconvert { true };
|
||||
|
||||
buffer_c16_t execute_decimation(const buffer_c8_t& buffer);
|
||||
|
||||
buffer_c16_t execute_stage_0(
|
||||
const buffer_c8_t& buffer,
|
||||
const buffer_c16_t& work_baseband_buffer
|
||||
);
|
||||
};
|
||||
|
||||
#endif/*__CHANNEL_DECIMATOR_H__*/
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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_STATS_COLLECTOR_H__
|
||||
#define __CHANNEL_STATS_COLLECTOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
class ChannelStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
void feed(const buffer_c16_t& src, Callback callback) {
|
||||
void *src_p = src.p;
|
||||
while(src_p < &src.p[src.count]) {
|
||||
const uint32_t sample = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq = __SMUAD(sample, sample);
|
||||
if( mag_sq > max_squared ) {
|
||||
max_squared = mag_sq;
|
||||
}
|
||||
}
|
||||
count += src.count;
|
||||
|
||||
const size_t samples_per_update = src.sampling_rate * update_interval;
|
||||
|
||||
if( count >= samples_per_update ) {
|
||||
const float max_squared_f = max_squared;
|
||||
const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f)));
|
||||
callback({ max_db, count });
|
||||
|
||||
max_squared = 0;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float update_interval { 0.1f };
|
||||
uint32_t max_squared { 0 };
|
||||
size_t count { 0 };
|
||||
};
|
||||
|
||||
#endif/*__CHANNEL_STATS_COLLECTOR_H__*/
|
546
Software/portapack-mayhem/firmware/baseband/chconf.h
Normal file
546
Software/portapack-mayhem/firmware/baseband/chconf.h
Normal file
@ -0,0 +1,546 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
Copyright (C) 2014 Jared Boone, ShareBrained Technology
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file templates/chconf.h
|
||||
* @brief Configuration file template.
|
||||
* @details A copy of this file must be placed in each project directory, it
|
||||
* contains the application specific kernel settings.
|
||||
*
|
||||
* @addtogroup config
|
||||
* @details Kernel related settings and hooks.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _CHCONF_H_
|
||||
#define _CHCONF_H_
|
||||
|
||||
/*===========================================================================*/
|
||||
/**
|
||||
* @name Kernel parameters and options
|
||||
* @{
|
||||
*/
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief System tick frequency.
|
||||
* @details Frequency of the system timer that drives the system ticks. This
|
||||
* setting also defines the system tick time unit.
|
||||
*/
|
||||
#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__)
|
||||
#define CH_FREQUENCY 1000
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Round robin interval.
|
||||
* @details This constant is the number of system ticks allowed for the
|
||||
* threads before preemption occurs. Setting this value to zero
|
||||
* disables the preemption for threads with equal priority and the
|
||||
* round robin becomes cooperative. Note that higher priority
|
||||
* threads can still preempt, the kernel is always preemptive.
|
||||
*
|
||||
* @note Disabling the round robin preemption makes the kernel more compact
|
||||
* and generally faster.
|
||||
*/
|
||||
#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__)
|
||||
#define CH_TIME_QUANTUM 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Managed RAM size.
|
||||
* @details Size of the RAM area to be managed by the OS. If set to zero
|
||||
* then the whole available RAM is used. The core memory is made
|
||||
* available to the heap allocator and/or can be used directly through
|
||||
* the simplified core memory allocator.
|
||||
*
|
||||
* @note In order to let the OS manage the whole RAM the linker script must
|
||||
* provide the @p __heap_base__ and @p __heap_end__ symbols.
|
||||
* @note Requires @p CH_USE_MEMCORE.
|
||||
*/
|
||||
#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__)
|
||||
#define CH_MEMCORE_SIZE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Idle thread automatic spawn suppression.
|
||||
* @details When this option is activated the function @p chSysInit()
|
||||
* does not spawn the idle thread automatically. The application has
|
||||
* then the responsibility to do one of the following:
|
||||
* - Spawn a custom idle thread at priority @p IDLEPRIO.
|
||||
* - Change the main() thread priority to @p IDLEPRIO then enter
|
||||
* an endless loop. In this scenario the @p main() thread acts as
|
||||
* the idle thread.
|
||||
* .
|
||||
* @note Unless an idle thread is spawned the @p main() thread must not
|
||||
* enter a sleep state.
|
||||
*/
|
||||
#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__)
|
||||
#define CH_NO_IDLE_THREAD FALSE
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/**
|
||||
* @name Performance options
|
||||
* @{
|
||||
*/
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief OS optimization.
|
||||
* @details If enabled then time efficient rather than space efficient code
|
||||
* is used when two possible implementations exist.
|
||||
*
|
||||
* @note This is not related to the compiler optimization options.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__)
|
||||
#define CH_OPTIMIZE_SPEED TRUE
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/**
|
||||
* @name Subsystem options
|
||||
* @{
|
||||
*/
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Threads registry APIs.
|
||||
* @details If enabled then the registry APIs are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
|
||||
#define CH_USE_REGISTRY FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Threads synchronization APIs.
|
||||
* @details If enabled then the @p chThdWait() function is included in
|
||||
* the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__)
|
||||
#define CH_USE_WAITEXIT TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Semaphores APIs.
|
||||
* @details If enabled then the Semaphores APIs are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__)
|
||||
#define CH_USE_SEMAPHORES TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Semaphores queuing mode.
|
||||
* @details If enabled then the threads are enqueued on semaphores by
|
||||
* priority rather than in FIFO order.
|
||||
*
|
||||
* @note The default is @p FALSE. Enable this if you have special requirements.
|
||||
* @note Requires @p CH_USE_SEMAPHORES.
|
||||
*/
|
||||
#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define CH_USE_SEMAPHORES_PRIORITY FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Atomic semaphore API.
|
||||
* @details If enabled then the semaphores the @p chSemSignalWait() API
|
||||
* is included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_SEMAPHORES.
|
||||
*/
|
||||
#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__)
|
||||
#define CH_USE_SEMSW TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Mutexes APIs.
|
||||
* @details If enabled then the mutexes APIs are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MUTEXES TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Conditional Variables APIs.
|
||||
* @details If enabled then the conditional variables APIs are included
|
||||
* in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_MUTEXES.
|
||||
*/
|
||||
#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__)
|
||||
#define CH_USE_CONDVARS TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Conditional Variables APIs with timeout.
|
||||
* @details If enabled then the conditional variables APIs with timeout
|
||||
* specification are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_CONDVARS.
|
||||
*/
|
||||
#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__)
|
||||
#define CH_USE_CONDVARS_TIMEOUT TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Events Flags APIs.
|
||||
* @details If enabled then the event flags APIs are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__)
|
||||
#define CH_USE_EVENTS TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Events Flags APIs with timeout.
|
||||
* @details If enabled then the events APIs with timeout specification
|
||||
* are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_EVENTS.
|
||||
*/
|
||||
#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__)
|
||||
#define CH_USE_EVENTS_TIMEOUT TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Synchronous Messages APIs.
|
||||
* @details If enabled then the synchronous messages APIs are included
|
||||
* in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MESSAGES TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Synchronous Messages queuing mode.
|
||||
* @details If enabled then messages are served by priority rather than in
|
||||
* FIFO order.
|
||||
*
|
||||
* @note The default is @p FALSE. Enable this if you have special requirements.
|
||||
* @note Requires @p CH_USE_MESSAGES.
|
||||
*/
|
||||
#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MESSAGES_PRIORITY FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Mailboxes APIs.
|
||||
* @details If enabled then the asynchronous messages (mailboxes) APIs are
|
||||
* included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_SEMAPHORES.
|
||||
*/
|
||||
#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MAILBOXES TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I/O Queues APIs.
|
||||
* @details If enabled then the I/O queues APIs are included in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__)
|
||||
#define CH_USE_QUEUES TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Core Memory Manager APIs.
|
||||
* @details If enabled then the core memory manager APIs are included
|
||||
* in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MEMCORE TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Heap Allocator APIs.
|
||||
* @details If enabled then the memory heap allocator APIs are included
|
||||
* in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_MEMCORE and either @p CH_USE_MUTEXES or
|
||||
* @p CH_USE_SEMAPHORES.
|
||||
* @note Mutexes are recommended.
|
||||
*/
|
||||
#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__)
|
||||
#define CH_USE_HEAP TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief C-runtime allocator.
|
||||
* @details If enabled the the heap allocator APIs just wrap the C-runtime
|
||||
* @p malloc() and @p free() functions.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
* @note Requires @p CH_USE_HEAP.
|
||||
* @note The C-runtime may or may not require @p CH_USE_MEMCORE, see the
|
||||
* appropriate documentation.
|
||||
*/
|
||||
#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MALLOC_HEAP FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Memory Pools Allocator APIs.
|
||||
* @details If enabled then the memory pools allocator APIs are included
|
||||
* in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__)
|
||||
#define CH_USE_MEMPOOLS TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Dynamic Threads APIs.
|
||||
* @details If enabled then the dynamic threads creation APIs are included
|
||||
* in the kernel.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note Requires @p CH_USE_WAITEXIT.
|
||||
* @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS.
|
||||
*/
|
||||
#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__)
|
||||
#define CH_USE_DYNAMIC TRUE
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/**
|
||||
* @name Debug options
|
||||
* @{
|
||||
*/
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Debug option, system state check.
|
||||
* @details If enabled the correct call protocol for system APIs is checked
|
||||
* at runtime.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_SYSTEM_STATE_CHECK TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Debug option, parameters checks.
|
||||
* @details If enabled then the checks on the API functions input
|
||||
* parameters are activated.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_ENABLE_CHECKS TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Debug option, consistency checks.
|
||||
* @details If enabled then all the assertions in the kernel code are
|
||||
* activated. This includes consistency checks inside the kernel,
|
||||
* runtime anomalies and port-defined checks.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_ENABLE_ASSERTS TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Debug option, trace buffer.
|
||||
* @details If enabled then the context switch circular trace buffer is
|
||||
* activated.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_ENABLE_TRACE FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Debug option, stack checks.
|
||||
* @details If enabled then a runtime stack check is performed.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
* @note The stack check is performed in a architecture/port dependent way.
|
||||
* It may not be implemented or some ports.
|
||||
* @note The default failure mode is to halt the system with the global
|
||||
* @p panic_msg variable set to @p NULL.
|
||||
*/
|
||||
#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_ENABLE_STACK_CHECK TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Debug option, stacks initialization.
|
||||
* @details If enabled then the threads working area is filled with a byte
|
||||
* value when a thread is created. This can be useful for the
|
||||
* runtime measurement of the used stack.
|
||||
*
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_FILL_THREADS TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Debug option, threads profiling.
|
||||
* @details If enabled then a field is added to the @p Thread structure that
|
||||
* counts the system ticks occurred while executing the thread.
|
||||
*
|
||||
* @note The default is @p TRUE.
|
||||
* @note This debug option is defaulted to TRUE because it is required by
|
||||
* some test cases into the test suite.
|
||||
*/
|
||||
#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
|
||||
#define CH_DBG_THREADS_PROFILING TRUE
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/**
|
||||
* @name Kernel hooks
|
||||
* @{
|
||||
*/
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Threads descriptor structure extension.
|
||||
* @details User fields added to the end of the @p Thread structure.
|
||||
*/
|
||||
#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
|
||||
#define THREAD_EXT_FIELDS \
|
||||
/* Add threads custom fields here.*/ \
|
||||
uint32_t switches; \
|
||||
uint32_t start_ticks; \
|
||||
uint32_t total_ticks;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Threads initialization hook.
|
||||
* @details User initialization code added to the @p chThdInit() API.
|
||||
*
|
||||
* @note It is invoked from within @p chThdInit() and implicitly from all
|
||||
* the threads creation APIs.
|
||||
*/
|
||||
#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__)
|
||||
#define THREAD_EXT_INIT_HOOK(tp) { \
|
||||
/* Add threads initialization code here.*/ \
|
||||
tp->switches = 0; \
|
||||
tp->start_ticks = 0; \
|
||||
tp->total_ticks = 0; \
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Threads finalization hook.
|
||||
* @details User finalization code added to the @p chThdExit() API.
|
||||
*
|
||||
* @note It is inserted into lock zone.
|
||||
* @note It is also invoked when the threads simply return in order to
|
||||
* terminate.
|
||||
*/
|
||||
#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__)
|
||||
#define THREAD_EXT_EXIT_HOOK(tp) { \
|
||||
/* Add threads finalization code here.*/ \
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Context switch hook.
|
||||
* @details This hook is invoked just before switching between threads.
|
||||
*/
|
||||
#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__)
|
||||
#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \
|
||||
/* System halt code here.*/ \
|
||||
otp->switches++; \
|
||||
ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \
|
||||
otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Idle Loop hook.
|
||||
* @details This hook is continuously invoked by the idle thread loop.
|
||||
*/
|
||||
#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__)
|
||||
#define IDLE_LOOP_HOOK() { \
|
||||
/* Idle loop code here.*/ \
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief System tick event hook.
|
||||
* @details This hook is invoked in the system tick handler immediately
|
||||
* after processing the virtual timers queue.
|
||||
*/
|
||||
#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__)
|
||||
#define SYSTEM_TICK_EVENT_HOOK() { \
|
||||
/* System tick event code here.*/ \
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief System halt hook.
|
||||
* @details This hook is invoked in case to a system halting error before
|
||||
* the system is halted.
|
||||
*/
|
||||
#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
|
||||
#define SYSTEM_HALT_HOOK() { \
|
||||
/* System halt code here.*/ \
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Port-specific settings (override port settings defaulted in chcore.h). */
|
||||
/*===========================================================================*/
|
||||
|
||||
/* NOTE: When changing this option you also have to enable or disable the FPU
|
||||
in the project options.*/
|
||||
#define CORTEX_USE_FPU TRUE
|
||||
#define CORTEX_ENABLE_WFI_IDLE TRUE
|
||||
|
||||
#endif /* _CHCONF_H_ */
|
||||
|
||||
/** @} */
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 "clock_recovery.hpp"
|
185
Software/portapack-mayhem/firmware/baseband/clock_recovery.hpp
Normal file
185
Software/portapack-mayhem/firmware/baseband/clock_recovery.hpp
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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 __CLOCK_RECOVERY_H__
|
||||
#define __CLOCK_RECOVERY_H__
|
||||
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
#include "linear_resampler.hpp"
|
||||
|
||||
namespace clock_recovery {
|
||||
|
||||
class GardnerTimingErrorDetector {
|
||||
public:
|
||||
static constexpr size_t samples_per_symbol { 2 };
|
||||
|
||||
/*
|
||||
Expects retimed samples at a rate of twice the expected symbol rate.
|
||||
Calculates timing error, sends symbol and error to handler.
|
||||
*/
|
||||
template<typename SymbolHandler>
|
||||
void operator()(
|
||||
const float in,
|
||||
SymbolHandler symbol_handler
|
||||
) {
|
||||
/* NOTE: Algorithm is sensitive to input magnitude. Timing error value
|
||||
* will scale proportionally. Best practice is to use error sign only.
|
||||
*/
|
||||
t[2] = t[1];
|
||||
t[1] = t[0];
|
||||
t[0] = in;
|
||||
|
||||
if( symbol_phase == 0 ) {
|
||||
const auto symbol = t[0];
|
||||
const float lateness = (t[0] - t[2]) * t[1];
|
||||
symbol_handler(symbol, lateness);
|
||||
}
|
||||
|
||||
symbol_phase = (symbol_phase + 1) % samples_per_symbol;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<float, 3> t { };
|
||||
size_t symbol_phase { 0 };
|
||||
};
|
||||
|
||||
class LinearErrorFilter {
|
||||
public:
|
||||
LinearErrorFilter(
|
||||
const float filter_alpha = 0.95f,
|
||||
const float error_weight = -1.0f
|
||||
) : filter_alpha { filter_alpha },
|
||||
error_weight { error_weight }
|
||||
{
|
||||
}
|
||||
|
||||
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:
|
||||
using SymbolHandler = std::function<void(const float)>;
|
||||
|
||||
ClockRecovery(
|
||||
const float sampling_rate,
|
||||
const float symbol_rate,
|
||||
ErrorFilter error_filter,
|
||||
SymbolHandler symbol_handler
|
||||
) : symbol_handler { std::move(symbol_handler) }
|
||||
{
|
||||
configure(sampling_rate, symbol_rate, error_filter);
|
||||
}
|
||||
|
||||
ClockRecovery(
|
||||
SymbolHandler symbol_handler
|
||||
) : symbol_handler { std::move(symbol_handler) }
|
||||
{
|
||||
}
|
||||
|
||||
void configure(
|
||||
const float sampling_rate,
|
||||
const float symbol_rate,
|
||||
ErrorFilter error_filter
|
||||
) {
|
||||
resampler.configure(sampling_rate, symbol_rate * timing_error_detector.samples_per_symbol);
|
||||
error_filter = error_filter;
|
||||
}
|
||||
|
||||
void operator()(
|
||||
const float baseband_sample
|
||||
) {
|
||||
resampler(baseband_sample,
|
||||
[this](const float interpolated_sample) {
|
||||
this->resampler_callback(interpolated_sample);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
dsp::interpolation::LinearResampler resampler { };
|
||||
GardnerTimingErrorDetector timing_error_detector { };
|
||||
ErrorFilter error_filter { };
|
||||
const SymbolHandler symbol_handler;
|
||||
|
||||
void resampler_callback(const float interpolated_sample) {
|
||||
timing_error_detector(interpolated_sample,
|
||||
[this](const float symbol, const float lateness) {
|
||||
this->symbol_callback(symbol, lateness);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void symbol_callback(const float symbol, const float lateness) {
|
||||
// NOTE: This check is to avoid std::function nullptr check, which
|
||||
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
|
||||
// TODO: Make symbol_handler known at compile time.
|
||||
if( symbol_handler) {
|
||||
symbol_handler(symbol);
|
||||
}
|
||||
|
||||
const float adjustment = error_filter(lateness);
|
||||
resampler.advance(adjustment);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace clock_recovery */
|
||||
|
||||
#endif/*__CLOCK_RECOVERY_H__*/
|
1
Software/portapack-mayhem/firmware/baseband/description
Normal file
1
Software/portapack-mayhem/firmware/baseband/description
Normal file
@ -0,0 +1 @@
|
||||
Original firmware's functionalities: Receiver.
|
790
Software/portapack-mayhem/firmware/baseband/dsp_decimate.cpp
Normal file
790
Software/portapack-mayhem/firmware/baseband/dsp_decimate.cpp
Normal file
@ -0,0 +1,790 @@
|
||||
/*
|
||||
* 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 "dsp_decimate.hpp"
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
namespace dsp {
|
||||
namespace decimate {
|
||||
|
||||
static inline complex32_t mac_fs4_shift(
|
||||
const vec2_s16* const z,
|
||||
const vec2_s16* const t,
|
||||
const size_t index,
|
||||
const complex32_t accum
|
||||
) {
|
||||
/* Accumulate sample * tap results for samples already in z buffer.
|
||||
* Multiply using swap/negation to achieve Fs/4 shift.
|
||||
* For iterations where samples are shifting out of z buffer (being discarded).
|
||||
* Expect negated tap t[2] to accomodate instruction set limitations.
|
||||
*/
|
||||
const bool negated_t2 = index & 1;
|
||||
const auto q1_i0 = z[index*2 + 0];
|
||||
const auto i1_q0 = z[index*2 + 1];
|
||||
const auto t1_t0 = t[index];
|
||||
const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real());
|
||||
const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag());
|
||||
return { real, imag };
|
||||
}
|
||||
|
||||
static inline complex32_t mac_shift(
|
||||
const vec2_s16* const z,
|
||||
const vec2_s16* const t,
|
||||
const size_t index,
|
||||
const complex32_t accum
|
||||
) {
|
||||
/* Accumulate sample * tap results for samples already in z buffer.
|
||||
* For iterations where samples are shifting out of z buffer (being discarded).
|
||||
* real += i1 * t1 + i0 * t0
|
||||
* imag += q1 * t1 + q0 * t0
|
||||
*/
|
||||
const auto i1_i0 = z[index*2 + 0];
|
||||
const auto q1_q0 = z[index*2 + 1];
|
||||
const auto t1_t0 = t[index];
|
||||
const auto real = smlad(i1_i0, t1_t0, accum.real());
|
||||
const auto imag = smlad(q1_q0, t1_t0, accum.imag());
|
||||
return { real, imag };
|
||||
}
|
||||
|
||||
static inline complex32_t mac_fs4_shift_and_store(
|
||||
vec2_s16* const z,
|
||||
const vec2_s16* const t,
|
||||
const size_t decimation_factor,
|
||||
const size_t index,
|
||||
const complex32_t accum
|
||||
) {
|
||||
/* Accumulate sample * tap results for samples already in z buffer.
|
||||
* Place new samples into z buffer.
|
||||
* Expect negated tap t[2] to accomodate instruction set limitations.
|
||||
*/
|
||||
const bool negated_t2 = index & 1;
|
||||
const auto q1_i0 = z[decimation_factor + index*2 + 0];
|
||||
const auto i1_q0 = z[decimation_factor + index*2 + 1];
|
||||
const auto t1_t0 = t[decimation_factor / 2 + index];
|
||||
z[index*2 + 0] = q1_i0;
|
||||
const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real());
|
||||
z[index*2 + 1] = i1_q0;
|
||||
const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag());
|
||||
return { real, imag };
|
||||
}
|
||||
|
||||
static inline complex32_t mac_shift_and_store(
|
||||
vec2_s16* const z,
|
||||
const vec2_s16* const t,
|
||||
const size_t decimation_factor,
|
||||
const size_t index,
|
||||
const complex32_t accum
|
||||
) {
|
||||
/* Accumulate sample * tap results for samples already in z buffer.
|
||||
* Place new samples into z buffer.
|
||||
* Expect negated tap t[2] to accomodate instruction set limitations.
|
||||
*/
|
||||
const auto i1_i0 = z[decimation_factor + index*2 + 0];
|
||||
const auto q1_q0 = z[decimation_factor + index*2 + 1];
|
||||
const auto t1_t0 = t[decimation_factor / 2 + index];
|
||||
z[index*2 + 0] = i1_i0;
|
||||
const auto real = smlad(i1_i0, t1_t0, accum.real());
|
||||
z[index*2 + 1] = q1_q0;
|
||||
const auto imag = smlad(q1_q0, t1_t0, accum.imag());
|
||||
return { real, imag };
|
||||
}
|
||||
|
||||
static inline complex32_t mac_fs4_shift_and_store_new_c8_samples(
|
||||
vec2_s16* const z,
|
||||
const vec2_s16* const t,
|
||||
const vec4_s8* const in,
|
||||
const size_t decimation_factor,
|
||||
const size_t index,
|
||||
const size_t length,
|
||||
const complex32_t accum
|
||||
) {
|
||||
/* Accumulate sample * tap results for new samples.
|
||||
* Place new samples into z buffer.
|
||||
* Expect negated tap t[2] to accomodate instruction set limitations.
|
||||
*/
|
||||
const bool negated_t2 = index & 1;
|
||||
const auto q1_i1_q0_i0 = in[index];
|
||||
const auto t1_t0 = t[(length - decimation_factor) / 2 + index];
|
||||
const auto i1_q1_i0_q0 = rev16(q1_i1_q0_i0);
|
||||
const auto i1_q1_q0_i0 = pkhbt(q1_i1_q0_i0, i1_q1_i0_q0);
|
||||
const auto q1_i0 = sxtb16(i1_q1_q0_i0);
|
||||
const auto i1_q0 = sxtb16(i1_q1_q0_i0, 8);
|
||||
z[length - decimation_factor * 2 + index*2 + 0] = q1_i0;
|
||||
const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real());
|
||||
z[length - decimation_factor * 2 + index*2 + 1] = i1_q0;
|
||||
const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag());
|
||||
return { real, imag };
|
||||
}
|
||||
|
||||
static inline complex32_t mac_shift_and_store_new_c16_samples(
|
||||
vec2_s16* const z,
|
||||
const vec2_s16* const t,
|
||||
const vec2_s16* const in,
|
||||
const size_t decimation_factor,
|
||||
const size_t index,
|
||||
const size_t length,
|
||||
const complex32_t accum
|
||||
) {
|
||||
/* Accumulate sample * tap results for new samples.
|
||||
* Place new samples into z buffer.
|
||||
* Expect negated tap t[2] to accomodate instruction set limitations.
|
||||
*/
|
||||
const auto q0_i0 = in[index*2+0];
|
||||
const auto q1_i1 = in[index*2+1];
|
||||
const auto i1_i0 = pkhbt(q0_i0, q1_i1, 16);
|
||||
const auto q1_q0 = pkhtb(q1_i1, q0_i0, 16);
|
||||
const auto t1_t0 = t[(length - decimation_factor) / 2 + index];
|
||||
z[length - decimation_factor * 2 + index*2 + 0] = i1_i0;
|
||||
const auto real = smlad(i1_i0, t1_t0, accum.real());
|
||||
z[length - decimation_factor * 2 + index*2 + 1] = q1_q0;
|
||||
const auto imag = smlad(q1_q0, t1_t0, accum.imag());
|
||||
return { real, imag };
|
||||
}
|
||||
|
||||
static inline uint32_t scale_round_and_pack(
|
||||
const complex32_t value,
|
||||
const int32_t scale_factor
|
||||
) {
|
||||
/* Multiply 32-bit components of the complex<int32_t> by a scale factor,
|
||||
* into int64_ts, then round to nearest LSB (1 << 32), saturate to 16 bits,
|
||||
* and pack into a complex<int16_t>.
|
||||
*/
|
||||
const auto scaled_real = __SMMULR(value.real(), scale_factor);
|
||||
const auto saturated_real = __SSAT(scaled_real, 16);
|
||||
|
||||
const auto scaled_imag = __SMMULR(value.imag(), scale_factor);
|
||||
const auto saturated_imag = __SSAT(scaled_imag, 16);
|
||||
|
||||
return __PKHBT(saturated_real, saturated_imag, 16);
|
||||
}
|
||||
|
||||
template<typename Tap>
|
||||
static void taps_copy(
|
||||
const Tap* const source,
|
||||
Tap* const target,
|
||||
const size_t count,
|
||||
const bool shift_up
|
||||
) {
|
||||
const uint32_t negate_pattern = shift_up ? 0b1110 : 0b0100;
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const bool negate = (negate_pattern >> (i & 3)) & 1;
|
||||
target[i] = negate ? -source[i] : source[i];
|
||||
}
|
||||
}
|
||||
|
||||
// FIRC8xR16x24FS4Decim4 //////////////////////////////////////////////////
|
||||
|
||||
void FIRC8xR16x24FS4Decim4::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
const Shift shift
|
||||
) {
|
||||
taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up);
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC8xR16x24FS4Decim4::execute(
|
||||
const buffer_c8_t& src,
|
||||
const buffer_c16_t& dst
|
||||
) {
|
||||
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
|
||||
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
|
||||
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
|
||||
|
||||
const auto k = output_scale;
|
||||
|
||||
const size_t count = src.count / decimation_factor;
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const vec4_s8* const in = static_cast<const vec4_s8*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
|
||||
|
||||
complex32_t accum;
|
||||
|
||||
// Oldest samples are discarded.
|
||||
accum = mac_fs4_shift(z, t, 0, accum);
|
||||
accum = mac_fs4_shift(z, t, 1, accum);
|
||||
|
||||
// Middle samples are shifted earlier in the "z" delay buffer.
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 4, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 5, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 6, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 7, accum);
|
||||
|
||||
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
|
||||
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum);
|
||||
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum);
|
||||
|
||||
d[i] = scale_round_and_pack(accum, k);
|
||||
}
|
||||
|
||||
return {
|
||||
dst.p,
|
||||
count,
|
||||
src.sampling_rate / decimation_factor
|
||||
};
|
||||
}
|
||||
|
||||
// FIRC8xR16x24FS4Decim8 //////////////////////////////////////////////////
|
||||
|
||||
void FIRC8xR16x24FS4Decim8::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
const Shift shift
|
||||
) {
|
||||
taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up);
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC8xR16x24FS4Decim8::execute(
|
||||
const buffer_c8_t& src,
|
||||
const buffer_c16_t& dst
|
||||
) {
|
||||
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
|
||||
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
|
||||
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
|
||||
|
||||
const auto k = output_scale;
|
||||
|
||||
const size_t count = src.count / decimation_factor;
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const vec4_s8* const in = static_cast<const vec4_s8*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
|
||||
|
||||
complex32_t accum;
|
||||
|
||||
// Oldest samples are discarded.
|
||||
accum = mac_fs4_shift(z, t, 0, accum);
|
||||
accum = mac_fs4_shift(z, t, 1, accum);
|
||||
accum = mac_fs4_shift(z, t, 2, accum);
|
||||
accum = mac_fs4_shift(z, t, 3, accum);
|
||||
|
||||
// Middle samples are shifted earlier in the "z" delay buffer.
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum);
|
||||
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum);
|
||||
|
||||
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
|
||||
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum);
|
||||
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum);
|
||||
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 2, taps_count, accum);
|
||||
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 3, taps_count, accum);
|
||||
|
||||
d[i] = scale_round_and_pack(accum, k);
|
||||
}
|
||||
|
||||
return {
|
||||
dst.p,
|
||||
count,
|
||||
src.sampling_rate / decimation_factor
|
||||
};
|
||||
}
|
||||
|
||||
// FIRC16xR16x16Decim2 ////////////////////////////////////////////////////
|
||||
|
||||
void FIRC16xR16x16Decim2::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
) {
|
||||
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC16xR16x16Decim2::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
) {
|
||||
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
|
||||
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
|
||||
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
|
||||
|
||||
const auto k = output_scale;
|
||||
|
||||
const size_t count = src.count / decimation_factor;
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const vec2_s16* const in = static_cast<const vec2_s16*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
|
||||
|
||||
complex32_t accum;
|
||||
|
||||
// Oldest samples are discarded.
|
||||
accum = mac_shift(z, t, 0, accum);
|
||||
|
||||
// Middle samples are shifted earlier in the "z" delay buffer.
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 0, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 1, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 2, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 3, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 4, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 5, accum);
|
||||
|
||||
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
|
||||
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum);
|
||||
|
||||
d[i] = scale_round_and_pack(accum, k);
|
||||
}
|
||||
|
||||
return {
|
||||
dst.p,
|
||||
count,
|
||||
src.sampling_rate / decimation_factor
|
||||
};
|
||||
}
|
||||
|
||||
// FIRC16xR16x32Decim8 ////////////////////////////////////////////////////
|
||||
|
||||
void FIRC16xR16x32Decim8::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
) {
|
||||
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC16xR16x32Decim8::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
) {
|
||||
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
|
||||
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
|
||||
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
|
||||
|
||||
const auto k = output_scale;
|
||||
|
||||
const size_t count = src.count / decimation_factor;
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const vec2_s16* const in = static_cast<const vec2_s16*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
|
||||
|
||||
complex32_t accum;
|
||||
|
||||
// Oldest samples are discarded.
|
||||
accum = mac_shift(z, t, 0, accum);
|
||||
accum = mac_shift(z, t, 1, accum);
|
||||
accum = mac_shift(z, t, 2, accum);
|
||||
accum = mac_shift(z, t, 3, accum);
|
||||
|
||||
// Middle samples are shifted earlier in the "z" delay buffer.
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 0, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 1, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 2, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 3, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 4, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 5, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 6, accum);
|
||||
accum = mac_shift_and_store(z, t, decimation_factor, 7, accum);
|
||||
|
||||
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
|
||||
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum);
|
||||
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 1, taps_count, accum);
|
||||
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 2, taps_count, accum);
|
||||
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 3, taps_count, accum);
|
||||
|
||||
d[i] = scale_round_and_pack(accum, k);
|
||||
}
|
||||
|
||||
return {
|
||||
dst.p,
|
||||
count,
|
||||
src.sampling_rate / decimation_factor
|
||||
};
|
||||
}
|
||||
|
||||
buffer_c16_t Complex8DecimateBy2CIC3::execute(const buffer_c8_t& src, const buffer_c16_t& dst) {
|
||||
/* Decimates by two using a non-recursive third-order CIC filter.
|
||||
*/
|
||||
|
||||
/* CIC filter (decimating by two):
|
||||
* D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1
|
||||
* D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1
|
||||
*
|
||||
* D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1
|
||||
* D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1
|
||||
*/
|
||||
|
||||
uint32_t i1_i0 = _i1_i0;
|
||||
uint32_t q1_q0 = _q1_q0;
|
||||
|
||||
/* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */
|
||||
constexpr uint32_t scale_factor = 32;
|
||||
constexpr uint32_t k_3_1 = 0x00030001 * scale_factor;
|
||||
uint32_t* src_p = reinterpret_cast<uint32_t*>(&src.p[0]);
|
||||
uint32_t* const src_end = reinterpret_cast<uint32_t*>(&src.p[src.count]);
|
||||
uint32_t* dst_p = reinterpret_cast<uint32_t*>(&dst.p[0]);
|
||||
while(src_p < src_end) {
|
||||
const uint32_t q3_i3_q2_i2 = *(src_p++); // 3
|
||||
const uint32_t q5_i5_q4_i4 = *(src_p++);
|
||||
|
||||
const uint32_t d_i0_partial = __SMUAD(k_3_1, i1_i0); // 1: = 3 * i1 + 1 * i0
|
||||
const uint32_t i3_i2 = __SXTB16(q3_i3_q2_i2, 0); // 1: (q3_i3_q2_i2 ror 0)[23:16]:(q3_i3_q2_i2 ror 0)[7:0]
|
||||
const uint32_t d_i0 = __SMLADX(k_3_1, i3_i2, d_i0_partial); // 1: + 3 * i2 + 1 * i3
|
||||
|
||||
const uint32_t d_q0_partial = __SMUAD(k_3_1, q1_q0); // 1: = 3 * q1 * 1 * q0
|
||||
const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0]
|
||||
const uint32_t d_q0 = __SMLADX(k_3_1, q3_q2, d_q0_partial); // 1: + 3 * q2 + 1 * q3
|
||||
|
||||
const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
const uint32_t d_i1_partial = __SMUAD(k_3_1, i3_i2); // 1: = 3 * i3 + 1 * i2
|
||||
const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0]
|
||||
const uint32_t d_i1 = __SMLADX(k_3_1, i5_i4, d_i1_partial); // 1: + 1 * i5 + 3 * i4
|
||||
|
||||
const uint32_t d_q1_partial = __SMUAD(k_3_1, q3_q2); // 1: = 3 * q3 * 1 * q2
|
||||
const uint32_t q5_q4 = __SXTB16(q5_i5_q4_i4, 8); // 1: (q5_i5_q4_i4 ror 8)[23:16]:(q5_i5_q4_i4 ror 8)[7:0]
|
||||
const uint32_t d_q1 = __SMLADX(k_3_1, q5_q4, d_q1_partial); // 1: + 1 * q5 + 3 * q4
|
||||
|
||||
const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
*(dst_p++) = d_q0_i0; // 3
|
||||
*(dst_p++) = d_q1_i1;
|
||||
|
||||
i1_i0 = i5_i4;
|
||||
q1_q0 = q5_q4;
|
||||
}
|
||||
_i1_i0 = i1_i0;
|
||||
_q1_q0 = q1_q0;
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
buffer_c16_t TranslateByFSOver4AndDecimateBy2CIC3::execute(const buffer_c8_t& src, const buffer_c16_t& dst) {
|
||||
/* Translates incoming complex<int8_t> samples by -fs/4,
|
||||
* decimates by two using a non-recursive third-order CIC filter.
|
||||
*/
|
||||
|
||||
/* Derivation of algorithm:
|
||||
* Original CIC filter (decimating by two):
|
||||
* D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1
|
||||
* D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1
|
||||
*
|
||||
* D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1
|
||||
* D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1
|
||||
*
|
||||
* Translate -fs/4, phased 180 degrees, accomplished by complex multiplication
|
||||
* of complex length-4 sequence:
|
||||
*
|
||||
* Substitute:
|
||||
* i0 = -i0, q0 = -q0
|
||||
* i1 = -q1, q1 = i1
|
||||
* i2 = i2, q2 = q2
|
||||
* i3 = q3, q3 = -i3
|
||||
* i4 = -i4, q4 = -q4
|
||||
* i5 = -q5, q5 = i5
|
||||
*
|
||||
* Resulting taps (with decimation by 2, four samples in, two samples out):
|
||||
* D_I0 = q3 * 1 + i2 * 3 + -q1 * 3 + -i0 * 1
|
||||
* D_Q0 = -i3 * 1 + q2 * 3 + i1 * 3 + -q0 * 1
|
||||
*
|
||||
* D_I1 = -q5 * 1 + -i4 * 3 + q3 * 3 + i2 * 1
|
||||
* D_Q1 = i5 * 1 + -q4 * 3 + -i3 * 3 + q2 * 1
|
||||
*/
|
||||
|
||||
// 6 cycles per complex input sample, not including loop overhead.
|
||||
uint32_t q1_i0 = _q1_i0;
|
||||
uint32_t q0_i1 = _q0_i1;
|
||||
/* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */
|
||||
constexpr uint32_t scale_factor = 32;
|
||||
const uint32_t k_3_1 = 0x00030001 * scale_factor;
|
||||
uint32_t* src_p = reinterpret_cast<uint32_t*>(&src.p[0]);
|
||||
uint32_t* const src_end = reinterpret_cast<uint32_t*>(&src.p[src.count]);
|
||||
uint32_t* dst_p = reinterpret_cast<uint32_t*>(&dst.p[0]);
|
||||
while(src_p < src_end) {
|
||||
const uint32_t q3_i3_q2_i2 = *(src_p++); // 3
|
||||
const uint32_t q5_i5_q4_i4 = *(src_p++);
|
||||
|
||||
const uint32_t i2_i3 = __SXTB16(q3_i3_q2_i2, 16); // 1: (q3_i3_q2_i2 ror 16)[23:16]:(q3_i3_q2_i2 ror 16)[7:0]
|
||||
const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0]
|
||||
const uint32_t i2_q3 = __PKHTB(i2_i3, q3_q2, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
|
||||
const uint32_t i3_q2 = __PKHBT(q3_q2, i2_i3, 16); // 1:(Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
// D_I0 = 3 * (i2 - q1) + (q3 - i0)
|
||||
const uint32_t i2_m_q1_q3_m_i0 = __QSUB16(i2_q3, q1_i0); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
|
||||
const uint32_t d_i0 = __SMUAD(k_3_1, i2_m_q1_q3_m_i0); // 1: Rm[15:0]*Rs[15:0]+Rm[31:16]*Rs[31:16]
|
||||
|
||||
// D_Q0 = 3 * (q2 + i1) - (i3 + q0)
|
||||
const uint32_t i3_p_q0_q2_p_i1 = __QADD16(i3_q2, q0_i1); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
|
||||
const uint32_t d_q0 = __SMUSDX(i3_p_q0_q2_p_i1, k_3_1); // 1: Rm[15:0]*Rs[31:16]–Rm[31:16]*RsX[15:0]
|
||||
const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0]
|
||||
const uint32_t q4_q5 = __SXTB16(q5_i5_q4_i4, 24); // 1: (q5_i5_q4_i4 ror 24)[23:16]:(q5_i5_q4_i4 ror 24)[7:0]
|
||||
const uint32_t q4_i5 = __PKHTB(q4_q5, i5_i4, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
|
||||
const uint32_t q5_i4 = __PKHBT(i5_i4, q4_q5, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
// D_I1 = (i2 - q5) + 3 * (q3 - i4)
|
||||
const uint32_t i2_m_q5_q3_m_i4 = __QSUB16(i2_q3, q5_i4); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
|
||||
const uint32_t d_i1 = __SMUADX(i2_m_q5_q3_m_i4, k_3_1); // 1: Rm[15:0]*Rs[31:16]+Rm[31:16]*Rs[15:0]
|
||||
|
||||
// D_Q1 = (i5 + q2) - 3 * (q4 + i3)
|
||||
const uint32_t q4_p_i3_i5_p_q2 = __QADD16(q4_i5, i3_q2); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
|
||||
const uint32_t d_q1 = __SMUSD(k_3_1, q4_p_i3_i5_p_q2); // 1: Rm[15:0]*Rs[15:0]–Rm[31:16]*Rs[31:16]
|
||||
const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
*(dst_p++) = d_q0_i0; // 3
|
||||
*(dst_p++) = d_q1_i1;
|
||||
|
||||
q1_i0 = q5_i4;
|
||||
q0_i1 = q4_i5;
|
||||
}
|
||||
_q1_i0 = q1_i0;
|
||||
_q0_i1 = q0_i1;
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
buffer_c16_t DecimateBy2CIC3::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
) {
|
||||
/* Complex non-recursive 3rd-order CIC filter (taps 1,3,3,1).
|
||||
* Gain of 8.
|
||||
* Consumes 16 bytes (4 s16:s16 samples) per loop iteration,
|
||||
* Produces 8 bytes (2 s16:s16 samples) per loop iteration.
|
||||
*/
|
||||
uint32_t t1 = _iq0;
|
||||
uint32_t t2 = _iq1;
|
||||
const uint32_t taps = 0x00000003;
|
||||
void* s = src.p;
|
||||
void* d = dst.p;
|
||||
const auto d_end = &dst.p[src.count / 2];
|
||||
while(d < d_end) {
|
||||
uint32_t i = __SXTH(t1, 0); /* 1: I0 */
|
||||
uint32_t q = __SXTH(t1, 16); /* 1: Q0 */
|
||||
i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */
|
||||
q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */
|
||||
|
||||
const uint32_t t3 = *__SIMD32(s)++; /* 3: Q2:I2 */
|
||||
const uint32_t t4 = *__SIMD32(s)++; /* Q3:I3 */
|
||||
|
||||
i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */
|
||||
q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */
|
||||
int32_t si0 = __SXTAH(i, t4, 0); /* 1: I3 + Q2*3 + Q1*3 + Q0 */
|
||||
int32_t sq0 = __SXTAH(q, t4, 16); /* 1: Q3 + Q2*3 + Q1*3 + Q0 */
|
||||
i = __BFI(si0 / 8, sq0 / 8, 16, 16); /* 1: D2_Q0:D2_I0 */
|
||||
*__SIMD32(d)++ = i; /* D2_Q0:D2_I0 */
|
||||
|
||||
i = __SXTH(t3, 0); /* 1: I2 */
|
||||
q = __SXTH(t3, 16); /* 1: Q2 */
|
||||
i = __SMLABB(t4, taps, i); /* 1: I3*3 + I2 */
|
||||
q = __SMLATB(t4, taps, q); /* 1: Q3*3 + Q2 */
|
||||
|
||||
t1 = *__SIMD32(s)++; /* 3: Q4:I4 */
|
||||
t2 = *__SIMD32(s)++; /* Q5:I5 */
|
||||
|
||||
i = __SMLABB(t1, taps, i); /* 1: I4*3 + I3*3 + I2 */
|
||||
q = __SMLATB(t1, taps, q); /* 1: Q4*3 + Q3*3 + Q2 */
|
||||
int32_t si1 = __SXTAH(i, t2, 0) ; /* 1: I5 + Q4*3 + Q3*3 + Q2 */
|
||||
int32_t sq1 = __SXTAH(q, t2, 16); /* 1: Q5 + Q4*3 + Q3*3 + Q2 */
|
||||
i = __BFI(si1 / 8, sq1 / 8, 16, 16); /* 1: D2_Q1:D2_I1 */
|
||||
*__SIMD32(d)++ = i; /* D2_Q1:D2_I1 */
|
||||
}
|
||||
_iq0 = t1;
|
||||
_iq1 = t2;
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
void FIR64AndDecimateBy2Real::configure(
|
||||
const std::array<int16_t, taps_count>& new_taps
|
||||
) {
|
||||
std::copy(new_taps.cbegin(), new_taps.cend(), taps.begin());
|
||||
}
|
||||
|
||||
buffer_s16_t FIR64AndDecimateBy2Real::execute(
|
||||
const buffer_s16_t& src,
|
||||
const buffer_s16_t& dst
|
||||
) {
|
||||
/* 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.p;
|
||||
auto dst_p = dst.p;
|
||||
int32_t n = src.count;
|
||||
for(; n>0; n-=2) {
|
||||
z[taps_count-2] = *(src_p++);
|
||||
z[taps_count-1] = *(src_p++);
|
||||
|
||||
int32_t t = 0;
|
||||
for(size_t j=0; j<taps_count; j+=4) {
|
||||
t += z[j+0] * taps[j+0];
|
||||
t += z[j+1] * taps[j+1];
|
||||
t += z[j+2] * taps[j+2];
|
||||
t += z[j+3] * taps[j+3];
|
||||
|
||||
z[j+0] = z[j+0+2];
|
||||
z[j+1] = z[j+1+2];
|
||||
z[j+2] = z[j+2+2];
|
||||
z[j+3] = z[j+3+2];
|
||||
}
|
||||
*(dst_p++) = t / 65536;
|
||||
}
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
void FIRAndDecimateComplex::configure_common(
|
||||
const size_t taps_count, const size_t decimation_factor
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps_count);
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps_count);
|
||||
taps_count_ = taps_count;
|
||||
decimation_factor_ = decimation_factor;
|
||||
}
|
||||
|
||||
buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
) {
|
||||
/* 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.
|
||||
*/
|
||||
const auto output_sampling_rate = src.sampling_rate / decimation_factor_;
|
||||
const size_t output_samples = src.count / decimation_factor_;
|
||||
|
||||
void* dst_p = dst.p;
|
||||
const buffer_c16_t result { dst.p, output_samples, output_sampling_rate };
|
||||
|
||||
const void* src_p = src.p;
|
||||
size_t outer_count = output_samples;
|
||||
while(outer_count > 0) {
|
||||
/* Put new samples into delay buffer */
|
||||
void* z_new_p = &samples_[taps_count_ - decimation_factor_];
|
||||
for(size_t i=0; i<decimation_factor_; i++) {
|
||||
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
|
||||
}
|
||||
|
||||
size_t loop_count = taps_count_ / 8;
|
||||
void* t_p = &taps_reversed_[0];
|
||||
void* z_p = &samples_[0];
|
||||
|
||||
int64_t t_real = 0;
|
||||
int64_t t_imag = 0;
|
||||
|
||||
while(loop_count > 0) {
|
||||
const auto tap0 = *__SIMD32(t_p)++;
|
||||
const auto sample0 = *__SIMD32(z_p)++;
|
||||
const auto tap1 = *__SIMD32(t_p)++;
|
||||
const auto sample1 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample0, tap0, t_real);
|
||||
t_imag = __SMLALDX(sample0, tap0, t_imag);
|
||||
t_real = __SMLSLD(sample1, tap1, t_real);
|
||||
t_imag = __SMLALDX(sample1, tap1, t_imag);
|
||||
|
||||
const auto tap2 = *__SIMD32(t_p)++;
|
||||
const auto sample2 = *__SIMD32(z_p)++;
|
||||
const auto tap3 = *__SIMD32(t_p)++;
|
||||
const auto sample3 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample2, tap2, t_real);
|
||||
t_imag = __SMLALDX(sample2, tap2, t_imag);
|
||||
t_real = __SMLSLD(sample3, tap3, t_real);
|
||||
t_imag = __SMLALDX(sample3, tap3, t_imag);
|
||||
|
||||
const auto tap4 = *__SIMD32(t_p)++;
|
||||
const auto sample4 = *__SIMD32(z_p)++;
|
||||
const auto tap5 = *__SIMD32(t_p)++;
|
||||
const auto sample5 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample4, tap4, t_real);
|
||||
t_imag = __SMLALDX(sample4, tap4, t_imag);
|
||||
t_real = __SMLSLD(sample5, tap5, t_real);
|
||||
t_imag = __SMLALDX(sample5, tap5, t_imag);
|
||||
|
||||
const auto tap6 = *__SIMD32(t_p)++;
|
||||
const auto sample6 = *__SIMD32(z_p)++;
|
||||
const auto tap7 = *__SIMD32(t_p)++;
|
||||
const auto sample7 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample6, tap6, t_real);
|
||||
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,
|
||||
* all that jazz.
|
||||
*/
|
||||
const int32_t r = t_real >> 16;
|
||||
const int32_t i = t_imag >> 16;
|
||||
const int32_t r_sat = __SSAT(r, 16);
|
||||
const int32_t i_sat = __SSAT(i, 16);
|
||||
*__SIMD32(dst_p)++ = __PKHBT(
|
||||
r_sat,
|
||||
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;
|
||||
|
||||
void* t = &samples_[0];
|
||||
const void* 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) {
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
outer_count--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer_s16_t DecimateBy2CIC4Real::execute(
|
||||
const buffer_s16_t& src,
|
||||
const buffer_s16_t& dst
|
||||
) {
|
||||
auto src_p = src.p;
|
||||
auto dst_p = dst.p;
|
||||
int32_t n = src.count;
|
||||
for(; n>0; n-=2) {
|
||||
/* TODO: Probably a lot of room to optimize... */
|
||||
z[0] = z[2];
|
||||
z[1] = z[3];
|
||||
z[2] = z[4];
|
||||
z[3] = *(src_p++);
|
||||
z[4] = *(src_p++);
|
||||
|
||||
int32_t t = z[0] + z[1] * 4 + z[2] * 6 + z[3] * 4 + z[4];
|
||||
*(dst_p++) = t / 16;
|
||||
}
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
} /* namespace decimate */
|
||||
} /* namespace dsp */
|
264
Software/portapack-mayhem/firmware/baseband/dsp_decimate.hpp
Normal file
264
Software/portapack-mayhem/firmware/baseband/dsp_decimate.hpp
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 __DSP_DECIMATE_H__
|
||||
#define __DSP_DECIMATE_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "simd.hpp"
|
||||
|
||||
namespace dsp {
|
||||
namespace decimate {
|
||||
|
||||
class Complex8DecimateBy2CIC3 {
|
||||
public:
|
||||
buffer_c16_t execute(
|
||||
const buffer_c8_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
uint32_t _i1_i0 { 0 };
|
||||
uint32_t _q1_q0 { 0 };
|
||||
};
|
||||
|
||||
class TranslateByFSOver4AndDecimateBy2CIC3 {
|
||||
public:
|
||||
buffer_c16_t execute(
|
||||
const buffer_c8_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
uint32_t _q1_i0 { 0 };
|
||||
uint32_t _q0_i1 { 0 };
|
||||
};
|
||||
|
||||
class DecimateBy2CIC3 {
|
||||
public:
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
uint32_t _iq0 { 0 };
|
||||
uint32_t _iq1 { 0 };
|
||||
};
|
||||
|
||||
class FIR64AndDecimateBy2Real {
|
||||
public:
|
||||
static constexpr size_t taps_count = 64;
|
||||
|
||||
void configure(
|
||||
const std::array<int16_t, taps_count>& taps
|
||||
);
|
||||
|
||||
buffer_s16_t execute(
|
||||
const buffer_s16_t& src,
|
||||
const buffer_s16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<int16_t, taps_count + 2> z { };
|
||||
std::array<int16_t, taps_count> taps { };
|
||||
};
|
||||
|
||||
class FIRC8xR16x24FS4Decim4 {
|
||||
public:
|
||||
static constexpr size_t taps_count = 24;
|
||||
static constexpr size_t decimation_factor = 4;
|
||||
|
||||
using sample_t = complex8_t;
|
||||
using tap_t = int16_t;
|
||||
|
||||
enum class Shift : bool {
|
||||
Down = true,
|
||||
Up = false
|
||||
};
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
const Shift shift = Shift::Down
|
||||
);
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c8_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
|
||||
std::array<tap_t, taps_count> taps_ { };
|
||||
int32_t output_scale = 0;
|
||||
};
|
||||
|
||||
class FIRC8xR16x24FS4Decim8 {
|
||||
public:
|
||||
static constexpr size_t taps_count = 24;
|
||||
static constexpr size_t decimation_factor = 8;
|
||||
|
||||
using sample_t = complex8_t;
|
||||
using tap_t = int16_t;
|
||||
|
||||
enum class Shift : bool {
|
||||
Down = true,
|
||||
Up = false
|
||||
};
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
const Shift shift = Shift::Down
|
||||
);
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c8_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
|
||||
std::array<tap_t, taps_count> taps_ { };
|
||||
int32_t output_scale = 0;
|
||||
};
|
||||
|
||||
class FIRC16xR16x16Decim2 {
|
||||
public:
|
||||
static constexpr size_t taps_count = 16;
|
||||
static constexpr size_t decimation_factor = 2;
|
||||
|
||||
using sample_t = complex16_t;
|
||||
using tap_t = int16_t;
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
);
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
|
||||
std::array<tap_t, taps_count> taps_ { };
|
||||
int32_t output_scale = 0;
|
||||
};
|
||||
|
||||
class FIRC16xR16x32Decim8 {
|
||||
public:
|
||||
static constexpr size_t taps_count = 32;
|
||||
static constexpr size_t decimation_factor = 8;
|
||||
|
||||
using sample_t = complex16_t;
|
||||
using tap_t = int16_t;
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
);
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<vec2_s16, taps_count - decimation_factor> z_ { };
|
||||
std::array<tap_t, taps_count> taps_ { };
|
||||
int32_t output_scale = 0;
|
||||
};
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
void configure(
|
||||
const T& taps,
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
configure(taps.data(), taps.size(), decimation_factor);
|
||||
}
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
using samples_t = sample_t[];
|
||||
|
||||
std::unique_ptr<samples_t> samples_ { };
|
||||
std::unique_ptr<taps_t> taps_reversed_ { };
|
||||
size_t taps_count_ { 0 };
|
||||
size_t decimation_factor_ { 1 };
|
||||
|
||||
template<typename T>
|
||||
void configure(
|
||||
const T* const taps,
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
configure_common(taps_count, decimation_factor);
|
||||
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
|
||||
}
|
||||
|
||||
void configure_common(
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor
|
||||
);
|
||||
};
|
||||
|
||||
class DecimateBy2CIC4Real {
|
||||
public:
|
||||
buffer_s16_t execute(
|
||||
const buffer_s16_t& src,
|
||||
const buffer_s16_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
int16_t z[5] { };
|
||||
int16_t _dummy { }; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0?
|
||||
};
|
||||
|
||||
} /* namespace decimate */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__DSP_DECIMATE_H__*/
|
151
Software/portapack-mayhem/firmware/baseband/dsp_demodulate.cpp
Normal file
151
Software/portapack-mayhem/firmware/baseband/dsp_demodulate.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 "dsp_demodulate.hpp"
|
||||
|
||||
#include "complex.hpp"
|
||||
#include "fxpt_atan2.hpp"
|
||||
#include "utility_m4.hpp"
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
namespace dsp {
|
||||
namespace demodulate {
|
||||
|
||||
buffer_f32_t AM::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
) {
|
||||
const void* src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
auto dst_p = dst.p;
|
||||
while(src_p < src_end) {
|
||||
const uint32_t sample0 = *__SIMD32(src_p)++;
|
||||
const uint32_t sample1 = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
|
||||
const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
|
||||
*(dst_p++) = __builtin_sqrtf(mag_sq0) * k;
|
||||
*(dst_p++) = __builtin_sqrtf(mag_sq1) * k;
|
||||
}
|
||||
|
||||
return { dst.p, src.count, src.sampling_rate };
|
||||
}
|
||||
|
||||
buffer_f32_t SSB::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
) {
|
||||
const complex16_t* src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
auto dst_p = dst.p;
|
||||
while(src_p < src_end) {
|
||||
*(dst_p++) = (src_p++)->real() * k;
|
||||
*(dst_p++) = (src_p++)->real() * k;
|
||||
*(dst_p++) = (src_p++)->real() * k;
|
||||
*(dst_p++) = (src_p++)->real() * k;
|
||||
}
|
||||
|
||||
return { dst.p, src.count, src.sampling_rate };
|
||||
}
|
||||
/*
|
||||
static inline float angle_approx_4deg0(const complex32_t t) {
|
||||
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
|
||||
return 16384.0f * x;
|
||||
}
|
||||
*/
|
||||
static inline float angle_approx_0deg27(const complex32_t t) {
|
||||
if( t.real() ) {
|
||||
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
|
||||
return x / (1.0f + 0.28086f * x * x);
|
||||
} else {
|
||||
return (t.imag() < 0) ? -1.5707963268f : 1.5707963268f;
|
||||
}
|
||||
}
|
||||
|
||||
static inline float angle_precise(const complex32_t t) {
|
||||
return atan2f(t.imag(), t.real());
|
||||
}
|
||||
|
||||
buffer_f32_t FM::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
) {
|
||||
auto z = z_;
|
||||
|
||||
const void* src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
auto dst_p = dst.p;
|
||||
while(src_p < src_end) {
|
||||
const auto s0 = *__SIMD32(src_p)++;
|
||||
const auto s1 = *__SIMD32(src_p)++;
|
||||
const auto t0 = multiply_conjugate_s16_s32(s0, z);
|
||||
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
|
||||
z = s1;
|
||||
*(dst_p++) = angle_precise(t0) * kf;
|
||||
*(dst_p++) = angle_precise(t1) * kf;
|
||||
}
|
||||
z_ = z;
|
||||
|
||||
return { dst.p, src.count, src.sampling_rate };
|
||||
}
|
||||
|
||||
buffer_s16_t FM::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_s16_t& dst
|
||||
) {
|
||||
auto z = z_;
|
||||
|
||||
const void* src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
void* dst_p = dst.p;
|
||||
while(src_p < src_end) {
|
||||
const auto s0 = *__SIMD32(src_p)++;
|
||||
const auto s1 = *__SIMD32(src_p)++;
|
||||
const auto t0 = multiply_conjugate_s16_s32(s0, z);
|
||||
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
|
||||
z = s1;
|
||||
const int32_t theta0_int = angle_approx_0deg27(t0) * ks16;
|
||||
const int32_t theta0_sat = __SSAT(theta0_int, 16);
|
||||
const int32_t theta1_int = angle_approx_0deg27(t1) * ks16;
|
||||
const int32_t theta1_sat = __SSAT(theta1_int, 16);
|
||||
*__SIMD32(dst_p)++ = __PKHBT(
|
||||
theta0_sat,
|
||||
theta1_sat,
|
||||
16
|
||||
);
|
||||
}
|
||||
z_ = z;
|
||||
|
||||
return { dst.p, src.count, src.sampling_rate };
|
||||
}
|
||||
|
||||
void FM::configure(const float sampling_rate, const float deviation_hz) {
|
||||
/*
|
||||
* angle: -pi to pi. output range: -32768 to 32767.
|
||||
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
|
||||
* delta_theta_max = 2 * pi * deviation / sampling_rate
|
||||
*/
|
||||
kf = static_cast<float>(1.0f / (2.0 * pi * deviation_hz / sampling_rate));
|
||||
ks16 = 32767.0f * kf;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
||||
#ifndef __DSP_DEMODULATE_H__
|
||||
#define __DSP_DEMODULATE_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
namespace dsp {
|
||||
namespace demodulate {
|
||||
|
||||
class AM {
|
||||
public:
|
||||
buffer_f32_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
static constexpr float k = 1.0f / 32768.0f;
|
||||
};
|
||||
|
||||
class SSB {
|
||||
public:
|
||||
buffer_f32_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
);
|
||||
|
||||
private:
|
||||
static constexpr float k = 1.0f / 32768.0f;
|
||||
};
|
||||
|
||||
class FM {
|
||||
public:
|
||||
buffer_f32_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
);
|
||||
|
||||
buffer_s16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_s16_t& dst
|
||||
);
|
||||
|
||||
void configure(const float sampling_rate, const float deviation_hz);
|
||||
|
||||
private:
|
||||
complex16_t::rep_type z_ { 0 };
|
||||
float kf { 0 };
|
||||
float ks16 { 0 };
|
||||
};
|
||||
|
||||
} /* namespace demodulate */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__DSP_DEMODULATE_H__*/
|
56
Software/portapack-mayhem/firmware/baseband/dsp_goertzel.cpp
Normal file
56
Software/portapack-mayhem/firmware/baseband/dsp_goertzel.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 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 "dsp_goertzel.hpp"
|
||||
|
||||
#include "complex.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
GoertzelDetector::GoertzelDetector(
|
||||
const float frequency,
|
||||
const uint32_t sample_rate
|
||||
) {
|
||||
coefficient = 2.0 * sin_f32((2.0 * pi * frequency / sample_rate) - pi / 2.0);
|
||||
}
|
||||
|
||||
float GoertzelDetector::execute(
|
||||
const buffer_s16_t& src
|
||||
) {
|
||||
|
||||
const size_t count = src.count;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
s[2] = s[1];
|
||||
s[1] = s[0];
|
||||
s[0] = src.p[i] + coefficient * s[1] - s[2];
|
||||
}
|
||||
|
||||
const uint32_t sq0 = s[0] * s[0];
|
||||
const uint32_t sq1 = s[1] * s[1];
|
||||
float magnitude = __builtin_sqrtf(sq0 + sq1 - s[0] * s[1] * coefficient);
|
||||
|
||||
return magnitude;
|
||||
}
|
||||
|
||||
} /* namespace dsp */
|
43
Software/portapack-mayhem/firmware/baseband/dsp_goertzel.hpp
Normal file
43
Software/portapack-mayhem/firmware/baseband/dsp_goertzel.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 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 __DSP_GOERTZEL_H__
|
||||
#define __DSP_GOERTZEL_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
class GoertzelDetector {
|
||||
public:
|
||||
GoertzelDetector(const float frequency, const uint32_t sample_rate);
|
||||
|
||||
float execute(const buffer_s16_t& src);
|
||||
|
||||
private:
|
||||
float coefficient { };
|
||||
int16_t s[2] { 0 };
|
||||
};
|
||||
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__DSP_GOERTZEL_H__*/
|
57
Software/portapack-mayhem/firmware/baseband/dsp_hilbert.cpp
Normal file
57
Software/portapack-mayhem/firmware/baseband/dsp_hilbert.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Belousov Oleg
|
||||
*
|
||||
* 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_hilbert.hpp"
|
||||
#include "dsp_sos_config.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
HilbertTransform::HilbertTransform() {
|
||||
n = 0;
|
||||
|
||||
sos_i.configure(half_band_lpf_config);
|
||||
sos_q.configure(half_band_lpf_config);
|
||||
}
|
||||
|
||||
void HilbertTransform::execute(float in, float &out_i, float &out_q) {
|
||||
float a = 0, b = 0;
|
||||
|
||||
switch (n) {
|
||||
case 0: a = in; b = 0; break;
|
||||
case 1: a = 0; b = -in; break;
|
||||
case 2: a = -in; b = 0; break;
|
||||
case 3: a = 0; b = in; break;
|
||||
}
|
||||
|
||||
float i = sos_i.execute(a) * 2.0f;
|
||||
float q = sos_q.execute(b) * 2.0f;
|
||||
|
||||
switch (n) {
|
||||
case 0: out_i = i; out_q = q; break;
|
||||
case 1: out_i = -q; out_q = i; break;
|
||||
case 2: out_i = -i; out_q = -q; break;
|
||||
case 3: out_i = q; out_q = -i; break;
|
||||
}
|
||||
|
||||
n = (n + 1) % 4;
|
||||
}
|
||||
|
||||
} /* namespace dsp */
|
44
Software/portapack-mayhem/firmware/baseband/dsp_hilbert.hpp
Normal file
44
Software/portapack-mayhem/firmware/baseband/dsp_hilbert.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Belousov Oleg
|
||||
*
|
||||
* 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_HILBERT_H__
|
||||
#define __DSP_HILBERT_H__
|
||||
|
||||
#include "dsp_sos.hpp"
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
class HilbertTransform {
|
||||
public:
|
||||
|
||||
HilbertTransform();
|
||||
void execute(float in, float &out_i, float &out_q);
|
||||
|
||||
private:
|
||||
uint8_t n = 0;
|
||||
SOSFilter<5> sos_i = {};
|
||||
SOSFilter<5> sos_q = {};
|
||||
};
|
||||
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__DSP_HILBERT_H__*/
|
225
Software/portapack-mayhem/firmware/baseband/dsp_modulate.cpp
Normal file
225
Software/portapack-mayhem/firmware/baseband/dsp_modulate.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Belousov Oleg
|
||||
*
|
||||
* 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_modulate.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "tonesets.hpp"
|
||||
|
||||
namespace dsp {
|
||||
namespace modulate {
|
||||
|
||||
Modulator::~Modulator() {
|
||||
}
|
||||
|
||||
Mode Modulator::get_mode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
void Modulator::set_mode(Mode new_mode) {
|
||||
mode = new_mode;
|
||||
}
|
||||
|
||||
void Modulator::set_over(uint32_t new_over) {
|
||||
over = new_over;
|
||||
}
|
||||
|
||||
void Modulator::set_gain_vumeter_beep(float new_audio_gain , bool new_play_beep ) {
|
||||
audio_gain = new_audio_gain ;
|
||||
play_beep = new_play_beep;
|
||||
|
||||
}
|
||||
|
||||
int32_t Modulator::apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message ) {
|
||||
|
||||
if (play_beep) { // We need to add audio beep sample.
|
||||
if (new_beep_timer) {
|
||||
new_beep_timer--;
|
||||
} else {
|
||||
new_beep_timer = baseband_fs * 0.05; // 50ms
|
||||
|
||||
if (new_beep_index == BEEP_TONES_NB) {
|
||||
configured_in = false;
|
||||
shared_memory.application_queue.push(new_txprogress_message);
|
||||
} else {
|
||||
beep_gen.configure(beep_deltas[new_beep_index], 1.0); // config sequentially the audio beep tone.
|
||||
new_beep_index++;
|
||||
}
|
||||
}
|
||||
sample_in = beep_gen.process(0); // Get sample of the selected sequence of 6 beep tones , and overwrite audio sample. Mix 0%.
|
||||
}
|
||||
return sample_in; // Return audio mic scaled with gain , 8 bit sample or audio beep sample.
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
|
||||
SSB::SSB() : hilbert() {
|
||||
mode = Mode::LSB;
|
||||
}
|
||||
|
||||
void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer,TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
|
||||
// No way to activate correctly the roger beep in this option, Maybe not enough M4 CPU power , Let's block roger beep in SSB selection by now .
|
||||
int32_t sample = 0;
|
||||
int8_t re = 0, im = 0;
|
||||
|
||||
for (size_t counter = 0; counter < buffer.count; counter++) {
|
||||
if (counter % 128 == 0) {
|
||||
float i = 0.0, q = 0.0;
|
||||
|
||||
sample = audio.p[counter / over] >> 2;
|
||||
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
|
||||
|
||||
//switch (mode) {
|
||||
//case Mode::LSB:
|
||||
hilbert.execute(sample / 32768.0f, i, q);
|
||||
//case Mode::USB: hilbert.execute(sample / 32768.0f, q, i);
|
||||
//default: break;
|
||||
//}
|
||||
|
||||
i *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
|
||||
q *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
|
||||
switch (mode) {
|
||||
case Mode::LSB: re = q; im = i; break;
|
||||
case Mode::USB: re = i; im = q; break;
|
||||
default: re = 0; im = 0; break;
|
||||
}
|
||||
//re = q;
|
||||
//im = i;
|
||||
//break;
|
||||
|
||||
}
|
||||
|
||||
buffer.p[counter] = { re, im };
|
||||
|
||||
// Update vu-meter bar in the LCD screen.
|
||||
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
|
||||
|
||||
if (new_power_acc_count) {
|
||||
new_power_acc_count--;
|
||||
} else { // power_acc_count = 0
|
||||
new_power_acc_count = new_divider;
|
||||
new_level_message.value = power_acc / (new_divider *8); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter .
|
||||
shared_memory.application_queue.push(new_level_message);
|
||||
power_acc = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
FM::FM() {
|
||||
mode = Mode::FM;
|
||||
}
|
||||
|
||||
void FM::set_fm_delta(uint32_t new_delta) {
|
||||
fm_delta = new_delta;
|
||||
}
|
||||
|
||||
void FM::set_tone_gen_configure(const uint32_t set_delta, const float set_tone_mix_weight) {
|
||||
tone_gen.configure(set_delta, set_tone_mix_weight);
|
||||
}
|
||||
|
||||
void FM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
|
||||
int32_t sample = 0;
|
||||
int8_t re, im;
|
||||
|
||||
for (size_t counter = 0; counter < buffer.count; counter++) {
|
||||
|
||||
sample = audio.p[counter>>6] >> 8; // sample = audio.p[counter / over] >> 8; (not enough efficient running code, over = 1536000/240000= 64 )
|
||||
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
|
||||
|
||||
if (play_beep) {
|
||||
sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message ); // Apply beep -if selected - atom ,sample by sample.
|
||||
} else {
|
||||
// Update vu-meter bar in the LCD screen.
|
||||
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
|
||||
|
||||
if (new_power_acc_count) {
|
||||
new_power_acc_count--;
|
||||
} else { // power_acc_count = 0
|
||||
new_power_acc_count = new_divider;
|
||||
new_level_message.value = power_acc / (new_divider / 4); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter .
|
||||
shared_memory.application_queue.push(new_level_message);
|
||||
power_acc = 0;
|
||||
}
|
||||
// TODO: pending to optimize CPU running code.
|
||||
// So far , we can not handle all 3 issues at the same time (vu-meter , CTCSS, beep).
|
||||
sample = tone_gen.process(sample); // Add selected Key_Tone or CTCSS subtone , atom function() , sample by sample.
|
||||
}
|
||||
|
||||
delta = sample * fm_delta; // Modulate FM
|
||||
|
||||
phase += delta;
|
||||
sphase = phase >> 24;
|
||||
|
||||
re = (sine_table_i8[(sphase + 64) & 255]);
|
||||
im = (sine_table_i8[sphase]);
|
||||
|
||||
buffer.p[counter] = { re, im };
|
||||
}
|
||||
}
|
||||
|
||||
AM::AM() {
|
||||
mode = Mode::AM;
|
||||
}
|
||||
|
||||
void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
|
||||
int32_t sample = 0;
|
||||
int8_t re = 0, im = 0;
|
||||
float q = 0.0;
|
||||
|
||||
for (size_t counter = 0; counter < buffer.count; counter++) {
|
||||
if (counter % 128 == 0) {
|
||||
sample = audio.p[counter / over] >> 2;
|
||||
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
|
||||
}
|
||||
|
||||
if (play_beep) {
|
||||
sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message )<<5; // Apply beep -if selected - atom sample by sample.
|
||||
} else {
|
||||
// Update vu-meter bar in the LCD screen.
|
||||
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
|
||||
|
||||
if (new_power_acc_count) {
|
||||
new_power_acc_count--;
|
||||
} else { // power_acc_count = 0
|
||||
new_power_acc_count = new_divider;
|
||||
new_level_message.value = power_acc / (new_divider *8); // Why ?orig / (new_divider / 4); // Why ?
|
||||
shared_memory.application_queue.push(new_level_message);
|
||||
power_acc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
q = sample / 32768.0f;
|
||||
q *= 256.0f; // Original 64.0f,now x4 (+12 dB's BB_modulation in AM & DSB)
|
||||
switch (mode) {
|
||||
case Mode::AM: re = q + 80; im = q + 80; break; // Original DC add +20_DC_level=carrier,now x4 (+12dB's AM carrier)
|
||||
case Mode::DSB: re = q; im = q; break;
|
||||
default: break;
|
||||
}
|
||||
buffer.p[counter] = { re, im };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
117
Software/portapack-mayhem/firmware/baseband/dsp_modulate.hpp
Normal file
117
Software/portapack-mayhem/firmware/baseband/dsp_modulate.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Belousov Oleg
|
||||
*
|
||||
* 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_MODULATE_H__
|
||||
#define __DSP_MODULATE_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "dsp_hilbert.hpp"
|
||||
#include "tone_gen.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
namespace dsp {
|
||||
namespace modulate {
|
||||
|
||||
enum class Mode {
|
||||
None,
|
||||
AM,
|
||||
DSB,
|
||||
LSB,
|
||||
USB,
|
||||
FM
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
class Modulator {
|
||||
public:
|
||||
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer,bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) = 0;
|
||||
virtual ~Modulator();
|
||||
|
||||
Mode get_mode();
|
||||
void set_mode(Mode new_mode);
|
||||
|
||||
void set_over(uint32_t new_over);
|
||||
void set_gain_vumeter_beep(float new_audio_gain , bool new_play_beep );
|
||||
int32_t apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message );
|
||||
float audio_gain { };
|
||||
bool play_beep { false };
|
||||
uint32_t power_acc_count { 0 }; // this var it is initialized from Proc_mictx.cpp
|
||||
uint32_t divider { }; // this var it is initialized from Proc_mictx.cpp
|
||||
uint64_t power_acc { 0 }; // it is aux Accumulated sum (Absolute sample signal) , initialitzed to zero.
|
||||
AudioLevelReportMessage level_message { };
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 1536000U;
|
||||
TXProgressMessage txprogress_message { };
|
||||
ToneGen beep_gen { };
|
||||
uint32_t beep_index { }, beep_timer { };
|
||||
|
||||
|
||||
protected:
|
||||
uint32_t over = 1;
|
||||
Mode mode = Mode::None;
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
class SSB : public Modulator {
|
||||
public:
|
||||
SSB();
|
||||
|
||||
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider );
|
||||
|
||||
private:
|
||||
dsp::HilbertTransform hilbert;
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
class FM : public Modulator {
|
||||
public:
|
||||
FM();
|
||||
|
||||
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) ;
|
||||
void set_fm_delta(uint32_t new_delta);
|
||||
void set_tone_gen_configure(const uint32_t delta, const float tone_mix_weight);
|
||||
|
||||
///
|
||||
|
||||
private:
|
||||
uint32_t fm_delta { 0 };
|
||||
uint32_t phase { 0 }, sphase { 0 };
|
||||
int32_t sample { 0 }, delta { };
|
||||
ToneGen tone_gen { };
|
||||
|
||||
|
||||
};
|
||||
|
||||
class AM : public Modulator {
|
||||
public:
|
||||
AM();
|
||||
|
||||
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider );
|
||||
};
|
||||
|
||||
} /* namespace modulate */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__DSP_MODULATE_H__*/
|
53
Software/portapack-mayhem/firmware/baseband/dsp_squelch.cpp
Normal file
53
Software/portapack-mayhem/firmware/baseband/dsp_squelch.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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(const buffer_f32_t& audio) {
|
||||
if( threshold_squared == 0.0f ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: No hard-coded array size.
|
||||
std::array<float, N> squelch_energy_buffer;
|
||||
const buffer_f32_t squelch_energy {
|
||||
squelch_energy_buffer.data(),
|
||||
squelch_energy_buffer.size()
|
||||
};
|
||||
non_audio_hpf.execute(audio, squelch_energy);
|
||||
|
||||
float non_audio_max_squared = 0;
|
||||
for(const auto sample : squelch_energy_buffer) {
|
||||
const float sample_squared = sample * sample;
|
||||
if( sample_squared > non_audio_max_squared ) {
|
||||
non_audio_max_squared = sample_squared;
|
||||
}
|
||||
}
|
||||
|
||||
return (non_audio_max_squared < threshold_squared);
|
||||
}
|
||||
|
||||
void FMSquelch::set_threshold(const float new_value) {
|
||||
threshold_squared = new_value * new_value;
|
||||
}
|
45
Software/portapack-mayhem/firmware/baseband/dsp_squelch.hpp
Normal file
45
Software/portapack-mayhem/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(const buffer_f32_t& audio);
|
||||
|
||||
void set_threshold(const float new_value);
|
||||
|
||||
private:
|
||||
static constexpr size_t N = 32;
|
||||
float threshold_squared { 0.0f };
|
||||
|
||||
IIRBiquadFilter non_audio_hpf { non_audio_hpf_config };
|
||||
};
|
||||
|
||||
#endif/*__DSP_SQUELCH_H__*/
|
122
Software/portapack-mayhem/firmware/baseband/event_m4.cpp
Normal file
122
Software/portapack-mayhem/firmware/baseband/event_m4.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 "event_m4.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "message_queue.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#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;
|
||||
|
||||
EventDispatcher::EventDispatcher(
|
||||
std::unique_ptr<BasebandProcessor> baseband_processor
|
||||
) : baseband_processor { std::move(baseband_processor) }
|
||||
{
|
||||
}
|
||||
|
||||
void EventDispatcher::run() {
|
||||
thread_event_loop = chThdSelf();
|
||||
|
||||
lpc43xx::creg::m0apptxevent::enable();
|
||||
|
||||
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() {
|
||||
const auto message = shared_memory.baseband_message;
|
||||
if( message ) {
|
||||
on_message(message);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::Shutdown:
|
||||
on_message_shutdown(*reinterpret_cast<const ShutdownMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
on_message_default(message);
|
||||
shared_memory.baseband_message = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message_shutdown(const ShutdownMessage&) {
|
||||
request_stop();
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message_default(const Message* const message) {
|
||||
baseband_processor->on_message(message);
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_spectrum() {
|
||||
const UpdateSpectrumMessage message;
|
||||
baseband_processor->on_message(&message);
|
||||
}
|
72
Software/portapack-mayhem/firmware/baseband/event_m4.hpp
Normal file
72
Software/portapack-mayhem/firmware/baseband/event_m4.hpp
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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
EventDispatcher(std::unique_ptr<BasebandProcessor> baseband_processor);
|
||||
|
||||
void run();
|
||||
void request_stop();
|
||||
|
||||
static inline void events_flag(const eventmask_t events) {
|
||||
chEvtSignal(thread_event_loop, events);
|
||||
}
|
||||
|
||||
static inline void events_flag_isr(const eventmask_t events) {
|
||||
chEvtSignalI(thread_event_loop, events);
|
||||
}
|
||||
|
||||
private:
|
||||
static Thread* thread_event_loop;
|
||||
|
||||
std::unique_ptr<BasebandProcessor> baseband_processor;
|
||||
|
||||
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__*/
|
152
Software/portapack-mayhem/firmware/baseband/fxpt_atan2.cpp
Normal file
152
Software/portapack-mayhem/firmware/baseband/fxpt_atan2.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* fxpt_atan2.c
|
||||
*
|
||||
* Copyright (C) 2012, Xo Wang
|
||||
*
|
||||
* Hacked up to be a bit more ARM-friendly by:
|
||||
* Copyright (C) 2013 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Convert floating point to Q15 (1.0.15 fixed point) format.
|
||||
*
|
||||
* @param d floating-point value within range -1 to (1 - (2**-15)), inclusive
|
||||
* @return Q15 value representing d; same range
|
||||
*/
|
||||
/*
|
||||
static inline int16_t q15_from_double(const double d) {
|
||||
return lrint(d * 32768);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Negative absolute value. Used to avoid undefined behavior for most negative
|
||||
* integer (see C99 standard 7.20.6.1.2 and footnote 265 for the description of
|
||||
* abs/labs/llabs behavior).
|
||||
*
|
||||
* @param i 16-bit signed integer
|
||||
* @return negative absolute value of i; defined for all values of i
|
||||
*/
|
||||
/*
|
||||
static inline int16_t s16_nabs(const int16_t j) {
|
||||
#if (((int16_t)-1) >> 1) == ((int16_t)-1)
|
||||
// signed right shift sign-extends (arithmetic)
|
||||
const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement
|
||||
// if j is positive (negSign is -1), xor will invert j and sub will add 1
|
||||
// otherwise j is unchanged
|
||||
return (j ^ negSign) - negSign;
|
||||
#else
|
||||
return (j < 0 ? j : -j);
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Q15 (1.0.15 fixed point) multiplication. Various common rounding modes are in
|
||||
* the function definition for reference (and preference).
|
||||
*
|
||||
* @param j 16-bit signed integer representing -1 to (1 - (2**-15)), inclusive
|
||||
* @param k same format as j
|
||||
* @return product of j and k, in same format
|
||||
*/
|
||||
static inline int16_t q15_mul(const int16_t j, const int16_t k) {
|
||||
const int32_t intermediate = j * k;
|
||||
#if 0 // don't round
|
||||
return intermediate >> 15;
|
||||
#elif 0 // biased rounding
|
||||
return (intermediate + 0x4000) >> 15;
|
||||
#else // unbiased rounding
|
||||
return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Q15 (1.0.15 fixed point) division (non-saturating). Be careful when using
|
||||
* this function, as it does not behave well when the result is out-of-range.
|
||||
*
|
||||
* Value is not defined if numerator is greater than or equal to denominator.
|
||||
*
|
||||
* @param numer 16-bit signed integer representing -1 to (1 - (2**-15))
|
||||
* @param denom same format as numer; must be greater than numerator
|
||||
* @return numer / denom in same format as numer and denom
|
||||
*/
|
||||
static inline int16_t q15_div(const int16_t numer, const int16_t denom) {
|
||||
return (static_cast<int32_t>(numer) << 15) / denom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 16-bit fixed point four-quadrant arctangent. Given some Cartesian vector
|
||||
* (x, y), find the angle subtended by the vector and the positive x-axis.
|
||||
*
|
||||
* The value returned is in units of 1/65536ths of one turn. This allows the use
|
||||
* of the full 16-bit unsigned range to represent a turn. e.g. 0x0000 is 0
|
||||
* radians, 0x8000 is pi radians, and 0xFFFF is (65535 / 32768) * pi radians.
|
||||
*
|
||||
* Because the magnitude of the input vector does not change the angle it
|
||||
* represents, the inputs can be in any signed 16-bit fixed-point format.
|
||||
*
|
||||
* @param y y-coordinate in signed 16-bit
|
||||
* @param x x-coordinate in signed 16-bit
|
||||
* @return angle in (val / 32768) * pi radian increments from 0x0000 to 0xFFFF
|
||||
*/
|
||||
|
||||
static inline int16_t nabs(const int16_t j) {
|
||||
//return -abs(x);
|
||||
return (j < 0 ? j : -j);
|
||||
}
|
||||
|
||||
int16_t fxpt_atan2(const int16_t y, const int16_t x) {
|
||||
static const int16_t k1 = 2847;
|
||||
static const int16_t k2 = 11039;
|
||||
if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
|
||||
if (y > 0) { // 1/8
|
||||
return 8192;
|
||||
} else if (y < 0) { // 5/8
|
||||
return 40960;
|
||||
} else { // x = y = 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
const int16_t nabs_y = nabs(y);
|
||||
const int16_t nabs_x = nabs(x);
|
||||
if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
|
||||
const int16_t y_over_x = q15_div(y, x);
|
||||
const int16_t correction = q15_mul(k1, nabs(y_over_x));
|
||||
const int16_t unrotated = q15_mul(k2 + correction, y_over_x);
|
||||
if (x > 0) { // octants 1, 8
|
||||
return unrotated;
|
||||
} else { // octants 4, 5
|
||||
return 32768 + unrotated;
|
||||
}
|
||||
} else { // octants 2, 3, 6, 7
|
||||
const int16_t x_over_y = q15_div(x, y);
|
||||
const int16_t correction = q15_mul(k1, nabs(x_over_y));
|
||||
const int16_t unrotated = q15_mul(k2 + correction, x_over_y);
|
||||
if (y > 0) { // octants 2, 3
|
||||
return 16384 - unrotated;
|
||||
} else { // octants 6, 7
|
||||
return 49152 - unrotated;
|
||||
}
|
||||
}
|
||||
}
|
29
Software/portapack-mayhem/firmware/baseband/fxpt_atan2.hpp
Normal file
29
Software/portapack-mayhem/firmware/baseband/fxpt_atan2.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 __FXPT_ATAN2_H__
|
||||
#define __FXPT_ATAN2_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
int16_t fxpt_atan2(const int16_t y, const int16_t x);
|
||||
|
||||
#endif/*__FXPT_ATAN2_H__*/
|
225
Software/portapack-mayhem/firmware/baseband/gpdma_lli.hpp
Normal file
225
Software/portapack-mayhem/firmware/baseband/gpdma_lli.hpp
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
namespace lpc43xx {
|
||||
namespace gpdma {
|
||||
namespace lli {
|
||||
|
||||
enum class ChainType : uint8_t {
|
||||
Loop = 0,
|
||||
OneShot = 1,
|
||||
};
|
||||
|
||||
enum class Interrupt : uint8_t {
|
||||
All = 0,
|
||||
Last = 1,
|
||||
};
|
||||
|
||||
struct ChainConfig {
|
||||
ChainType type;
|
||||
size_t length;
|
||||
Interrupt interrupt;
|
||||
};
|
||||
|
||||
enum class BurstSize : uint8_t {
|
||||
Transfer1 = 0,
|
||||
Transfer4 = 1,
|
||||
Transfer8 = 2,
|
||||
Transfer16 = 3,
|
||||
Transfer32 = 4,
|
||||
Transfer64 = 5,
|
||||
Transfer128 = 6,
|
||||
Transfer256 = 7,
|
||||
};
|
||||
|
||||
enum class TransferWidth : uint8_t {
|
||||
Byte = 0,
|
||||
HalfWord = 1,
|
||||
Word = 2,
|
||||
};
|
||||
|
||||
enum class Increment : uint8_t {
|
||||
No = 0,
|
||||
Yes = 1,
|
||||
};
|
||||
|
||||
using PeripheralIndex = uint8_t;
|
||||
|
||||
struct Endpoint {
|
||||
PeripheralIndex peripheral;
|
||||
BurstSize burst_size;
|
||||
TransferWidth transfer_size;
|
||||
Increment increment;
|
||||
};
|
||||
|
||||
struct ChannelConfig {
|
||||
ChainConfig chain;
|
||||
FlowControl flow_control;
|
||||
Endpoint source;
|
||||
Endpoint destination;
|
||||
|
||||
constexpr gpdma::channel::Control control(
|
||||
const size_t transfer_size,
|
||||
const bool last
|
||||
) {
|
||||
return {
|
||||
.transfersize = transfer_size,
|
||||
.sbsize = toUType(source.burst_size),
|
||||
.dbsize = toUType(destination.burst_size),
|
||||
.swidth = toUType(source.transfer_size),
|
||||
.dwidth = toUType(destination.transfer_size),
|
||||
.s = source_endpoint_type(flow_control),
|
||||
.d = destination_endpoint_type(flow_control),
|
||||
.si = toUType(source.increment),
|
||||
.di = toUType(destination.increment),
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = source.peripheral,
|
||||
.destperipheral = destination.peripheral,
|
||||
.flowcntrl = flow_control,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_baseband_tx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_baseband_rx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_audio_tx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
|
||||
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
|
||||
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_audio_rx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
|
||||
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_rssi {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
{ 0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No },
|
||||
{ 0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
|
||||
};
|
||||
|
||||
class Chain {
|
||||
public:
|
||||
using chain_t = std::vector<gpdma::channel::LLI>;
|
||||
using chain_p = std::unique_ptr<chain_t>;
|
||||
|
||||
Chain(const ChannelConfig& cc) :
|
||||
chain(std::make_unique<chain_t>(cc.chain.length))
|
||||
{
|
||||
set_lli_sequential(cc.chain_type);
|
||||
set_source_address()...
|
||||
}
|
||||
|
||||
private:
|
||||
chain_p chain;
|
||||
|
||||
void set_source_peripheral(void* const address) {
|
||||
set_source_address(address, 0);
|
||||
}
|
||||
|
||||
void set_destination_peripheral(void* const address) {
|
||||
set_destination_address(address, 0);
|
||||
}
|
||||
|
||||
void set_source_address(void* const address, const size_t increment) {
|
||||
size_t offset = 0;
|
||||
for(auto& item : *chain) {
|
||||
item.srcaddr = (uint32_t)address + offset;
|
||||
offset += increment;
|
||||
}
|
||||
}
|
||||
|
||||
void set_destination_address(void* const address, const size_t increment) {
|
||||
size_t offset = 0;
|
||||
for(auto& item : *chain) {
|
||||
item.destaddr = (uint32_t)address + offset;
|
||||
offset += increment;
|
||||
}
|
||||
}
|
||||
|
||||
void set_control(const gpdma::channel::Control control) {
|
||||
for(auto& item : *chain) {
|
||||
item.control = control;
|
||||
}
|
||||
}
|
||||
|
||||
void set_lli_sequential(ChainType chain_type) {
|
||||
for(auto& item : *chain) {
|
||||
item.lli = lli_pointer(&item + 1);
|
||||
}
|
||||
if( chain_type == ChainType::Loop ) {
|
||||
chain[chain->size() - 1].lli = lli_pointer(&chain[0]);
|
||||
} else {
|
||||
chain[chain->size() - 1].lli = lli_pointer(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
gpdma::channel::LLIPointer lli_pointer(const void* lli) {
|
||||
return {
|
||||
.lm = 0,
|
||||
.r = 0,
|
||||
.lli = reinterpret_cast<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace lli */
|
||||
} /* namespace gpdma */
|
||||
} /* namespace lpc43xx */
|
313
Software/portapack-mayhem/firmware/baseband/halconf.h
Normal file
313
Software/portapack-mayhem/firmware/baseband/halconf.h
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
Copyright (C) 2014 Jared Boone, ShareBrained Technology
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file templates/halconf.h
|
||||
* @brief HAL configuration header.
|
||||
* @details HAL configuration file, this file allows to enable or disable the
|
||||
* various device drivers from your application. You may also use
|
||||
* this file in order to override the device drivers default settings.
|
||||
*
|
||||
* @addtogroup HAL_CONF
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _HALCONF_H_
|
||||
#define _HALCONF_H_
|
||||
|
||||
#include "mcuconf.h"
|
||||
|
||||
/**
|
||||
* @brief Enables the TM subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_TM) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_TM FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the PAL subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_PAL FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the ADC subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_ADC FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the CAN subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_CAN FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the EXT subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_EXT FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the GPT subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_GPT FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the I2C subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_I2C FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the ICU subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_ICU FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the MAC subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_MAC FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the MMC_SPI subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_MMC_SPI FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the PWM subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_PWM FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the RTC subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_RTC FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the SDC subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_SDC FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the SERIAL subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_SERIAL FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the SERIAL over USB subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_SERIAL_USB FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the SPI subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_SPI FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the UART subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_UART FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the USB subsystem.
|
||||
*/
|
||||
#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
|
||||
#define HAL_USE_USB FALSE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* ADC driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Enables synchronous APIs.
|
||||
* @note Disabling this option saves both code and data space.
|
||||
*/
|
||||
#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
|
||||
#define ADC_USE_WAIT TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
|
||||
* @note Disabling this option saves both code and data space.
|
||||
*/
|
||||
#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
|
||||
#define ADC_USE_MUTUAL_EXCLUSION TRUE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* CAN driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Sleep mode related APIs inclusion switch.
|
||||
*/
|
||||
#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
|
||||
#define CAN_USE_SLEEP_MODE TRUE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* I2C driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Enables the mutual exclusion APIs on the I2C bus.
|
||||
*/
|
||||
#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
|
||||
#define I2C_USE_MUTUAL_EXCLUSION TRUE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* MAC driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Enables an event sources for incoming packets.
|
||||
*/
|
||||
#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
|
||||
#define MAC_USE_ZERO_COPY FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables an event sources for incoming packets.
|
||||
*/
|
||||
#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
|
||||
#define MAC_USE_EVENTS TRUE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* MMC_SPI driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Delays insertions.
|
||||
* @details If enabled this options inserts delays into the MMC waiting
|
||||
* routines releasing some extra CPU time for the threads with
|
||||
* lower priority, this may slow down the driver a bit however.
|
||||
* This option is recommended also if the SPI driver does not
|
||||
* use a DMA channel and heavily loads the CPU.
|
||||
*/
|
||||
#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
|
||||
#define MMC_NICE_WAITING TRUE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* SDC driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Number of initialization attempts before rejecting the card.
|
||||
* @note Attempts are performed at 10mS intervals.
|
||||
*/
|
||||
#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
|
||||
#define SDC_INIT_RETRY 100
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Include support for MMC cards.
|
||||
* @note MMC support is not yet implemented so this option must be kept
|
||||
* at @p FALSE.
|
||||
*/
|
||||
#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
|
||||
#define SDC_MMC_SUPPORT FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Delays insertions.
|
||||
* @details If enabled this options inserts delays into the MMC waiting
|
||||
* routines releasing some extra CPU time for the threads with
|
||||
* lower priority, this may slow down the driver a bit however.
|
||||
*/
|
||||
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
|
||||
#define SDC_NICE_WAITING FALSE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* SERIAL driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Default bit rate.
|
||||
* @details Configuration parameter, this is the baud rate selected for the
|
||||
* default configuration.
|
||||
*/
|
||||
#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
|
||||
#define SERIAL_DEFAULT_BITRATE 38400
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Serial buffers size.
|
||||
* @details Configuration parameter, you can change the depth of the queue
|
||||
* buffers depending on the requirements of your application.
|
||||
* @note The default is 64 bytes for both the transmission and receive
|
||||
* buffers.
|
||||
*/
|
||||
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
|
||||
#define SERIAL_BUFFERS_SIZE 16
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* SPI driver related settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Enables synchronous APIs.
|
||||
* @note Disabling this option saves both code and data space.
|
||||
*/
|
||||
#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
|
||||
#define SPI_USE_WAIT TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
|
||||
* @note Disabling this option saves both code and data space.
|
||||
*/
|
||||
#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
|
||||
#define SPI_USE_MUTUAL_EXCLUSION TRUE
|
||||
#endif
|
||||
|
||||
#endif /* _HALCONF_H_ */
|
||||
|
||||
/** @} */
|
@ -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__*/
|
121
Software/portapack-mayhem/firmware/baseband/main.cpp
Normal file
121
Software/portapack-mayhem/firmware/baseband/main.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 "ch.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_dma.hpp"
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "touch_dma.hpp"
|
||||
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "message_queue.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "gcc.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
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 void init() {
|
||||
audio::dma::init();
|
||||
audio::dma::configure();
|
||||
audio::dma::enable();
|
||||
|
||||
LPC_CREG->DMAMUX = portapack::gpdma_mux;
|
||||
gpdma::controller.enable();
|
||||
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
|
||||
|
||||
touch::dma::init();
|
||||
touch::dma::allocate();
|
||||
touch::dma::enable();
|
||||
}
|
||||
|
||||
static void halt() {
|
||||
port_disable();
|
||||
while(true) {
|
||||
port_wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdown() {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
chSysDisable();
|
||||
|
||||
systick_stop();
|
||||
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.application_queue.push(shutdown_message);
|
||||
|
||||
halt();
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
init();
|
||||
|
||||
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
|
||||
|
||||
EventDispatcher event_dispatcher;
|
||||
event_dispatcher.run();
|
||||
|
||||
shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
107
Software/portapack-mayhem/firmware/baseband/matched_filter.cpp
Normal file
107
Software/portapack-mayhem/firmware/baseband/matched_filter.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace dsp {
|
||||
namespace matched_filter {
|
||||
|
||||
void MatchedFilter::configure(
|
||||
const tap_t* const taps,
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps_count);
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps_count);
|
||||
taps_count_ = taps_count;
|
||||
decimation_factor_ = decimation_factor;
|
||||
output = 0;
|
||||
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
|
||||
}
|
||||
|
||||
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 */
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 <cstddef>
|
||||
#include <complex>
|
||||
#include <memory>
|
||||
|
||||
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
|
||||
) {
|
||||
configure(taps.data(), taps.size(), decimation_factor);
|
||||
}
|
||||
|
||||
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 { 0 };
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void configure(
|
||||
const tap_t* const taps,
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor
|
||||
);
|
||||
};
|
||||
|
||||
} /* namespace matched_filter */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__MATCHED_FILTER_H__*/
|
48
Software/portapack-mayhem/firmware/baseband/mcuconf.h
Normal file
48
Software/portapack-mayhem/firmware/baseband/mcuconf.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
Copyright (C) 2014 Jared Boone, ShareBrained Technology
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* LPC43xx drivers configuration.
|
||||
* The following settings override the default settings present in
|
||||
* the various device driver implementation headers.
|
||||
* Note that the settings for each driver only have effect if the whole
|
||||
* driver is enabled in halconf.h.
|
||||
*
|
||||
* IRQ priorities:
|
||||
* 7...0 Lowest...Highest.
|
||||
*/
|
||||
|
||||
/* NOTE: Beware setting IRQ priorities < "2":
|
||||
* dbg_check_enter_isr "#SV8 means that probably you have some IRQ set at a
|
||||
* priority level above the kernel level (level 0 or 1 usually) so it is able
|
||||
* to preempt the kernel and mess things up.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DMA driver system settings.
|
||||
*/
|
||||
|
||||
//#define LPC_ADC0_IRQ_PRIORITY 2
|
||||
#define LPC_DMA_IRQ_PRIORITY 3
|
||||
//#define LPC_ADC1_IRQ_PRIORITY 4
|
||||
|
||||
#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4
|
||||
|
||||
/* M4 is initialized by M0, which has already started PLL1 */
|
||||
#if !defined(LPC43XX_M4_CLK) || defined(__DOXYGEN__)
|
||||
#define LPC43XX_M4_CLK 200000000
|
||||
#endif
|
1
Software/portapack-mayhem/firmware/baseband/name
Normal file
1
Software/portapack-mayhem/firmware/baseband/name
Normal file
@ -0,0 +1 @@
|
||||
Receiver
|
95
Software/portapack-mayhem/firmware/baseband/ook.hpp
Normal file
95
Software/portapack-mayhem/firmware/baseband/ook.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 __OOK_HPP__
|
||||
#define __OOK_HPP__
|
||||
|
||||
#include "phase_detector.hpp"
|
||||
#include "phase_accumulator.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <complex>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
class OOKSlicerMagSquaredInt {
|
||||
public:
|
||||
using symbol_t = bool;
|
||||
|
||||
constexpr OOKSlicerMagSquaredInt(
|
||||
const float samples_per_symbol
|
||||
) : mag2_threshold_leak_factor {
|
||||
static_cast<uint32_t>(
|
||||
factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32)
|
||||
)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
symbol_t operator()(const std::complex<int16_t> in) {
|
||||
const uint32_t real2 = in.real() * in.real();
|
||||
const uint32_t imag2 = in.imag() * in.imag();
|
||||
const uint32_t mag2 = real2 + imag2;
|
||||
|
||||
const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2
|
||||
mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32;
|
||||
mag2_threshold = std::max(mag2_threshold, mag2_attenuated);
|
||||
const bool symbol = (mag2 > mag2_threshold);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t mag2_threshold_leak_factor;
|
||||
uint32_t mag2_threshold = 0;
|
||||
|
||||
constexpr float factor_sq(float db) {
|
||||
return std::pow(10.0f, db / (10.0f / 2));
|
||||
}
|
||||
};
|
||||
|
||||
class OOKClockRecovery {
|
||||
public:
|
||||
constexpr OOKClockRecovery(
|
||||
const float samples_per_symbol
|
||||
) : symbol_phase_inc_nominal { static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol)) },
|
||||
symbol_phase_inc_k { static_cast<uint32_t>(std::round(symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol)) },
|
||||
phase_detector { static_cast<size_t>(std::round(samples_per_symbol)) },
|
||||
phase_accumulator { symbol_phase_inc_nominal }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename SymbolHandler>
|
||||
void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) {
|
||||
if( phase_accumulator() ) {
|
||||
const auto detector_result = phase_detector(slicer_history);
|
||||
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * symbol_phase_inc_k);
|
||||
symbol_handler(detector_result.symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t symbol_phase_inc_nominal;
|
||||
const uint32_t symbol_phase_inc_k;
|
||||
PhaseDetectorEarlyLateGate phase_detector;
|
||||
PhaseAccumulator phase_accumulator;
|
||||
};
|
||||
|
||||
#endif/*__OOK_HPP__*/
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 "packet_builder.hpp"
|
139
Software/portapack-mayhem/firmware/baseband/packet_builder.hpp
Normal file
139
Software/portapack-mayhem/firmware/baseband/packet_builder.hpp
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 __PACKET_BUILDER_H__
|
||||
#define __PACKET_BUILDER_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
|
||||
#include "bit_pattern.hpp"
|
||||
#include "baseband_packet.hpp"
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename PreambleMatcher, typename UnstuffMatcher, typename EndMatcher>
|
||||
class PacketBuilder {
|
||||
public:
|
||||
using PayloadHandlerFunc = std::function<void(const baseband::Packet& packet)>;
|
||||
|
||||
PacketBuilder(
|
||||
const PreambleMatcher preamble_matcher,
|
||||
const UnstuffMatcher unstuff_matcher,
|
||||
const EndMatcher end_matcher,
|
||||
PayloadHandlerFunc payload_handler
|
||||
) : payload_handler { std::move(payload_handler) },
|
||||
preamble(preamble_matcher),
|
||||
unstuff(unstuff_matcher),
|
||||
end(end_matcher)
|
||||
{
|
||||
}
|
||||
|
||||
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::Preamble:
|
||||
if( preamble(bit_history, packet.size()) ) {
|
||||
state = State::Payload;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Payload:
|
||||
if( !unstuff(bit_history, packet.size()) ) {
|
||||
packet.add(symbol);
|
||||
}
|
||||
|
||||
if( end(bit_history, packet.size()) ) {
|
||||
// NOTE: This check is to avoid std::function nullptr check, which
|
||||
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
|
||||
// TODO: Make payload_handler known at compile time.
|
||||
if( payload_handler ) {
|
||||
packet.set_timestamp(Timestamp::now());
|
||||
payload_handler(packet);
|
||||
}
|
||||
reset_state();
|
||||
} else {
|
||||
if( packet_truncated() ) {
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
reset_state();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum State {
|
||||
Preamble,
|
||||
Payload,
|
||||
};
|
||||
|
||||
bool packet_truncated() const {
|
||||
return packet.size() >= packet.capacity();
|
||||
}
|
||||
|
||||
const PayloadHandlerFunc payload_handler;
|
||||
|
||||
BitHistory bit_history { };
|
||||
PreambleMatcher preamble { };
|
||||
UnstuffMatcher unstuff { };
|
||||
EndMatcher end { };
|
||||
|
||||
State state { State::Preamble };
|
||||
baseband::Packet packet { };
|
||||
|
||||
void reset_state() {
|
||||
packet.clear();
|
||||
state = State::Preamble;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__PACKET_BUILDER_H__*/
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 __PHASE_ACCUMULATOR_HPP__
|
||||
#define __PHASE_ACCUMULATOR_HPP__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class PhaseAccumulator {
|
||||
public:
|
||||
constexpr PhaseAccumulator(
|
||||
const uint32_t phase_inc
|
||||
) : phase_inc { phase_inc }
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()() {
|
||||
const auto last_phase = phase;
|
||||
phase += phase_inc;
|
||||
return (phase < last_phase);
|
||||
}
|
||||
|
||||
void set_inc(const uint32_t new_phase_inc) {
|
||||
phase_inc = new_phase_inc;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t phase { 0 };
|
||||
uint32_t phase_inc;
|
||||
};
|
||||
|
||||
#endif/*__PHASE_ACCUMULATOR_HPP__*/
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 __PHASE_DETECTOR_HPP__
|
||||
#define __PHASE_DETECTOR_HPP__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
class PhaseDetectorEarlyLateGate {
|
||||
public:
|
||||
using history_t = uint32_t;
|
||||
|
||||
using symbol_t = bool;
|
||||
using error_t = int;
|
||||
|
||||
struct result_t {
|
||||
symbol_t symbol;
|
||||
error_t error;
|
||||
};
|
||||
|
||||
constexpr PhaseDetectorEarlyLateGate(
|
||||
const size_t samples_per_symbol
|
||||
) : sample_threshold { samples_per_symbol / 2 },
|
||||
late_mask { (1UL << sample_threshold) - 1UL },
|
||||
early_mask { late_mask << sample_threshold }
|
||||
{
|
||||
}
|
||||
|
||||
result_t operator()(const history_t symbol_history) const {
|
||||
static_assert(sizeof(history_t) == sizeof(unsigned long), "popcountl size mismatch");
|
||||
|
||||
// history = ...0111, early
|
||||
// history = ...1110, late
|
||||
|
||||
const size_t late_side = __builtin_popcountl(symbol_history & late_mask);
|
||||
const size_t early_side = __builtin_popcountl(symbol_history & early_mask);
|
||||
const size_t total_count = late_side + early_side;
|
||||
const auto lateness = static_cast<int>(late_side) - static_cast<int>(early_side);
|
||||
const symbol_t symbol = (total_count >= sample_threshold);
|
||||
const error_t error = symbol ? -lateness : lateness;
|
||||
return { symbol, error };
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t sample_threshold;
|
||||
const history_t late_mask;
|
||||
const history_t early_mask;
|
||||
};
|
||||
|
||||
#endif/*__PHASE_DETECTOR_HPP__*/
|
81
Software/portapack-mayhem/firmware/baseband/proc_acars.cpp
Normal file
81
Software/portapack-mayhem/firmware/baseband/proc_acars.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 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_acars.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
ACARSProcessor::ACARSProcessor() {
|
||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||
packet.clear();
|
||||
}
|
||||
|
||||
void ACARSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* 2.4576MHz, 2048 samples */
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
const auto decimator_out = decim_1_out;
|
||||
|
||||
/* 38.4kHz, 32 samples */
|
||||
feed_channel_stats(decimator_out);
|
||||
|
||||
for(size_t i=0; i<decimator_out.count; i++) {
|
||||
if( mf.execute_once(decimator_out.p[i]) ) {
|
||||
clock_recovery(mf.get_output());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACARSProcessor::consume_symbol(
|
||||
const float raw_symbol
|
||||
) {
|
||||
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
|
||||
//const auto decoded_symbol = acars_decode(sliced_symbol);
|
||||
|
||||
// DEBUG
|
||||
packet.add(sliced_symbol);
|
||||
if (packet.size() == 256) {
|
||||
payload_handler(packet);
|
||||
packet.clear();
|
||||
}
|
||||
|
||||
//packet_builder.execute(decoded_symbol);
|
||||
}
|
||||
|
||||
void ACARSProcessor::payload_handler(
|
||||
const baseband::Packet& packet
|
||||
) {
|
||||
const ACARSPacketMessage message { packet };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ACARSProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
138
Software/portapack-mayhem/firmware/baseband/proc_acars.hpp
Normal file
138
Software/portapack-mayhem/firmware/baseband/proc_acars.hpp
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 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_ACARS_H__
|
||||
#define __PROC_ACARS_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "matched_filter.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "symbol_coding.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
#include "baseband_packet.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
// AIS:
|
||||
// IN: 2457600/8/8 = 38400
|
||||
// Offset: 2457600/4 = 614400 (614400/8/8 = 9600)
|
||||
// Deviation: 2400
|
||||
// Symbol: 9600
|
||||
// Decimate: 2
|
||||
// 4 taps, 1 symbol, 1/4 cycle
|
||||
|
||||
// TPMS:
|
||||
// IN: 2457600/4/2 = 307200
|
||||
// Offset: 2457600/4 = 614400 (614400/4/2 = 76800)
|
||||
// Deviation: 38400
|
||||
// Symbol: 19200
|
||||
// Decimate: 8
|
||||
// 16 taps, 1 symbol, 2 cycles
|
||||
|
||||
// ACARS:
|
||||
// IN: 2457600/8/8 = 38400
|
||||
// Offset: 2457600/4 = 614400 (614400/8/8 = 9600)
|
||||
// Deviation: ???
|
||||
// Symbol: 2400
|
||||
// Decimate: 8
|
||||
// 16 taps, 1 symbol, 2 cycles
|
||||
|
||||
// Number of taps: size of one symbol in samples (in/symbol)
|
||||
// Cycles:
|
||||
|
||||
|
||||
// Translate+rectangular filter
|
||||
// sample=38.4k, deviation=4800, symbol=2400
|
||||
// Length: 16 taps, 1 symbol, 2 cycles of sinusoid
|
||||
// This is actually the same as rect_taps_307k2_38k4_1t_19k2_p
|
||||
constexpr std::array<std::complex<float>, 16> rect_taps_38k4_4k8_1t_2k4_p { {
|
||||
{ 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f },
|
||||
{ 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f },
|
||||
{ -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f },
|
||||
{ 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f },
|
||||
{ 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f },
|
||||
{ 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f },
|
||||
{ -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f },
|
||||
{ 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f },
|
||||
} };
|
||||
|
||||
class ACARSProcessor : public BasebandProcessor {
|
||||
public:
|
||||
ACARSProcessor();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 2457600;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; // Translate already done here !
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::matched_filter::MatchedFilter mf { rect_taps_38k4_4k8_1t_2k4_p, 8 };
|
||||
|
||||
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
|
||||
4800, 2400, { 0.0555f },
|
||||
[this](const float symbol) { this->consume_symbol(symbol); }
|
||||
};
|
||||
symbol_coding::ACARSDecoder acars_decode { };
|
||||
/*PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
|
||||
{ 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH
|
||||
{ },
|
||||
{ 128 },
|
||||
[this](const baseband::Packet& packet) {
|
||||
this->payload_handler(packet);
|
||||
}
|
||||
};*/
|
||||
baseband::Packet packet { };
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void payload_handler(const baseband::Packet& packet);
|
||||
};
|
||||
|
||||
#endif/*__PROC_ACARS_H__*/
|
167
Software/portapack-mayhem/firmware/baseband/proc_adsbrx.cpp
Normal file
167
Software/portapack-mayhem/firmware/baseband/proc_adsbrx.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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_adsbrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
using namespace adsb;
|
||||
|
||||
void ADSBRXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
int8_t re, im;
|
||||
uint32_t mag;
|
||||
uint32_t c;
|
||||
uint8_t bit, byte{};
|
||||
|
||||
// This is called at 2M/2048 = 977Hz
|
||||
// One pulse = 500ns = 2 samples
|
||||
// One bit = 2 pulses = 1us = 4 samples
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
// Compute sample's magnitude
|
||||
re = (int32_t)buffer.p[i].real(); // make re float and scale it
|
||||
im = (int32_t)buffer.p[i].imag(); // make re float and scale it
|
||||
mag = ((uint32_t)(re*re) + (uint32_t)(im*im));
|
||||
|
||||
if (decoding) {
|
||||
// Decode
|
||||
|
||||
// 1 bit lasts 2 samples
|
||||
if (sample_count & 1) {
|
||||
if (bit_count >= msgLen)
|
||||
{
|
||||
const ADSBFrameMessage message(frame, amp);
|
||||
shared_memory.application_queue.push(message);
|
||||
decoding = false;
|
||||
bit = (prev_mag > mag) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//confidence = true;
|
||||
bit = (prev_mag > mag) ? 1 : 0;
|
||||
}
|
||||
|
||||
byte = bit | (byte << 1);
|
||||
bit_count++;
|
||||
|
||||
// Perform checks at the end of the first byte
|
||||
if (!(bit_count & 7)) {
|
||||
|
||||
// Store the byte
|
||||
frame.push_byte(byte);
|
||||
|
||||
// Check at the end of the first byte of the message
|
||||
uint8_t df = (byte >> 3);
|
||||
if ( (bit_count == 8) && !(df & 0x10) ) {
|
||||
msgLen = 56; // DFs 16 or greater are long 112. DFs 15 or less are short 56.
|
||||
}
|
||||
|
||||
// Abondon all frames that arent DF17 or DF18 extended squitters
|
||||
if ( (bit_count == 8) && !((df == 17)||(df == 18)) ) {
|
||||
decoding = false;
|
||||
bit = (prev_mag > mag) ? 1 : 0;
|
||||
frame.clear();
|
||||
}
|
||||
} // last bit of a byte
|
||||
} // Second sample of each bit
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
// Continue looking for preamble even if in a packet
|
||||
// switch is new preamble id higher magnitude
|
||||
|
||||
// Shift the preamble
|
||||
for (c = 0; c < (ADSB_PREAMBLE_LENGTH ); c++) { shifter[c] = shifter[c + 1]; }
|
||||
shifter[ADSB_PREAMBLE_LENGTH] = mag;
|
||||
|
||||
// First check of relations between the first 10 samples
|
||||
// representing a valid preamble. We don't even investigate further
|
||||
// if this simple test is not passed
|
||||
if (shifter[0] < shifter[1] &&
|
||||
shifter[1] > shifter[2] &&
|
||||
shifter[2] < shifter[3] &&
|
||||
shifter[3] > shifter[4] &&
|
||||
shifter[4] < shifter[1] &&
|
||||
shifter[5] < shifter[1] &&
|
||||
shifter[6] < shifter[1] &&
|
||||
shifter[7] < shifter[1] &&
|
||||
shifter[8] > shifter[9] &&
|
||||
shifter[9] < shifter[10] &&
|
||||
shifter[10]> shifter[11] )
|
||||
{
|
||||
// The samples between the two spikes must be < than the average
|
||||
// of the high spikes level. We don't test bits too near to
|
||||
// the high levels as signals can be out of phase so part of the
|
||||
// energy can be in the near samples
|
||||
int32_t thisAmp = (shifter[1] + shifter[3] + shifter[8] + shifter[10]);
|
||||
uint32_t high = thisAmp / 9;
|
||||
if (
|
||||
shifter[5] < high &&
|
||||
shifter[6] < high &&
|
||||
// Similarly samples in the range 11-13 must be low, as it is the
|
||||
// space between the preamble and real data. Again we don't test
|
||||
// bits too near to high levels, see above
|
||||
shifter[12] < high &&
|
||||
shifter[13] < high &&
|
||||
shifter[14] < high )
|
||||
{
|
||||
if ((decoding == false) || // New preamble
|
||||
((decoding == true) && (thisAmp > amp))) // Higher power than existing packet
|
||||
{
|
||||
decoding = true;
|
||||
msgLen = 112;
|
||||
amp = thisAmp;
|
||||
sample_count = 0;
|
||||
bit_count = 0;
|
||||
frame.clear();
|
||||
}
|
||||
} // 4 & 5 low and 11-14 low
|
||||
} // Check for preamble pattern
|
||||
|
||||
// Store mag for next time
|
||||
prev_mag = mag;
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRXProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::ADSBConfigure) {
|
||||
bit_count = 0;
|
||||
sample_count = 0;
|
||||
decoding = false;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ADSBRXProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
63
Software/portapack-mayhem/firmware/baseband/proc_adsbrx.hpp
Normal file
63
Software/portapack-mayhem/firmware/baseband/proc_adsbrx.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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_ADSBRX_H__
|
||||
#define __PROC_ADSBRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "adsb_frame.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
|
||||
#define ADSB_PREAMBLE_LENGTH 16
|
||||
|
||||
class ADSBRXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 2000000;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
ADSBFrame frame { };
|
||||
bool configured { false };
|
||||
uint32_t prev_mag { 0 };
|
||||
size_t bit_count { 0 }, sample_count { 0 };
|
||||
size_t msgLen{ 112 };
|
||||
uint32_t shifter[ADSB_PREAMBLE_LENGTH+1];
|
||||
bool decoding { };
|
||||
bool preamble { }, active { };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample { 0 };
|
||||
int32_t re { }, im { };
|
||||
int32_t amp {0};
|
||||
};
|
||||
|
||||
#endif
|
73
Software/portapack-mayhem/firmware/baseband/proc_adsbtx.cpp
Normal file
73
Software/portapack-mayhem/firmware/baseband/proc_adsbtx.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_adsbtx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
// This is called at 4M/2048 = 1953Hz
|
||||
// One pulse = 500ns = 2 samples
|
||||
// One bit = 2 pulses = 1us = 4 samples
|
||||
// Test this with ./dump1090 --freq 434000000 --gain 20
|
||||
// Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
if (bit_pos >= (240 << 1)) {
|
||||
configured = false;
|
||||
cur_bit = 0;
|
||||
} else {
|
||||
cur_bit = shared_memory.bb_data.data[bit_pos >> 1];
|
||||
bit_pos++;
|
||||
}
|
||||
|
||||
if (cur_bit) {
|
||||
// Crude AM
|
||||
buffer.p[i] = am_lut[phase & 3];
|
||||
phase++;
|
||||
} else {
|
||||
buffer.p[i] = { 0, 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBTXProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ADSBConfigureMessage*>(p);
|
||||
|
||||
if (message.id == Message::ID::ADSBConfigure) {
|
||||
bit_pos = 0;
|
||||
phase = 0;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ADSBTXProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
54
Software/portapack-mayhem/firmware/baseband/proc_adsbtx.hpp
Normal file
54
Software/portapack-mayhem/firmware/baseband/proc_adsbtx.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_ADSBTX_H__
|
||||
#define __PROC_ADSBTX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
class ADSBTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const p) override;
|
||||
|
||||
private:
|
||||
bool configured = false;
|
||||
|
||||
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
const complex8_t am_lut[4] = {
|
||||
{ 127, 0 },
|
||||
{ 0, 127 },
|
||||
{ -127, 0 },
|
||||
{ 0, -127 }
|
||||
};
|
||||
|
||||
uint32_t bit_pos { 0 };
|
||||
uint32_t cur_bit { 0 };
|
||||
uint32_t phase { 0 };
|
||||
|
||||
TXProgressMessage txprogress_message { };
|
||||
};
|
||||
|
||||
#endif
|
124
Software/portapack-mayhem/firmware/baseband/proc_afsk.cpp
Normal file
124
Software/portapack-mayhem/firmware/baseband/proc_afsk.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_afsk.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void AFSKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
// This is called at 2.28M/2048 = 1113Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
if (sample_count >= afsk_samples_per_bit) {
|
||||
if (configured) {
|
||||
cur_word = *word_ptr;
|
||||
|
||||
if (!cur_word) {
|
||||
// End of data
|
||||
if (repeat_counter < afsk_repeat) {
|
||||
// Repeat
|
||||
bit_pos = 0;
|
||||
word_ptr = (uint16_t*)shared_memory.bb_data.data;
|
||||
cur_word = *word_ptr;
|
||||
txprogress_message.done = false;
|
||||
txprogress_message.progress = repeat_counter + 1;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
repeat_counter++;
|
||||
} else {
|
||||
// Stop
|
||||
cur_word = 0;
|
||||
txprogress_message.done = true;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
configured = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_bit = (cur_word >> (symbol_count - bit_pos)) & 1;
|
||||
|
||||
if (bit_pos >= symbol_count) {
|
||||
bit_pos = 0;
|
||||
word_ptr++;
|
||||
} else {
|
||||
bit_pos++;
|
||||
}
|
||||
|
||||
sample_count = 0;
|
||||
} else {
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
if (cur_bit)
|
||||
tone_phase += afsk_phase_inc_mark;
|
||||
else
|
||||
tone_phase += afsk_phase_inc_space;
|
||||
|
||||
tone_sample = sine_table_i8[(tone_phase & 0xFF000000U) >> 24];
|
||||
|
||||
delta = tone_sample * fm_delta;
|
||||
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 24);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
|
||||
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
|
||||
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
}
|
||||
|
||||
void AFSKProcessor::on_message(const Message* const msg) {
|
||||
const auto message = *reinterpret_cast<const AFSKTxConfigureMessage*>(msg);
|
||||
|
||||
if (message.id == Message::ID::AFSKTxConfigure) {
|
||||
if (message.samples_per_bit) {
|
||||
afsk_samples_per_bit = message.samples_per_bit;
|
||||
afsk_phase_inc_mark = message.phase_inc_mark * AFSK_DELTA_COEF;
|
||||
afsk_phase_inc_space = message.phase_inc_space * AFSK_DELTA_COEF;
|
||||
afsk_repeat = message.repeat - 1;
|
||||
fm_delta = message.fm_delta * (0xFFFFFFULL / AFSK_SAMPLERATE);
|
||||
symbol_count = message.symbol_count - 1;
|
||||
|
||||
sample_count = afsk_samples_per_bit;
|
||||
repeat_counter = 0;
|
||||
bit_pos = 0;
|
||||
word_ptr = (uint16_t*)shared_memory.bb_data.data;
|
||||
cur_word = 0;
|
||||
cur_bit = 0;
|
||||
configured = true;
|
||||
} else
|
||||
configured = false; // Kill
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<AFSKProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
64
Software/portapack-mayhem/firmware/baseband/proc_afsk.hpp
Normal file
64
Software/portapack-mayhem/firmware/baseband/proc_afsk.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_AFSK_H__
|
||||
#define __PROC_AFSK_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#define AFSK_SAMPLERATE 1536000
|
||||
#define AFSK_DELTA_COEF ((1ULL << 32) / AFSK_SAMPLERATE)
|
||||
|
||||
class AFSKProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const msg) override;
|
||||
|
||||
private:
|
||||
bool configured = false;
|
||||
|
||||
BasebandThread baseband_thread { AFSK_SAMPLERATE, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
uint32_t afsk_samples_per_bit { 0 };
|
||||
uint32_t afsk_phase_inc_mark { 0 };
|
||||
uint32_t afsk_phase_inc_space { 0 };
|
||||
uint8_t afsk_repeat { 0 };
|
||||
uint32_t fm_delta { 0 };
|
||||
uint8_t symbol_count { 0 };
|
||||
|
||||
uint8_t repeat_counter { 0 };
|
||||
uint8_t bit_pos { 0 };
|
||||
uint16_t * word_ptr { };
|
||||
uint16_t cur_word { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample_count { 0 };
|
||||
uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 };
|
||||
int32_t tone_sample { 0 }, delta { 0 };
|
||||
|
||||
int8_t re { 0 }, im { 0 };
|
||||
|
||||
TXProgressMessage txprogress_message { };
|
||||
};
|
||||
|
||||
#endif
|
191
Software/portapack-mayhem/firmware/baseband/proc_afskrx.cpp
Normal file
191
Software/portapack-mayhem/firmware/baseband/proc_afskrx.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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_afskrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// This is called at 3072000 / 2048 = 1500Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
// FM demodulation
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
|
||||
|
||||
feed_channel_stats(channel_out);
|
||||
|
||||
auto audio = demod.execute(channel_out, audio_buffer);
|
||||
|
||||
audio_output.write(audio);
|
||||
|
||||
// Audio signal processing
|
||||
for (size_t c = 0; c < audio.count; c++) {
|
||||
|
||||
const int32_t sample_int = audio.p[c] * 32768.0f;
|
||||
int32_t current_sample = __SSAT(sample_int, 16);
|
||||
|
||||
current_sample /= 128;
|
||||
|
||||
// Delay line put
|
||||
delay_line[delay_line_index & 0x3F] = current_sample;
|
||||
|
||||
// Delay line get, and LPF
|
||||
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4;
|
||||
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
|
||||
|
||||
delay_line_index++;
|
||||
|
||||
prev_filtered = sample_filtered;
|
||||
prev_mixed = sample_mixed;
|
||||
|
||||
// Slice
|
||||
sample_bits <<= 1;
|
||||
sample_bits |= (sample_filtered < -20) ? 1 : 0;
|
||||
|
||||
// Check for "clean" transition: either 0011 or 1100
|
||||
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
|
||||
// Adjust phase
|
||||
if (phase < 0x8000)
|
||||
phase += 0x800; // Is this a proper value ?
|
||||
else
|
||||
phase -= 0x800;
|
||||
}
|
||||
|
||||
phase += phase_inc;
|
||||
|
||||
if (phase >= 0x10000) {
|
||||
phase &= 0xFFFF;
|
||||
|
||||
if (trigger_word) {
|
||||
|
||||
// Continuous-stream value-triggered mode (AX.25) - UNTESTED
|
||||
word_bits <<= 1;
|
||||
word_bits |= (sample_bits & 1);
|
||||
|
||||
bit_counter++;
|
||||
|
||||
if (triggered) {
|
||||
if (bit_counter == word_length) {
|
||||
bit_counter = 0;
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = word_bits & word_mask;
|
||||
shared_memory.application_queue.push(data_message);
|
||||
}
|
||||
} else {
|
||||
if ((word_bits & word_mask) == trigger_value) {
|
||||
triggered = !triggered;
|
||||
bit_counter = 0;
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = trigger_value;
|
||||
shared_memory.application_queue.push(data_message);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// RS232-like modem mode
|
||||
if (state == WAIT_START) {
|
||||
if (!(sample_bits & 1)) {
|
||||
// Got start bit
|
||||
state = RECEIVE;
|
||||
bit_counter = 0;
|
||||
}
|
||||
} else if (state == WAIT_STOP) {
|
||||
if (sample_bits & 1) {
|
||||
// Got stop bit
|
||||
state = WAIT_START;
|
||||
}
|
||||
} else {
|
||||
word_bits <<= 1;
|
||||
word_bits |= (sample_bits & 1);
|
||||
|
||||
bit_counter++;
|
||||
}
|
||||
|
||||
if (bit_counter == word_length) {
|
||||
bit_counter = 0;
|
||||
state = WAIT_STOP;
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = word_bits;
|
||||
shared_memory.application_queue.push(data_message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AFSKRxProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::AFSKRxConfigure)
|
||||
configure(*reinterpret_cast<const AFSKRxConfigureMessage*>(message));
|
||||
}
|
||||
|
||||
void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) {
|
||||
/*constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
|
||||
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
|
||||
|
||||
const size_t demod_input_fs = channel_filter_output_fs;*/
|
||||
|
||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||
demod.configure(audio_fs, 5000);
|
||||
|
||||
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
|
||||
|
||||
samples_per_bit = audio_fs / message.baudrate;
|
||||
|
||||
phase_inc = (0x10000 * message.baudrate) / audio_fs;
|
||||
phase = 0;
|
||||
|
||||
trigger_word = message.trigger_word;
|
||||
word_length = message.word_length;
|
||||
trigger_value = message.trigger_value;
|
||||
word_mask = (1 << word_length) - 1;
|
||||
|
||||
// Delay line
|
||||
delay_line_index = 0;
|
||||
|
||||
triggered = false;
|
||||
state = WAIT_START;
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<AFSKRxProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
103
Software/portapack-mayhem/firmware/baseband/proc_afskrx.hpp
Normal file
103
Software/portapack-mayhem/firmware/baseband/proc_afskrx.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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_AFSKRX_H__
|
||||
#define __PROC_AFSKRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "fifo.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
class AFSKRxProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
|
||||
|
||||
size_t samples_per_bit { };
|
||||
|
||||
enum State {
|
||||
WAIT_START = 0,
|
||||
WAIT_STOP,
|
||||
RECEIVE
|
||||
};
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
std::array<float, 32> audio { };
|
||||
const buffer_f32_t audio_buffer {
|
||||
audio.data(),
|
||||
audio.size()
|
||||
};
|
||||
|
||||
// Array size ok down to 375 bauds (24000 / 375)
|
||||
std::array<int32_t, 64> delay_line { 0 };
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
|
||||
AudioOutput audio_output { };
|
||||
|
||||
State state { };
|
||||
size_t delay_line_index { };
|
||||
uint32_t bit_counter { 0 };
|
||||
uint32_t word_bits { 0 };
|
||||
uint32_t sample_bits { 0 };
|
||||
uint32_t phase { }, phase_inc { };
|
||||
int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { };
|
||||
uint32_t word_length { };
|
||||
uint32_t word_mask { };
|
||||
uint32_t trigger_value { };
|
||||
|
||||
bool configured { false };
|
||||
bool wait_start { };
|
||||
bool bit_value { };
|
||||
bool trigger_word { };
|
||||
bool triggered { };
|
||||
|
||||
void configure(const AFSKRxConfigureMessage& message);
|
||||
|
||||
AFSKDataMessage data_message { false, 0 };
|
||||
};
|
||||
|
||||
#endif/*__PROC_TPMS_H__*/
|
72
Software/portapack-mayhem/firmware/baseband/proc_ais.cpp
Normal file
72
Software/portapack-mayhem/firmware/baseband/proc_ais.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_ais.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
AISProcessor::AISProcessor() {
|
||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||
}
|
||||
|
||||
void AISProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* 2.4576MHz, 2048 samples */
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
const auto decimator_out = decim_1_out;
|
||||
|
||||
/* 38.4kHz, 32 samples */
|
||||
feed_channel_stats(decimator_out);
|
||||
|
||||
for(size_t i=0; i<decimator_out.count; i++) {
|
||||
if( mf.execute_once(decimator_out.p[i]) ) {
|
||||
clock_recovery(mf.get_output());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISProcessor::consume_symbol(
|
||||
const float raw_symbol
|
||||
) {
|
||||
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
|
||||
const auto decoded_symbol = nrzi_decode(sliced_symbol);
|
||||
|
||||
packet_builder.execute(decoded_symbol);
|
||||
}
|
||||
|
||||
void AISProcessor::payload_handler(
|
||||
const baseband::Packet& packet
|
||||
) {
|
||||
const AISPacketMessage message { packet };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<AISProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
85
Software/portapack-mayhem/firmware/baseband/proc_ais.hpp
Normal file
85
Software/portapack-mayhem/firmware/baseband/proc_ais.hpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "matched_filter.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "symbol_coding.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
#include "baseband_packet.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
#include "ais_baseband.hpp"
|
||||
|
||||
class AISProcessor : public BasebandProcessor {
|
||||
public:
|
||||
AISProcessor();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 2457600;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 };
|
||||
|
||||
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
|
||||
19200, 9600, { 0.0555f },
|
||||
[this](const float symbol) { this->consume_symbol(symbol); }
|
||||
};
|
||||
symbol_coding::NRZIDecoder nrzi_decode { };
|
||||
PacketBuilder<BitPattern, BitPattern, BitPattern> packet_builder {
|
||||
{ 0b0101010101111110, 16, 1 },
|
||||
{ 0b111110, 6 },
|
||||
{ 0b01111110, 8 },
|
||||
[this](const baseband::Packet& packet) {
|
||||
this->payload_handler(packet);
|
||||
}
|
||||
};
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void payload_handler(const baseband::Packet& packet);
|
||||
};
|
||||
|
||||
#endif/*__PROC_AIS_H__*/
|
118
Software/portapack-mayhem/firmware/baseband/proc_am_audio.cpp
Normal file
118
Software/portapack-mayhem/firmware/baseband/proc_am_audio.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 "audio_output.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
|
||||
if( !configured ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
|
||||
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
|
||||
|
||||
// TODO: Feed channel_stats post-decimation data?
|
||||
feed_channel_stats(channel_out);
|
||||
|
||||
auto audio = demodulate(channel_out);
|
||||
audio_compressor.execute_in_place(audio);
|
||||
audio_output.write(audio);
|
||||
}
|
||||
|
||||
buffer_f32_t NarrowbandAMAudio::demodulate(const buffer_c16_t& channel) {
|
||||
if( modulation_ssb ) {
|
||||
return demod_ssb.execute(channel, audio_buffer);
|
||||
} else {
|
||||
return demod_am.execute(channel, audio_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
channel_spectrum.on_message(message);
|
||||
break;
|
||||
|
||||
case Message::ID::AMConfigure:
|
||||
configure(*reinterpret_cast<const AMConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::CaptureConfig:
|
||||
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t decim_2_input_fs = decim_1_output_fs;
|
||||
constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
|
||||
//const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
|
||||
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
|
||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(1.0f);
|
||||
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
|
||||
audio_output.configure(message.audio_hpf_config);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
|
||||
} else {
|
||||
audio_output.set_stream(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<NarrowbandAMAudio>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "audio_compressor.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class NarrowbandAMAudio : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr size_t decim_2_decimation_factor = 4;
|
||||
static constexpr size_t channel_filter_decimation_factor = 1;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
std::array<float, 32> audio { };
|
||||
const buffer_f32_t audio_buffer {
|
||||
audio.data(),
|
||||
audio.size()
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex decim_2 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
bool modulation_ssb = false;
|
||||
dsp::demodulate::AM demod_am { };
|
||||
dsp::demodulate::SSB demod_ssb { };
|
||||
FeedForwardCompressor audio_compressor { };
|
||||
AudioOutput audio_output { };
|
||||
|
||||
SpectrumCollector channel_spectrum { };
|
||||
|
||||
bool configured { false };
|
||||
void configure(const AMConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
|
||||
buffer_f32_t demodulate(const buffer_c16_t& channel);
|
||||
};
|
||||
|
||||
#endif/*__PROC_AM_AUDIO_H__*/
|
88
Software/portapack-mayhem/firmware/baseband/proc_am_tv.cpp
Normal file
88
Software/portapack-mayhem/firmware/baseband/proc_am_tv.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_tv.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "dsp_fft.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
|
||||
if( !configured ) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::fill(spectrum.begin(), spectrum.end(), 0);
|
||||
|
||||
for(size_t i=0; i<spectrum.size(); i++) {
|
||||
spectrum[i] += buffer.p[i];
|
||||
}
|
||||
|
||||
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
|
||||
channel_spectrum.feed(buffer_c16);
|
||||
|
||||
int8_t re ;
|
||||
//int8_t im;
|
||||
//int8_t mag;
|
||||
|
||||
for (size_t i = 0; i < 128; i++)
|
||||
{
|
||||
re = buffer.p[i].real();
|
||||
//im = buffer.p[i].imag();
|
||||
//mag = __builtin_sqrtf((re * re) + (im * im)) ;
|
||||
const unsigned int v = re + 127.0f; //timescope
|
||||
audio_spectrum.db[i] = std::max(0U, std::min(255U, v));
|
||||
}
|
||||
AudioSpectrumMessage message { &audio_spectrum };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void WidebandFMAudio::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
channel_spectrum.on_message(message);
|
||||
break;
|
||||
|
||||
case Message::ID::WFMConfigure:
|
||||
configure(*reinterpret_cast<const WFMConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
|
||||
(void)message; // avoid warning
|
||||
configured = true;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<WidebandFMAudio>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
65
Software/portapack-mayhem/firmware/baseband/proc_am_tv.hpp
Normal file
65
Software/portapack-mayhem/firmware/baseband/proc_am_tv.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_TV_H__
|
||||
#define __PROC_AM_TV_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "block_decimator.hpp"
|
||||
|
||||
#include "tv_collector.hpp"
|
||||
|
||||
class WidebandFMAudio : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 2000000;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
AudioSpectrum audio_spectrum { };
|
||||
TvCollector channel_spectrum { };
|
||||
std::array<complex16_t, 256> spectrum { };
|
||||
|
||||
bool configured { false };
|
||||
void configure(const WFMConfigureMessage& message);
|
||||
|
||||
};
|
||||
|
||||
#endif/*__PROC_AM_TV_H__*/
|
257
Software/portapack-mayhem/firmware/baseband/proc_aprsrx.cpp
Normal file
257
Software/portapack-mayhem/firmware/baseband/proc_aprsrx.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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_aprsrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
void APRSRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// This is called at 3072000 / 2048 = 1500Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
// FM demodulation
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
|
||||
|
||||
feed_channel_stats(channel_out);
|
||||
|
||||
auto audio = demod.execute(channel_out, audio_buffer);
|
||||
|
||||
audio_output.write(audio);
|
||||
|
||||
// Audio signal processing
|
||||
for (size_t c = 0; c < audio.count; c++) {
|
||||
|
||||
const int32_t sample_int = audio.p[c] * 32768.0f;
|
||||
int32_t current_sample = __SSAT(sample_int, 16);
|
||||
|
||||
current_sample /= 128;
|
||||
|
||||
// Delay line put
|
||||
delay_line[delay_line_index & 0x3F] = current_sample;
|
||||
|
||||
// Delay line get, and LPF
|
||||
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4;
|
||||
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
|
||||
|
||||
delay_line_index++;
|
||||
|
||||
prev_filtered = sample_filtered;
|
||||
prev_mixed = sample_mixed;
|
||||
|
||||
// Slice
|
||||
sample_bits <<= 1;
|
||||
|
||||
uint8_t bit = (sample_filtered < -20) ? 1 : 0;
|
||||
sample_bits |= bit;
|
||||
|
||||
/*
|
||||
int16_t scaled = bit == 1 ? 32767 : -32767;
|
||||
|
||||
if( stream ) {
|
||||
const size_t bytes_to_write = sizeof(scaled) * 1;
|
||||
const auto result = stream->write(&scaled, bytes_to_write);
|
||||
}
|
||||
*/
|
||||
|
||||
// Check for "clean" transition: either 0011 or 1100
|
||||
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
|
||||
// Adjust phase
|
||||
if (phase < 0x8000)
|
||||
phase += 0x800; // Is this a proper value ?
|
||||
else
|
||||
phase -= 0x800;
|
||||
}
|
||||
|
||||
phase += phase_inc;
|
||||
|
||||
if (phase >= 0x10000) {
|
||||
phase &= 0xFFFF;
|
||||
|
||||
if (true) {
|
||||
uint8_t bit;
|
||||
if(__builtin_popcount(sample_bits & 0xFF) >= 0x05){
|
||||
bit = 0x1;
|
||||
}
|
||||
else {
|
||||
bit = 0x0;
|
||||
}
|
||||
|
||||
if(parse_bit(bit)){
|
||||
parse_packet();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APRSRxProcessor::parse_packet(){
|
||||
//validate crc
|
||||
if(packet_buffer_size >= aprs::APRS_MIN_LENGTH){
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for(size_t i = 0; i < packet_buffer_size; i++){
|
||||
uint8_t byte = packet_buffer[i];
|
||||
crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF;
|
||||
}
|
||||
|
||||
if(crc == 0xF0B8){
|
||||
parse_ax25();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APRSRxProcessor::parse_ax25(){
|
||||
aprs_packet.clear();
|
||||
aprs_packet.set_valid_checksum(true);
|
||||
|
||||
for(size_t i = 0; i < packet_buffer_size; i++){
|
||||
aprs_packet.set(i, packet_buffer[i]);
|
||||
}
|
||||
|
||||
APRSPacketMessage packet_message { aprs_packet };
|
||||
shared_memory.application_queue.push(packet_message);
|
||||
}
|
||||
|
||||
bool APRSRxProcessor::parse_bit(const uint8_t current_bit){
|
||||
uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1;
|
||||
last_bit = current_bit;
|
||||
|
||||
//int16_t log = decoded_bit == 0 ? -32768 : 32767;
|
||||
//if(stream){
|
||||
// const size_t bytes_to_write = sizeof(log) * 1;
|
||||
// const auto result = stream->write(&log, bytes_to_write);
|
||||
//}
|
||||
|
||||
if(decoded_bit & 0x1){
|
||||
if(ones_count < 8){
|
||||
ones_count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(ones_count > 6){ //not valid
|
||||
state = WAIT_FLAG;
|
||||
current_byte = 0;
|
||||
ones_count = 0;
|
||||
byte_index = 0;
|
||||
packet_buffer_size = 0;
|
||||
return false;
|
||||
}
|
||||
else if(ones_count == 6){ //flag
|
||||
bool done = false;
|
||||
if(state == IN_FRAME){
|
||||
done = true;
|
||||
}
|
||||
else {
|
||||
packet_buffer_size = 0;
|
||||
}
|
||||
state = WAIT_FRAME;
|
||||
current_byte = 0;
|
||||
ones_count = 0;
|
||||
byte_index = 0;
|
||||
|
||||
return done;
|
||||
}
|
||||
else if(ones_count == 5){ //bit stuff
|
||||
ones_count = 0;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
ones_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//store
|
||||
current_byte = current_byte >> 1;
|
||||
current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0);
|
||||
byte_index++;
|
||||
|
||||
if(byte_index >= 8){
|
||||
byte_index = 0;
|
||||
if(state == WAIT_FRAME){
|
||||
state = IN_FRAME;
|
||||
}
|
||||
|
||||
if(state == IN_FRAME){
|
||||
if(packet_buffer_size + 1 >= 256){
|
||||
state = WAIT_FLAG;
|
||||
current_byte = 0;
|
||||
ones_count = 0;
|
||||
byte_index = 0;
|
||||
packet_buffer_size = 0;
|
||||
return false;
|
||||
}
|
||||
packet_buffer[packet_buffer_size++] = current_byte;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void APRSRxProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::APRSRxConfigure)
|
||||
configure(*reinterpret_cast<const APRSRxConfigureMessage*>(message));
|
||||
if(message->id == Message::ID::CaptureConfig)
|
||||
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
|
||||
}
|
||||
|
||||
void APRSRxProcessor::capture_config(const CaptureConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
//stream = std::make_unique<StreamInput>(message.config);
|
||||
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
|
||||
} else {
|
||||
//stream.reset();
|
||||
audio_output.set_stream(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
|
||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||
demod.configure(audio_fs, 5000);
|
||||
|
||||
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
|
||||
|
||||
samples_per_bit = audio_fs / message.baudrate;
|
||||
|
||||
phase_inc = (0x10000 * message.baudrate) / audio_fs;
|
||||
phase = 0;
|
||||
|
||||
// Delay line
|
||||
delay_line_index = 0;
|
||||
|
||||
state = WAIT_FLAG;
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<APRSRxProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
148
Software/portapack-mayhem/firmware/baseband/proc_aprsrx.hpp
Normal file
148
Software/portapack-mayhem/firmware/baseband/proc_aprsrx.hpp
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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_APRSRX_H__
|
||||
#define __PROC_APRSRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "stream_input.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "fifo.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include "aprs_packet.hpp"
|
||||
|
||||
static uint16_t crc_ccitt_tab[256] = {
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||||
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
||||
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
||||
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||||
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
||||
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
||||
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
||||
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
||||
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
||||
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
||||
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
||||
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
||||
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||||
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
||||
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
||||
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
||||
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
||||
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
||||
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
||||
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
||||
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
||||
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||||
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
||||
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
||||
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
||||
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
||||
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
||||
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
||||
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
||||
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
||||
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||||
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
|
||||
};
|
||||
|
||||
class APRSRxProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
|
||||
|
||||
size_t samples_per_bit { };
|
||||
|
||||
enum State {
|
||||
WAIT_FLAG,
|
||||
WAIT_FRAME,
|
||||
IN_FRAME
|
||||
};
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
std::array<float, 32> audio { };
|
||||
const buffer_f32_t audio_buffer {
|
||||
audio.data(),
|
||||
audio.size()
|
||||
};
|
||||
|
||||
// Array size ok down to 375 bauds (24000 / 375)
|
||||
std::array<int32_t, 64> delay_line { 0 };
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
|
||||
std::unique_ptr<StreamInput> stream { };
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
|
||||
AudioOutput audio_output { };
|
||||
|
||||
State state { };
|
||||
size_t delay_line_index { };
|
||||
uint32_t bit_counter { 0 };
|
||||
uint32_t word_bits { 0 };
|
||||
uint32_t sample_bits { 0 };
|
||||
uint32_t phase { }, phase_inc { };
|
||||
int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { };
|
||||
uint8_t last_bit = 0;
|
||||
uint8_t ones_count = 0 ;
|
||||
uint8_t current_byte = 0;
|
||||
uint8_t byte_index = 0;
|
||||
uint8_t packet_buffer[256];
|
||||
size_t packet_buffer_size = 0;
|
||||
|
||||
bool configured { false };
|
||||
bool wait_start { 0 };
|
||||
bool bit_value { 0 };
|
||||
|
||||
aprs::APRSPacket aprs_packet { };
|
||||
|
||||
void configure(const APRSRxConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
void parse_packet();
|
||||
bool parse_bit(const uint8_t bit);
|
||||
void parse_ax25();
|
||||
};
|
||||
|
||||
#endif/*__PROC_TPMS_H__*/
|
121
Software/portapack-mayhem/firmware/baseband/proc_audiotx.cpp
Normal file
121
Software/portapack-mayhem/firmware/baseband/proc_audiotx.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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_audiotx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void AudioTXProcessor::execute(const buffer_c8_t& buffer){
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
// Zero-order hold (poop)
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
resample_acc += resample_inc;
|
||||
if (resample_acc >= 0x10000) {
|
||||
resample_acc -= 0x10000;
|
||||
if (stream) {
|
||||
stream->read(&audio_sample, 1);
|
||||
bytes_read++;
|
||||
}
|
||||
}
|
||||
|
||||
sample = tone_gen.process(audio_sample - 0x80);
|
||||
|
||||
// FM
|
||||
delta = sample * fm_delta;
|
||||
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 24);
|
||||
|
||||
re = sine_table_i8[(sphase & 0xFF000000U) >> 24];
|
||||
im = sine_table_i8[(phase & 0xFF000000U) >> 24];
|
||||
|
||||
buffer.p[i] = { (int8_t)re, (int8_t)im };
|
||||
}
|
||||
|
||||
progress_samples += buffer.count;
|
||||
if (progress_samples >= progress_interval_samples) {
|
||||
progress_samples -= progress_interval_samples;
|
||||
|
||||
txprogress_message.progress = bytes_read; // Inform UI about progress
|
||||
txprogress_message.done = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTXProcessor::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::AudioTXConfig:
|
||||
audio_config(*reinterpret_cast<const AudioTXConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::ReplayConfig:
|
||||
configured = false;
|
||||
bytes_read = 0;
|
||||
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::SamplerateConfig:
|
||||
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::FIFOData:
|
||||
configured = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTXProcessor::audio_config(const AudioTXConfigMessage& message) {
|
||||
fm_delta = message.deviation_hz * (0xFFFFFFULL / baseband_fs);
|
||||
tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight);
|
||||
progress_interval_samples = message.divider;
|
||||
resample_acc = 0;
|
||||
}
|
||||
|
||||
void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
|
||||
stream = std::make_unique<StreamOutput>(message.config);
|
||||
|
||||
// Tell application that the buffers and FIFO pointers are ready, prefill
|
||||
shared_memory.application_queue.push(sig_message);
|
||||
} else {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTXProcessor::samplerate_config(const SamplerateConfigMessage& message) {
|
||||
resample_inc = (((uint64_t)message.sample_rate) << 16) / baseband_fs; // 16.16 fixed point message.sample_rate
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<AudioTXProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
66
Software/portapack-mayhem/firmware/baseband/proc_audiotx.hpp
Normal file
66
Software/portapack-mayhem/firmware/baseband/proc_audiotx.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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_AUDIOTX_H__
|
||||
#define __PROC_AUDIOTX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "tone_gen.hpp"
|
||||
#include "stream_output.hpp"
|
||||
|
||||
class AudioTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const msg) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 1536000;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
std::unique_ptr<StreamOutput> stream { };
|
||||
|
||||
ToneGen tone_gen { };
|
||||
|
||||
uint32_t resample_inc { }, resample_acc { };
|
||||
uint32_t fm_delta { 0 };
|
||||
uint32_t phase { 0 }, sphase { 0 };
|
||||
uint8_t audio_sample { };
|
||||
int32_t sample { 0 }, delta { };
|
||||
int8_t re { 0 }, im { 0 };
|
||||
|
||||
size_t progress_interval_samples = 0 , progress_samples = 0;
|
||||
|
||||
bool configured { false };
|
||||
uint32_t bytes_read { 0 };
|
||||
|
||||
void samplerate_config(const SamplerateConfigMessage& message);
|
||||
void audio_config(const AudioTXConfigMessage& message);
|
||||
void replay_config(const ReplayConfigMessage& message);
|
||||
|
||||
TXProgressMessage txprogress_message { };
|
||||
RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest };
|
||||
};
|
||||
|
||||
#endif
|
339
Software/portapack-mayhem/firmware/baseband/proc_btlerx.cpp
Normal file
339
Software/portapack-mayhem/firmware/baseband/proc_btlerx.cpp
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_btlerx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
// FM demodulation
|
||||
|
||||
/*const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto channel = decim_1.execute(decim_0_out, dst_buffer);
|
||||
|
||||
feed_channel_stats(channel);
|
||||
|
||||
auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
feed_channel_stats(decim_0_out);
|
||||
|
||||
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
|
||||
|
||||
/*std::fill(spectrum.begin(), spectrum.end(), 0);
|
||||
for(size_t i=0; i<spectrum.size(); i++) {
|
||||
spectrum[i] += buffer.p[i];
|
||||
}
|
||||
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
|
||||
feed_channel_stats(buffer_c16);
|
||||
|
||||
auto audio_oversampled = demod.execute(buffer_c16, work_audio_buffer);*/
|
||||
// Audio signal processing
|
||||
for (size_t c = 0; c < audio_oversampled.count; c++) {
|
||||
|
||||
/*const int32_t sample_int = audio_oversampled.p[c] * 32768.0f;
|
||||
int32_t current_sample = __SSAT(sample_int, 16);
|
||||
current_sample /= 128;*/
|
||||
|
||||
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
|
||||
rb_head++;
|
||||
rb_head=(rb_head)%RB_SIZE;
|
||||
|
||||
rb_buf[rb_head] = current_sample;
|
||||
|
||||
skipSamples = skipSamples - 1;
|
||||
|
||||
|
||||
if (skipSamples<1)
|
||||
{
|
||||
|
||||
|
||||
int32_t threshold_tmp=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
|
||||
}
|
||||
g_threshold = (int32_t)threshold_tmp/8;
|
||||
|
||||
|
||||
int transitions=0;
|
||||
if (rb_buf[(rb_head+9)%RB_SIZE] > g_threshold)
|
||||
{
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE])
|
||||
transitions = transitions + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE])
|
||||
transitions = transitions + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool packet_detected=false;
|
||||
//if ( transitions==4 && abs(g_threshold)<15500)
|
||||
if ( transitions==4)
|
||||
{
|
||||
|
||||
|
||||
uint8_t packet_data[500];
|
||||
int packet_length;
|
||||
uint32_t packet_crc;
|
||||
//uint32_t calced_crc; // NOTE: restore when CRC is passing
|
||||
uint64_t packet_addr_l;
|
||||
//uint32_t result; // NOTE: restore when CRC is passing
|
||||
uint8_t crc[3];
|
||||
uint8_t packet_header_arr[2];
|
||||
|
||||
packet_addr_l=0;
|
||||
for (int i=0;i<4;i++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
|
||||
packet_addr_l|=((uint64_t)byte_temp)<<(8*i);
|
||||
}
|
||||
|
||||
channel_number = 38;
|
||||
|
||||
|
||||
for (int t=0;t<2;t++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
|
||||
packet_header_arr[t] = byte;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
|
||||
uint8_t lfsr_1 = byte_temp2 | 2;
|
||||
int header_length = 2;
|
||||
int header_counter = 0;
|
||||
while(header_length--)
|
||||
{
|
||||
for(uint8_t i = 0x80; i; i >>= 1)
|
||||
{
|
||||
if(lfsr_1 & 0x80)
|
||||
{
|
||||
lfsr_1 ^= 0x11;
|
||||
(packet_header_arr[header_counter]) ^= i;
|
||||
}
|
||||
lfsr_1 <<= 1;
|
||||
}
|
||||
header_counter = header_counter + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (packet_addr_l==0x8E89BED6)
|
||||
{
|
||||
|
||||
uint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
|
||||
packet_length=byte_temp3&0x3F;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_length=0;
|
||||
}
|
||||
|
||||
for (int t=0;t<packet_length+2+3;t++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
|
||||
packet_data[t] = byte;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
|
||||
uint8_t lfsr_2 = byte_temp4 | 2;
|
||||
int pdu_crc_length = packet_length+2+3;
|
||||
int pdu_crc_counter = 0;
|
||||
while(pdu_crc_length--)
|
||||
{
|
||||
for(uint8_t i = 0x80; i; i >>= 1)
|
||||
{
|
||||
if(lfsr_2 & 0x80)
|
||||
{
|
||||
lfsr_2 ^= 0x11;
|
||||
(packet_data[pdu_crc_counter]) ^= i;
|
||||
}
|
||||
lfsr_2 <<= 1;
|
||||
}
|
||||
pdu_crc_counter = pdu_crc_counter + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (packet_addr_l==0x8E89BED6)
|
||||
{
|
||||
crc[0]=crc[1]=crc[2]=0x55;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc[0]=crc[1]=crc[2]=0;
|
||||
}
|
||||
|
||||
uint8_t v, t, d, crc_length;
|
||||
uint32_t crc_result=0;
|
||||
crc_length = packet_length + 2;
|
||||
int counter = 0;
|
||||
while(crc_length--)
|
||||
{
|
||||
uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
|
||||
d = byte_temp5;
|
||||
for(v = 0; v < 8; v++, d >>= 1)
|
||||
{
|
||||
t = crc[0] >> 7;
|
||||
crc[0] <<= 1;
|
||||
if(crc[1] & 0x80) crc[0] |= 1;
|
||||
crc[1] <<= 1;
|
||||
if(crc[2] & 0x80) crc[1] |= 1;
|
||||
crc[2] <<= 1;
|
||||
if(t != (d & 1))
|
||||
{
|
||||
crc[2] ^= 0x5B;
|
||||
crc[1] ^= 0x06;
|
||||
}
|
||||
}
|
||||
counter = counter + 1;
|
||||
}
|
||||
for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v];
|
||||
//calced_crc = crc_result; // NOTE: restore when CRC is passing
|
||||
|
||||
packet_crc=0;
|
||||
for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c];
|
||||
|
||||
if (packet_addr_l==0x8E89BED6)
|
||||
//if (packet_crc==calced_crc) // NOTE: restore when CRC is passing
|
||||
{
|
||||
uint8_t mac_data[6];
|
||||
int counter = 0;
|
||||
for (int i = 7; i >= 2; i--)
|
||||
{
|
||||
uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
|
||||
//result = byte_temp6; // NOTE: restore when CRC is passing
|
||||
mac_data[counter] = byte_temp6;
|
||||
counter = counter + 1;
|
||||
}
|
||||
|
||||
data_message.is_data = false;
|
||||
data_message.value = 'A';
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = mac_data[0];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = mac_data[1];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = mac_data[2];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = mac_data[3];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = mac_data[4];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = mac_data[5];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = false;
|
||||
data_message.value = 'B';
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
packet_detected = true;
|
||||
}
|
||||
else
|
||||
packet_detected = false;
|
||||
}
|
||||
|
||||
if (packet_detected)
|
||||
{
|
||||
skipSamples=20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BTLERxProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::BTLERxConfigure)
|
||||
configure(*reinterpret_cast<const BTLERxConfigureMessage*>(message));
|
||||
}
|
||||
|
||||
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
|
||||
(void)message; //avoid warning
|
||||
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
|
||||
demod.configure(audio_fs, 5000);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<BTLERxProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
95
Software/portapack-mayhem/firmware/baseband/proc_btlerx.hpp
Normal file
95
Software/portapack-mayhem/firmware/baseband/proc_btlerx.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_BTLERX_H__
|
||||
#define __PROC_BTLERX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "fifo.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
class BTLERxProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 4000000;
|
||||
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
std::array<complex16_t, 512> spectrum { };
|
||||
const buffer_c16_t spectrum_buffer {
|
||||
spectrum.data(),
|
||||
spectrum.size()
|
||||
};
|
||||
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)dst.data(),
|
||||
sizeof(dst) / sizeof(int16_t)
|
||||
};
|
||||
|
||||
|
||||
// Array size ok down to 375 bauds (24000 / 375)
|
||||
std::array<int32_t, 64> delay_line { 0 };
|
||||
std::array<int16_t, 1000> rb_buf { 0 };
|
||||
|
||||
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
int rb_head {-1};
|
||||
int32_t g_threshold {0};
|
||||
uint8_t channel_number {38};
|
||||
int skipSamples {1000};
|
||||
int RB_SIZE {1000};
|
||||
|
||||
bool configured { false };
|
||||
|
||||
|
||||
void configure(const BTLERxConfigureMessage& message);
|
||||
|
||||
AFSKDataMessage data_message { false, 0 };
|
||||
};
|
||||
|
||||
#endif/*__PROC_BTLERX_H__*/
|
112
Software/portapack-mayhem/firmware/baseband/proc_capture.cpp
Normal file
112
Software/portapack-mayhem/firmware/baseband/proc_capture.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 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_capture.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
CaptureProcessor::CaptureProcessor() {
|
||||
decim_0.configure(taps_200k_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_200k_decim_1.taps, 131072);
|
||||
|
||||
channel_spectrum.set_decimation_factor(1);
|
||||
}
|
||||
|
||||
void CaptureProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* 2.4576MHz, 2048 samples */
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
const auto& decimator_out = decim_1_out;
|
||||
const auto& channel = decimator_out;
|
||||
|
||||
if( stream ) {
|
||||
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
|
||||
const size_t written = stream->write(decimator_out.p, bytes_to_write);
|
||||
if( written != bytes_to_write )
|
||||
{
|
||||
//TODO eventually report error somewhere
|
||||
}
|
||||
}
|
||||
|
||||
feed_channel_stats(channel);
|
||||
|
||||
spectrum_samples += channel.count;
|
||||
if( spectrum_samples >= spectrum_interval_samples ) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureProcessor::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
channel_spectrum.on_message(message);
|
||||
break;
|
||||
|
||||
case Message::ID::SamplerateConfig:
|
||||
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::CaptureConfig:
|
||||
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureProcessor::samplerate_config(const SamplerateConfigMessage& message) {
|
||||
baseband_fs = message.sample_rate;
|
||||
baseband_thread.set_sampling_rate(baseband_fs);
|
||||
|
||||
size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor;
|
||||
|
||||
size_t decim_1_input_fs = decim_0_output_fs;
|
||||
size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs;
|
||||
|
||||
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
|
||||
spectrum_samples = 0;
|
||||
}
|
||||
|
||||
void CaptureProcessor::capture_config(const CaptureConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
stream = std::make_unique<StreamInput>(message.config);
|
||||
} else {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<CaptureProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
77
Software/portapack-mayhem/firmware/baseband/proc_capture.hpp
Normal file
77
Software/portapack-mayhem/firmware/baseband/proc_capture.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 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_CAPTURE_HPP__
|
||||
#define __PROC_CAPTURE_HPP__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include "stream_input.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
class CaptureProcessor : public BasebandProcessor {
|
||||
public:
|
||||
CaptureProcessor();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
// TODO: Repeated value needs to be transmitted from application side.
|
||||
size_t baseband_fs = 0;
|
||||
static constexpr auto spectrum_rate_hz = 50.0f;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
std::unique_ptr<StreamInput> stream { };
|
||||
|
||||
SpectrumCollector channel_spectrum { };
|
||||
size_t spectrum_interval_samples = 0;
|
||||
size_t spectrum_samples = 0;
|
||||
|
||||
void samplerate_config(const SamplerateConfigMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
};
|
||||
|
||||
#endif/*__PROC_CAPTURE_HPP__*/
|
111
Software/portapack-mayhem/firmware/baseband/proc_ert.cpp
Normal file
111
Software/portapack-mayhem/firmware/baseband/proc_ert.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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_ert.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
float ERTProcessor::abs(const complex8_t& v) {
|
||||
// const int16_t r = v.real() - offset_i;
|
||||
// const int16_t i = v.imag() - offset_q;
|
||||
// const uint32_t r2 = r * r;
|
||||
// const uint32_t i2 = i * i;
|
||||
// const uint32_t r2_i2 = r2 + i2;
|
||||
// return std::sqrt(static_cast<float>(r2_i2));
|
||||
const float r = static_cast<float>(v.real()) - offset_i;
|
||||
const float i = static_cast<float>(v.imag()) - offset_q;
|
||||
const float r2 = r * r;
|
||||
const float i2 = i * i;
|
||||
const float r2_i2 = r2 + i2;
|
||||
return std::sqrt(r2_i2);
|
||||
}
|
||||
|
||||
void ERTProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* 4.194304MHz, 2048 samples */
|
||||
|
||||
const complex8_t* src = &buffer.p[0];
|
||||
const complex8_t* const src_end = &buffer.p[buffer.count];
|
||||
|
||||
average_i += src->real();
|
||||
average_q += src->imag();
|
||||
average_count++;
|
||||
if( average_count == average_window ) {
|
||||
offset_i = static_cast<float>(average_i) / average_window;
|
||||
offset_q = static_cast<float>(average_q) / average_window;
|
||||
average_i = 0;
|
||||
average_q = 0;
|
||||
average_count = 0;
|
||||
}
|
||||
|
||||
const float gain = 128 * samples_per_symbol;
|
||||
const float k = 1.0f / gain;
|
||||
|
||||
while(src < src_end) {
|
||||
float sum = 0.0f;
|
||||
for(size_t i=0; i<(samples_per_symbol / 2); i++) {
|
||||
sum += abs(*(src++));
|
||||
}
|
||||
sum_half_period[1] = sum_half_period[0];
|
||||
sum_half_period[0] = sum;
|
||||
|
||||
sum_period[2] = sum_period[1];
|
||||
sum_period[1] = sum_period[0];
|
||||
sum_period[0] = (sum_half_period[0] + sum_half_period[1]) * k;
|
||||
|
||||
manchester[2] = manchester[1];
|
||||
manchester[1] = manchester[0];
|
||||
manchester[0] = sum_period[2] - sum_period[0];
|
||||
|
||||
const auto data = manchester[0] - manchester[2];
|
||||
|
||||
clock_recovery(data);
|
||||
}
|
||||
}
|
||||
|
||||
void ERTProcessor::consume_symbol(
|
||||
const float raw_symbol
|
||||
) {
|
||||
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
|
||||
scm_builder.execute(sliced_symbol);
|
||||
idm_builder.execute(sliced_symbol);
|
||||
}
|
||||
|
||||
void ERTProcessor::scm_handler(
|
||||
const baseband::Packet& packet
|
||||
) {
|
||||
const ERTPacketMessage message { ert::Packet::Type::SCM, packet };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void ERTProcessor::idm_handler(
|
||||
const baseband::Packet& packet
|
||||
) {
|
||||
const ERTPacketMessage message { ert::Packet::Type::IDM, packet };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ERTProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
110
Software/portapack-mayhem/firmware/baseband/proc_ert.hpp
Normal file
110
Software/portapack-mayhem/firmware/baseband/proc_ert.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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_ERT_H__
|
||||
#define __PROC_ERT_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "symbol_coding.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
#include "baseband_packet.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x1f2a60)[2:].zfill(21))])
|
||||
constexpr uint64_t scm_preamble_and_sync_manchester { 0b101010101001011001100110010110100101010101 };
|
||||
constexpr size_t scm_preamble_and_sync_length { 42 - 10 };
|
||||
constexpr size_t scm_payload_length_max { 150 };
|
||||
|
||||
// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x555516a3)[2:].zfill(32))])
|
||||
constexpr uint64_t idm_preamble_and_sync_manchester { 0b0110011001100110011001100110011001010110011010011001100101011010 };
|
||||
constexpr size_t idm_preamble_and_sync_length { 64 - 16 };
|
||||
|
||||
constexpr size_t idm_payload_length_max { 1408 };
|
||||
|
||||
class ERTProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
const uint32_t baseband_sampling_rate = 4194304;
|
||||
const size_t decimation = 1;
|
||||
const float symbol_rate = 32768;
|
||||
|
||||
const uint32_t channel_sampling_rate = baseband_sampling_rate / decimation;
|
||||
const size_t samples_per_symbol = channel_sampling_rate / symbol_rate;
|
||||
const float clock_recovery_rate = symbol_rate * 2;
|
||||
|
||||
BasebandThread baseband_thread { baseband_sampling_rate, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
|
||||
clock_recovery_rate, symbol_rate, { 1.0f / 18.0f },
|
||||
[this](const float symbol) { this->consume_symbol(symbol); }
|
||||
};
|
||||
|
||||
PacketBuilder<BitPattern, NeverMatch, FixedLength> scm_builder {
|
||||
{ scm_preamble_and_sync_manchester, scm_preamble_and_sync_length, 1 },
|
||||
{ },
|
||||
{ scm_payload_length_max },
|
||||
[this](const baseband::Packet& packet) {
|
||||
this->scm_handler(packet);
|
||||
}
|
||||
};
|
||||
|
||||
PacketBuilder<BitPattern, NeverMatch, FixedLength> idm_builder {
|
||||
{ idm_preamble_and_sync_manchester, idm_preamble_and_sync_length, 1 },
|
||||
{ },
|
||||
{ idm_payload_length_max },
|
||||
[this](const baseband::Packet& packet) {
|
||||
this->idm_handler(packet);
|
||||
}
|
||||
};
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void scm_handler(const baseband::Packet& packet);
|
||||
void idm_handler(const baseband::Packet& packet);
|
||||
|
||||
float sum_half_period[2];
|
||||
float sum_period[3];
|
||||
float manchester[3];
|
||||
|
||||
const size_t average_window { 2048 };
|
||||
int32_t average_i { 0 };
|
||||
int32_t average_q { 0 };
|
||||
size_t average_count { 0 };
|
||||
float offset_i { 0.0f };
|
||||
float offset_q { 0.0f };
|
||||
|
||||
float abs(const complex8_t& v);
|
||||
};
|
||||
|
||||
#endif/*__PROC_ERT_H__*/
|
107
Software/portapack-mayhem/firmware/baseband/proc_fsk.cpp
Normal file
107
Software/portapack-mayhem/firmware/baseband/proc_fsk.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_fsk.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void FSKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
int8_t re, im;
|
||||
|
||||
// This is called at 2.28M/2048 = 1113Hz
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
if (configured) {
|
||||
if (sample_count >= samples_per_bit) {
|
||||
if (bit_pos > length) {
|
||||
// End of data
|
||||
cur_bit = 0;
|
||||
txprogress_message.done = true;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
configured = false;
|
||||
} else {
|
||||
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
|
||||
bit_pos++;
|
||||
if (progress_count >= progress_notice) {
|
||||
progress_count = 0;
|
||||
txprogress_message.progress++;
|
||||
txprogress_message.done = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
} else {
|
||||
progress_count++;
|
||||
}
|
||||
}
|
||||
sample_count = 0;
|
||||
} else {
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
if (cur_bit)
|
||||
phase += shift_one;
|
||||
else
|
||||
phase += shift_zero;
|
||||
|
||||
sphase = phase + (64 << 24);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
|
||||
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
}
|
||||
|
||||
void FSKProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const FSKConfigureMessage*>(p);
|
||||
|
||||
if (message.id == Message::ID::FSKConfigure) {
|
||||
samples_per_bit = message.samples_per_bit;
|
||||
length = message.stream_length + 32; // Why ?!
|
||||
|
||||
shift_one = message.shift * (0xFFFFFFFFULL / 2280000);
|
||||
shift_zero = -shift_one;
|
||||
|
||||
progress_notice = message.progress_notice;
|
||||
|
||||
sample_count = samples_per_bit;
|
||||
progress_count = 0;
|
||||
bit_pos = 0;
|
||||
cur_bit = 0;
|
||||
|
||||
txprogress_message.progress = 0;
|
||||
txprogress_message.done = false;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<FSKProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
53
Software/portapack-mayhem/firmware/baseband/proc_fsk.hpp
Normal file
53
Software/portapack-mayhem/firmware/baseband/proc_fsk.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_FSK_H__
|
||||
#define __PROC_FSK_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
class FSKProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const p) override;
|
||||
|
||||
private:
|
||||
bool configured = false;
|
||||
|
||||
BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
uint32_t samples_per_bit { 0 };
|
||||
uint32_t length { 0 };
|
||||
|
||||
uint32_t shift_zero { }, shift_one { };
|
||||
uint32_t bit_pos { 0 };
|
||||
uint32_t progress_notice { }, progress_count { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample_count { 0 };
|
||||
uint32_t phase { 0 }, sphase { 0 };
|
||||
|
||||
TXProgressMessage txprogress_message { };
|
||||
};
|
||||
|
||||
#endif
|
128
Software/portapack-mayhem/firmware/baseband/proc_gps_sim.cpp
Normal file
128
Software/portapack-mayhem/firmware/baseband/proc_gps_sim.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_gps_sim.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
ReplayProcessor::ReplayProcessor() {
|
||||
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
|
||||
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
|
||||
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
|
||||
|
||||
spectrum_samples = 0;
|
||||
|
||||
channel_spectrum.set_decimation_factor(1);
|
||||
|
||||
configured = false;
|
||||
}
|
||||
|
||||
void ReplayProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* 4MHz, 2048 samples */
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
// File data is in C16 format, we need C8
|
||||
// File samplerate is 500kHz, we're at 4MHz
|
||||
// iq_buffer can only be 512 C16 samples (RAM limitation)
|
||||
// To fill up the 2048-sample C8 buffer, we need:
|
||||
// 2048 samples * 2 bytes per sample = 4096 bytes
|
||||
// Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each
|
||||
// So 256 * 4 bytes per sample (C16) = 1024 bytes from the file
|
||||
if( stream ) { //sizeof(*buffer.p) = sizeof(C8) = 2*int8 = 2 bytes //buffer.count = 2048
|
||||
const size_t bytes_to_read = sizeof(*buffer.p) * 1 * (buffer.count ); // *2 (C16), /8 (oversampling) should be == 1024
|
||||
bytes_read += stream->read(iq_buffer.p, bytes_to_read);
|
||||
}
|
||||
|
||||
// Fill and "stretch"
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
auto re_out = iq_buffer.p[i].real() ;
|
||||
auto im_out = iq_buffer.p[i].imag() ;
|
||||
buffer.p[i] = { (int8_t)re_out, (int8_t)im_out };
|
||||
}
|
||||
|
||||
spectrum_samples += buffer.count;
|
||||
if( spectrum_samples >= spectrum_interval_samples ) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
|
||||
txprogress_message.progress = bytes_read / 1024; // Inform UI about progress
|
||||
|
||||
txprogress_message.done = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayProcessor::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
channel_spectrum.on_message(message);
|
||||
break;
|
||||
|
||||
case Message::ID::SamplerateConfig:
|
||||
samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::ReplayConfig:
|
||||
configured = false;
|
||||
bytes_read = 0;
|
||||
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
// App has prefilled the buffers, we're ready to go now
|
||||
case Message::ID::FIFOData:
|
||||
configured = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayProcessor::samplerate_config(const SamplerateConfigMessage& message) {
|
||||
baseband_fs = message.sample_rate;
|
||||
baseband_thread.set_sampling_rate(baseband_fs);
|
||||
spectrum_interval_samples = baseband_fs / spectrum_rate_hz;
|
||||
}
|
||||
|
||||
void ReplayProcessor::replay_config(const ReplayConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
|
||||
stream = std::make_unique<StreamOutput>(message.config);
|
||||
|
||||
// Tell application that the buffers and FIFO pointers are ready, prefill
|
||||
shared_memory.application_queue.push(sig_message);
|
||||
} else {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ReplayProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
78
Software/portapack-mayhem/firmware/baseband/proc_gps_sim.hpp
Normal file
78
Software/portapack-mayhem/firmware/baseband/proc_gps_sim.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_GPS_SIM_HPP__
|
||||
#define __PROC_GPS_SIM_HPP__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include "stream_output.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
class ReplayProcessor : public BasebandProcessor {
|
||||
public:
|
||||
ReplayProcessor();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
size_t baseband_fs = 0;
|
||||
static constexpr auto spectrum_rate_hz = 50.0f;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
std::array<complex8_t, 2048> iq { };
|
||||
const buffer_c8_t iq_buffer {
|
||||
iq.data(),
|
||||
iq.size(),
|
||||
baseband_fs
|
||||
};
|
||||
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
std::unique_ptr<StreamOutput> stream { };
|
||||
|
||||
SpectrumCollector channel_spectrum { };
|
||||
size_t spectrum_interval_samples = 0;
|
||||
size_t spectrum_samples = 0;
|
||||
|
||||
bool configured { false };
|
||||
uint32_t bytes_read { 0 };
|
||||
|
||||
void samplerate_config(const SamplerateConfigMessage& message);
|
||||
void replay_config(const ReplayConfigMessage& message);
|
||||
|
||||
TXProgressMessage txprogress_message { };
|
||||
RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest };
|
||||
};
|
||||
|
||||
#endif/*__PROC_GPS_SIM_HPP__*/
|
115
Software/portapack-mayhem/firmware/baseband/proc_jammer.cpp
Normal file
115
Software/portapack-mayhem/firmware/baseband/proc_jammer.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_jammer.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
if (!jammer_duration) {
|
||||
// Find next enabled range
|
||||
do {
|
||||
current_range++;
|
||||
if (current_range == JAMMER_MAX_CH) current_range = 0;
|
||||
} while (!jammer_channels[current_range].enabled);
|
||||
|
||||
jammer_duration = jammer_channels[current_range].duration;
|
||||
jammer_bw = jammer_channels[current_range].width / 2; // TODO: Exact value
|
||||
|
||||
// Ask for retune
|
||||
message.freq = jammer_channels[current_range].center;
|
||||
message.range = current_range;
|
||||
shared_memory.application_queue.push(message);
|
||||
} else {
|
||||
jammer_duration--;
|
||||
}
|
||||
|
||||
// Phase noise
|
||||
if (!period_counter) {
|
||||
period_counter = noise_period;
|
||||
|
||||
if (noise_type == JammerType::TYPE_FSK) {
|
||||
sample = (sample + lfsr) >> 1;
|
||||
} else if (noise_type == JammerType::TYPE_TONE) {
|
||||
tone_delta = 150000 + (lfsr >> 9); // Approx 100Hz to 6kHz
|
||||
} else if (noise_type == JammerType::TYPE_SWEEP) {
|
||||
sample++; // This is like saw wave FM
|
||||
}
|
||||
|
||||
feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1;
|
||||
lfsr = (lfsr << 1) | feedback;
|
||||
if (!lfsr) lfsr = 0x1337; // Shouldn't do this :(
|
||||
} else {
|
||||
period_counter--;
|
||||
}
|
||||
|
||||
if (noise_type == JammerType::TYPE_TONE) {
|
||||
aphase += tone_delta;
|
||||
sample = sine_table_i8[(aphase & 0xFF000000) >> 24];
|
||||
}
|
||||
|
||||
delta = sample * jammer_bw;
|
||||
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 24);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
|
||||
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
|
||||
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
};
|
||||
|
||||
void JammerProcessor::on_message(const Message* const msg) {
|
||||
if (msg->id == Message::ID::JammerConfigure) {
|
||||
const auto message = *reinterpret_cast<const JammerConfigureMessage*>(msg);
|
||||
|
||||
if (message.run) {
|
||||
jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
|
||||
noise_type = message.type;
|
||||
noise_period = 3072000 / message.speed;
|
||||
if (noise_type == JammerType::TYPE_SWEEP)
|
||||
noise_period >>= 8;
|
||||
period_counter = 0;
|
||||
jammer_duration = 0;
|
||||
current_range = 0;
|
||||
lfsr = 0xDEAD0012;
|
||||
|
||||
configured = true;
|
||||
} else {
|
||||
configured = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<JammerProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
59
Software/portapack-mayhem/firmware/baseband/proc_jammer.hpp
Normal file
59
Software/portapack-mayhem/firmware/baseband/proc_jammer.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_JAMMER_H__
|
||||
#define __PROC_JAMMER_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "jammer.hpp"
|
||||
|
||||
using namespace jammer;
|
||||
|
||||
class JammerProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const msg) override;
|
||||
|
||||
private:
|
||||
bool configured { false };
|
||||
|
||||
BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
JammerChannel * jammer_channels { };
|
||||
|
||||
JammerType noise_type { };
|
||||
uint32_t tone_delta { 0 }, lfsr { }, feedback { };
|
||||
uint32_t noise_period { 0 }, period_counter { 0 };
|
||||
uint32_t jammer_duration { 0 };
|
||||
uint32_t current_range { 0 };
|
||||
int64_t jammer_center { 0 }, jammer_bw { 0 };
|
||||
uint32_t sample_count { 0 };
|
||||
uint32_t aphase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 };
|
||||
int8_t sample { 0 };
|
||||
int8_t re { 0 }, im { 0 };
|
||||
RetuneMessage message { };
|
||||
};
|
||||
|
||||
#endif
|
173
Software/portapack-mayhem/firmware/baseband/proc_mictx.cpp
Normal file
173
Software/portapack-mayhem/firmware/baseband/proc_mictx.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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_mictx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "tonesets.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void MicTXProcessor::execute(const buffer_c8_t& buffer){
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
audio_input.read_audio_buffer(audio_buffer);
|
||||
modulator->set_gain_vumeter_beep(audio_gain, play_beep ) ;
|
||||
modulator->execute(audio_buffer, buffer, configured, beep_index, beep_timer, txprogress_message, level_message, power_acc_count, divider ); // Now "Key Tones & CTCSS" baseband additon inside FM mod. dsp_modulate.cpp"
|
||||
|
||||
/* Original fw 1.3.1 good reference, beep and vu-meter
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
if (!play_beep) {
|
||||
sample = audio_buffer.p[i >> 6] >> 8; // 1536000 / 64 = 24000
|
||||
sample *= audio_gain;
|
||||
|
||||
power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter
|
||||
|
||||
if (power_acc_count) {
|
||||
power_acc_count--;
|
||||
} else {
|
||||
power_acc_count = divider;
|
||||
level_message.value = power_acc / (divider / 4); // Why ?
|
||||
shared_memory.application_queue.push(level_message);
|
||||
power_acc = 0;
|
||||
}
|
||||
} else {
|
||||
if (beep_timer) {
|
||||
beep_timer--;
|
||||
} else {
|
||||
beep_timer = baseband_fs * 0.05; // 50ms
|
||||
|
||||
if (beep_index == BEEP_TONES_NB) {
|
||||
configured = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
} else {
|
||||
beep_gen.configure(beep_deltas[beep_index], 1.0);
|
||||
beep_index++;
|
||||
}
|
||||
}
|
||||
sample = beep_gen.process(0); // TODO : Pending how to move inside modulate.cpp
|
||||
}
|
||||
*/
|
||||
|
||||
/* Original fw 1.3.1 good reference FM moulation version, including "key tones CTCSS" fw 1.3.1
|
||||
sample = tone_gen.process(sample);
|
||||
|
||||
// FM
|
||||
if (configured) {
|
||||
delta = sample * fm_delta;
|
||||
|
||||
phase += delta;
|
||||
sphase = phase >> 24;
|
||||
|
||||
re = (sine_table_i8[(sphase + 64) & 255]);
|
||||
im = (sine_table_i8[sphase]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
|
||||
buffer.p[i] = { re, im };
|
||||
|
||||
} */
|
||||
}
|
||||
|
||||
void MicTXProcessor::on_message(const Message* const msg) {
|
||||
const AudioTXConfigMessage config_message = *reinterpret_cast<const AudioTXConfigMessage*>(msg);
|
||||
const RequestSignalMessage request_message = *reinterpret_cast<const RequestSignalMessage*>(msg);
|
||||
|
||||
switch(msg->id) {
|
||||
case Message::ID::AudioTXConfig:
|
||||
if (fm_enabled) {
|
||||
dsp::modulate::FM *fm = new dsp::modulate::FM();
|
||||
|
||||
// Config fm_delta private var inside DSP modulate.cpp
|
||||
fm->set_fm_delta(config_message.deviation_hz * (0xFFFFFFUL / baseband_fs));
|
||||
|
||||
// Config properly the private tone_gen function parameters inside DSP modulate.cpp
|
||||
fm->set_tone_gen_configure(config_message.tone_key_delta, config_message.tone_key_mix_weight);
|
||||
modulator = fm;
|
||||
}
|
||||
|
||||
if (usb_enabled) {
|
||||
modulator = new dsp::modulate::SSB();
|
||||
modulator->set_mode(dsp::modulate::Mode::USB);
|
||||
}
|
||||
|
||||
if (lsb_enabled) {
|
||||
modulator = new dsp::modulate::SSB();
|
||||
modulator->set_mode(dsp::modulate::Mode::LSB);
|
||||
}
|
||||
if (am_enabled) {
|
||||
modulator = new dsp::modulate::AM();
|
||||
modulator->set_mode(dsp::modulate::Mode::AM);
|
||||
}
|
||||
if (dsb_enabled) {
|
||||
modulator = new dsp::modulate::AM();
|
||||
modulator->set_mode(dsp::modulate::Mode::DSB);
|
||||
}
|
||||
|
||||
modulator->set_over(baseband_fs / 24000); // Keep no change.
|
||||
|
||||
am_enabled = config_message.am_enabled;
|
||||
usb_enabled = config_message.usb_enabled;
|
||||
lsb_enabled = config_message.lsb_enabled;
|
||||
dsb_enabled = config_message.dsb_enabled;
|
||||
if (!am_enabled || !usb_enabled || !lsb_enabled || !dsb_enabled) {
|
||||
fm_enabled = true;
|
||||
}
|
||||
|
||||
audio_gain = config_message.audio_gain;
|
||||
divider = config_message.divider;
|
||||
power_acc_count = 0;
|
||||
|
||||
// now this config moved, in the case Message::ID::AudioTXConfig , only FM case.
|
||||
// tone_gen.configure(config_message.tone_key_delta, config_message.tone_key_mix_weight);
|
||||
|
||||
txprogress_message.done = true;
|
||||
|
||||
play_beep = false;
|
||||
configured = true;
|
||||
break;
|
||||
|
||||
case Message::ID::RequestSignal:
|
||||
if (request_message.signal == RequestSignalMessage::Signal::BeepRequest) {
|
||||
beep_index = 0;
|
||||
beep_timer = 0;
|
||||
play_beep = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<MicTXProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
78
Software/portapack-mayhem/firmware/baseband/proc_mictx.hpp
Normal file
78
Software/portapack-mayhem/firmware/baseband/proc_mictx.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_MICTX_H__
|
||||
#define __PROC_MICTX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "audio_input.hpp"
|
||||
#include "tone_gen.hpp"
|
||||
#include "dsp_modulate.hpp"
|
||||
|
||||
class MicTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const msg) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 1536000U;
|
||||
|
||||
bool configured { false };
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
int16_t audio_data[64];
|
||||
buffer_s16_t audio_buffer {
|
||||
audio_data,
|
||||
sizeof(int16_t) * 64
|
||||
};
|
||||
|
||||
AudioInput audio_input { };
|
||||
// ToneGen tone_gen { }; moved to dsp_modulate.cpp
|
||||
// ToneGen beep_gen { }; moved to dsp_modulate.cpp
|
||||
dsp::modulate::Modulator *modulator = NULL ;
|
||||
|
||||
bool am_enabled { false };
|
||||
bool fm_enabled { true };
|
||||
bool usb_enabled { false };
|
||||
bool lsb_enabled { false };
|
||||
bool dsb_enabled { false };
|
||||
|
||||
uint32_t divider { };
|
||||
float audio_gain { };
|
||||
uint64_t power_acc { 0 };
|
||||
uint32_t power_acc_count { 0 };
|
||||
bool play_beep { false };
|
||||
uint32_t fm_delta { 0 };
|
||||
uint32_t phase { 0 }, sphase { 0 };
|
||||
int32_t sample { 0 }, delta { };
|
||||
uint32_t beep_index { }, beep_timer { };
|
||||
|
||||
int8_t re { 0 }, im { 0 };
|
||||
|
||||
AudioLevelReportMessage level_message { };
|
||||
TXProgressMessage txprogress_message { };
|
||||
};
|
||||
|
||||
#endif
|
177
Software/portapack-mayhem/firmware/baseband/proc_nfm_audio.cpp
Normal file
177
Software/portapack-mayhem/firmware/baseband/proc_nfm_audio.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_nfm_audio.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
|
||||
//bool new_state;
|
||||
|
||||
if( !configured ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
||||
|
||||
feed_channel_stats(channel_out);
|
||||
|
||||
if (!pitch_rssi_enabled) {
|
||||
// Normal mode, output demodulated audio
|
||||
auto audio = demod.execute(channel_out, audio_buffer);
|
||||
audio_output.write(audio);
|
||||
|
||||
if (ctcss_detect_enabled) {
|
||||
/* 24kHz int16_t[16]
|
||||
* -> FIR filter, <300Hz pass, >300Hz stop, gain of 1
|
||||
* -> 12kHz int16_t[8] */
|
||||
auto audio_ctcss = ctcss_filter.execute(audio, work_audio_buffer);
|
||||
|
||||
// s16 to f32 for hpf
|
||||
std::array<float, 8> audio_f;
|
||||
for (size_t i = 0; i < audio_ctcss.count; i++) {
|
||||
audio_f[i] = audio_ctcss.p[i] * ki;
|
||||
}
|
||||
|
||||
hpf.execute_in_place(buffer_f32_t {
|
||||
audio_f.data(),
|
||||
audio_ctcss.count,
|
||||
audio_ctcss.sampling_rate
|
||||
});
|
||||
|
||||
// Zero-crossing detection
|
||||
for (size_t c = 0; c < audio_ctcss.count; c++) {
|
||||
cur_sample = audio_f[c];
|
||||
if (cur_sample * prev_sample < 0.0) {
|
||||
z_acc += z_timer;
|
||||
z_timer = 0;
|
||||
z_count++;
|
||||
} else
|
||||
z_timer++;
|
||||
prev_sample = cur_sample;
|
||||
}
|
||||
|
||||
if (z_count >= 30) {
|
||||
ctcss_message.value = (100 * 12000 / 2 * z_count) / z_acc;
|
||||
shared_memory.application_queue.push(ctcss_message);
|
||||
z_count = 0;
|
||||
z_acc = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Direction-finding mode; output tone with pitch related to RSSI
|
||||
for (size_t c = 0; c < 16; c++) {
|
||||
tone_buffer.p[c] = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]) * 128;
|
||||
tone_phase += tone_delta;
|
||||
}
|
||||
|
||||
audio_output.write(tone_buffer);
|
||||
|
||||
/*new_state = audio_output.is_squelched();
|
||||
|
||||
if (new_state && !old_state)
|
||||
shared_memory.application_queue.push(sig_message);
|
||||
|
||||
old_state = new_state;*/
|
||||
}
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
channel_spectrum.on_message(message);
|
||||
break;
|
||||
|
||||
case Message::ID::NBFMConfigure:
|
||||
configure(*reinterpret_cast<const NBFMConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::CaptureConfig:
|
||||
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::PitchRSSIConfigure:
|
||||
pitch_rssi_config(*reinterpret_cast<const PitchRSSIConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
|
||||
const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation;
|
||||
|
||||
const size_t demod_input_fs = channel_filter_output_fs;
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
|
||||
demod.configure(demod_input_fs, message.deviation);
|
||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(1.0f);
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
|
||||
|
||||
hpf.configure(audio_24k_hpf_30hz_config);
|
||||
ctcss_filter.configure(taps_64_lp_025_025.taps);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::pitch_rssi_config(const PitchRSSIConfigureMessage& message) {
|
||||
pitch_rssi_enabled = message.enabled;
|
||||
tone_delta = (message.rssi + 1000) * ((1ULL << 32) / 24000);
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
|
||||
} else {
|
||||
audio_output.set_stream(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<NarrowbandFMAudio>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
109
Software/portapack-mayhem/firmware/baseband/proc_nfm_audio.hpp
Normal file
109
Software/portapack-mayhem/firmware/baseband/proc_nfm_audio.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_NFM_AUDIO_H__
|
||||
#define __PROC_NFM_AUDIO_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class NarrowbandFMAudio : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)dst.data(),
|
||||
sizeof(dst) / sizeof(int16_t)
|
||||
};
|
||||
|
||||
std::array<int16_t, 16> audio { };
|
||||
const buffer_s16_t audio_buffer {
|
||||
(int16_t*)audio.data(),
|
||||
sizeof(audio) / sizeof(int16_t)
|
||||
};
|
||||
|
||||
std::array<int16_t, 16> tone { };
|
||||
const buffer_s16_t tone_buffer {
|
||||
(int16_t*)tone.data(),
|
||||
sizeof(tone) / sizeof(int16_t)
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
// For CTCSS decoding
|
||||
dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter { };
|
||||
IIRBiquadFilter hpf { };
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
|
||||
AudioOutput audio_output { };
|
||||
|
||||
SpectrumCollector channel_spectrum { };
|
||||
|
||||
uint32_t tone_phase { 0 };
|
||||
uint32_t tone_delta { 0 };
|
||||
bool pitch_rssi_enabled { false };
|
||||
|
||||
float cur_sample { }, prev_sample { };
|
||||
uint32_t z_acc { 0}, z_timer { 0 }, z_count { 0 };
|
||||
bool ctcss_detect_enabled { true };
|
||||
static constexpr float k = 32768.0f;
|
||||
static constexpr float ki = 1.0f / k;
|
||||
|
||||
bool configured { false };
|
||||
void pitch_rssi_config(const PitchRSSIConfigureMessage& message);
|
||||
void configure(const NBFMConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
|
||||
//RequestSignalMessage sig_message { RequestSignalMessage::Signal::Squelched };
|
||||
CodedSquelchMessage ctcss_message { 0 };
|
||||
};
|
||||
|
||||
#endif/*__PROC_NFM_AUDIO_H__*/
|
38
Software/portapack-mayhem/firmware/baseband/proc_noop.cpp
Normal file
38
Software/portapack-mayhem/firmware/baseband/proc_noop.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_noop.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void NOOPProcessor::execute(const buffer_c8_t& buffer) {
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
buffer.p[i] = {0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<NOOPProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
37
Software/portapack-mayhem/firmware/baseband/proc_noop.hpp
Normal file
37
Software/portapack-mayhem/firmware/baseband/proc_noop.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_NOOP_H__
|
||||
#define __PROC_NOOP_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
class NOOPProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
};
|
||||
|
||||
#endif
|
283
Software/portapack-mayhem/firmware/baseband/proc_nrfrx.cpp
Normal file
283
Software/portapack-mayhem/firmware/baseband/proc_nrfrx.cpp
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_nrfrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
void NRFRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
// FM demodulation
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
feed_channel_stats(decim_0_out);
|
||||
|
||||
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
|
||||
// Audio signal processing
|
||||
for (size_t c = 0; c < audio_oversampled.count; c++) {
|
||||
int g_srate = 4; //4 for 250KPS
|
||||
//int g_srate = 1; //1 for 1MPS, not working yet
|
||||
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
|
||||
rb_head++;
|
||||
rb_head=(rb_head)%RB_SIZE;
|
||||
|
||||
rb_buf[rb_head] = current_sample;
|
||||
|
||||
skipSamples = skipSamples - 1;
|
||||
|
||||
if (skipSamples<1)
|
||||
{
|
||||
int32_t threshold_tmp=0;
|
||||
for (int c=0;c<8*g_srate;c++)
|
||||
{
|
||||
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
|
||||
}
|
||||
|
||||
g_threshold = (int32_t)threshold_tmp/(8*g_srate);
|
||||
|
||||
int transitions=0;
|
||||
if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold)
|
||||
{
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
|
||||
transitions = transitions + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
|
||||
transitions = transitions + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool packet_detected=false;
|
||||
//if ( transitions==4 && abs(g_threshold)<15500)
|
||||
if ( transitions==4 && abs(g_threshold)<15500)
|
||||
{
|
||||
int packet_length = 0;
|
||||
uint8_t tmp_buf[10];
|
||||
uint8_t packet_data[500];
|
||||
uint8_t packet_packed[50];
|
||||
uint16_t pcf;
|
||||
uint32_t packet_crc;
|
||||
uint32_t calced_crc;
|
||||
uint64_t packet_addr_l;
|
||||
|
||||
/* extract address */
|
||||
packet_addr_l=0;
|
||||
|
||||
for (int t=0;t<5;t++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
tmp_buf[t]=byte;
|
||||
}
|
||||
|
||||
for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8;
|
||||
|
||||
//channel_number = 26;
|
||||
|
||||
|
||||
/* extract pcf */
|
||||
for (int t=0;t<2;t++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
tmp_buf[t]=byte;
|
||||
}
|
||||
|
||||
pcf = tmp_buf[0]<<8 | tmp_buf[1];
|
||||
pcf >>=7;
|
||||
|
||||
/* extract packet length, avoid excessive length packets */
|
||||
if(packet_length == 0)
|
||||
packet_length=(int)pcf>>3;
|
||||
if (packet_length>32)
|
||||
packet_detected = false;
|
||||
|
||||
/* extract data */
|
||||
for (int t=0;t<packet_length;t++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head+(6*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
packet_data[t]=byte;
|
||||
}
|
||||
|
||||
/* Prepare packed bit stream for CRC calculation */
|
||||
uint64_t packet_header=packet_addr_l;
|
||||
packet_header<<=9;
|
||||
packet_header|=pcf;
|
||||
for (int c=0;c<7;c++){
|
||||
packet_packed[c]=(packet_header>>((6-c)*8))&0xFF;
|
||||
}
|
||||
|
||||
for (int c=0;c<packet_length;c++){
|
||||
packet_packed[c+7]=packet_data[c];
|
||||
}
|
||||
|
||||
/* calculate packet crc */
|
||||
const uint8_t* data = packet_packed;
|
||||
size_t data_len = 7+packet_length;
|
||||
bool bit;
|
||||
uint8_t cc;
|
||||
uint_fast16_t crc=0x3C18;
|
||||
while (data_len--) {
|
||||
cc = *data++;
|
||||
for (uint8_t i = 0x80; i > 0; i >>= 1)
|
||||
{
|
||||
bit = crc & 0x8000;
|
||||
if (cc & i)
|
||||
{
|
||||
bit = !bit;
|
||||
}
|
||||
crc <<= 1;
|
||||
if (bit)
|
||||
{
|
||||
crc ^= 0x1021;
|
||||
}
|
||||
}
|
||||
crc &= 0xffff;
|
||||
}
|
||||
calced_crc = (uint16_t)(crc & 0xffff);
|
||||
|
||||
/* extract crc */
|
||||
for (int t=0;t<2;t++)
|
||||
{
|
||||
bool current_bit;
|
||||
uint8_t byte=0;
|
||||
for (int c=0;c<8;c++)
|
||||
{
|
||||
if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
|
||||
current_bit = true;
|
||||
else
|
||||
current_bit = false;
|
||||
byte |= current_bit << (7-c);
|
||||
}
|
||||
tmp_buf[t]=byte;
|
||||
}
|
||||
packet_crc = tmp_buf[0]<<8 | tmp_buf[1];
|
||||
|
||||
/* NRF24L01+ packet found, dump information */
|
||||
//if (packet_addr_l==0xE7E7E7E7)
|
||||
if (packet_crc==calced_crc)
|
||||
{
|
||||
data_message.is_data = false;
|
||||
data_message.value = 'A';
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = packet_addr_l;
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
for (int c=0;c<7;c++)
|
||||
{
|
||||
data_message.is_data = true;
|
||||
data_message.value = packet_addr_l >> 8;
|
||||
shared_memory.application_queue.push(data_message);
|
||||
}
|
||||
/*data_message.is_data = true;
|
||||
data_message.value = packet_addr_l;
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
data_message.is_data = true;
|
||||
data_message.value = packet_addr_l >> 8;
|
||||
shared_memory.application_queue.push(data_message);*/
|
||||
|
||||
data_message.is_data = false;
|
||||
data_message.value = 'B';
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
for (int c=0;c<packet_length;c++)
|
||||
{
|
||||
data_message.is_data = true;
|
||||
data_message.value = packet_data[c];
|
||||
shared_memory.application_queue.push(data_message);
|
||||
}
|
||||
|
||||
data_message.is_data = false;
|
||||
data_message.value = 'C';
|
||||
shared_memory.application_queue.push(data_message);
|
||||
|
||||
|
||||
packet_detected = true;
|
||||
}
|
||||
else
|
||||
packet_detected = false;
|
||||
}
|
||||
|
||||
if (packet_detected)
|
||||
{
|
||||
skipSamples=20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NRFRxProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::NRFRxConfigure)
|
||||
configure(*reinterpret_cast<const NRFRxConfigureMessage*>(message));
|
||||
}
|
||||
|
||||
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
|
||||
(void)message; //avoir unused warning
|
||||
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
|
||||
demod.configure(audio_fs, 5000);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<NRFRxProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
96
Software/portapack-mayhem/firmware/baseband/proc_nrfrx.hpp
Normal file
96
Software/portapack-mayhem/firmware/baseband/proc_nrfrx.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* 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_NRFRX_H__
|
||||
#define __PROC_NRFRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "fifo.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
class NRFRxProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 4000000;
|
||||
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst { };
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
std::array<complex16_t, 512> spectrum { };
|
||||
const buffer_c16_t spectrum_buffer {
|
||||
spectrum.data(),
|
||||
spectrum.size()
|
||||
};
|
||||
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)dst.data(),
|
||||
sizeof(dst) / sizeof(int16_t)
|
||||
};
|
||||
|
||||
|
||||
// Array size ok down to 375 bauds (24000 / 375)
|
||||
std::array<int32_t, 64> delay_line { 0 };
|
||||
std::array<int16_t, 1000> rb_buf { 0 };
|
||||
|
||||
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
int rb_head {-1};
|
||||
int32_t g_threshold {0};
|
||||
//uint8_t g_srate {8};
|
||||
uint8_t channel_number {38};
|
||||
int skipSamples {1000};
|
||||
int RB_SIZE {1000};
|
||||
|
||||
bool configured { false };
|
||||
|
||||
|
||||
void configure(const NRFRxConfigureMessage& message);
|
||||
|
||||
AFSKDataMessage data_message { false, 0 };
|
||||
};
|
||||
|
||||
#endif/*__PROC_NRFRX_H__*/
|
123
Software/portapack-mayhem/firmware/baseband/proc_ook.cpp
Normal file
123
Software/portapack-mayhem/firmware/baseband/proc_ook.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_ook.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void OOKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
int8_t re, im;
|
||||
|
||||
// This is called at 2.28M/2048 = 1113Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
// Synthesis at 2.28M/10 = 228kHz
|
||||
if (!s) {
|
||||
s = 10 - 1;
|
||||
if (sample_count >= samples_per_bit) {
|
||||
if (configured) {
|
||||
if (bit_pos >= length) {
|
||||
// End of data
|
||||
if (pause_counter == 0) {
|
||||
pause_counter = pause;
|
||||
cur_bit = 0;
|
||||
} else if (pause_counter == 1) {
|
||||
if (repeat_counter < repeat) {
|
||||
// Repeat
|
||||
bit_pos = 0;
|
||||
cur_bit = shared_memory.bb_data.data[0] & 0x80;
|
||||
txprogress_message.progress = repeat_counter + 1;
|
||||
txprogress_message.done = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
repeat_counter++;
|
||||
} else {
|
||||
// Stop
|
||||
cur_bit = 0;
|
||||
txprogress_message.done = true;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
configured = false;
|
||||
}
|
||||
pause_counter = 0;
|
||||
} else {
|
||||
pause_counter--;
|
||||
}
|
||||
} else {
|
||||
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
|
||||
bit_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
sample_count = 0;
|
||||
} else {
|
||||
sample_count++;
|
||||
}
|
||||
} else {
|
||||
s--;
|
||||
}
|
||||
|
||||
if (cur_bit) {
|
||||
phase = (phase + 200); // What ?
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const OOKConfigureMessage*>(p);
|
||||
|
||||
if (message.id == Message::ID::OOKConfigure) {
|
||||
samples_per_bit = message.samples_per_bit / 10;
|
||||
repeat = message.repeat - 1;
|
||||
length = message.stream_length;
|
||||
pause = message.pause_symbols + 1;
|
||||
|
||||
pause_counter = 0;
|
||||
s = 0;
|
||||
sample_count = samples_per_bit;
|
||||
repeat_counter = 0;
|
||||
bit_pos = 0;
|
||||
cur_bit = 0;
|
||||
txprogress_message.progress = 0;
|
||||
txprogress_message.done = false;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<OOKProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
57
Software/portapack-mayhem/firmware/baseband/proc_ook.hpp
Normal file
57
Software/portapack-mayhem/firmware/baseband/proc_ook.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_OOK_H__
|
||||
#define __PROC_OOK_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
class OOKProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const p) override;
|
||||
|
||||
private:
|
||||
bool configured = false;
|
||||
|
||||
BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
uint32_t samples_per_bit { 0 };
|
||||
uint8_t repeat { 0 };
|
||||
uint32_t length { 0 };
|
||||
uint32_t pause { 0 };
|
||||
|
||||
uint32_t pause_counter { 0 };
|
||||
uint8_t repeat_counter { 0 };
|
||||
uint8_t s { 0 };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample_count { 0 };
|
||||
uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 };
|
||||
int32_t tone_sample { 0 }, sig { 0 }, frq { 0 };
|
||||
|
||||
TXProgressMessage txprogress_message { };
|
||||
};
|
||||
|
||||
#endif
|
539
Software/portapack-mayhem/firmware/baseband/proc_pocsag.cpp
Normal file
539
Software/portapack-mayhem/firmware/baseband/proc_pocsag.cpp
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu)
|
||||
* Copyright (C) 2012-2014 Elias Oenal (multimon-ng@eliasoenal.com)
|
||||
* 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_pocsag.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <algorithm> // std::max
|
||||
#include <cmath>
|
||||
|
||||
|
||||
void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// This is called at 1500Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
// Get 24kHz audio
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
||||
auto audio = demod.execute(channel_out, audio_buffer);
|
||||
smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate
|
||||
audio_output.write(audio);
|
||||
|
||||
processDemodulatedSamples(audio.p, 16);
|
||||
extractFrames();
|
||||
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
int POCSAGProcessor::OnDataWord(uint32_t word, int pos)
|
||||
{
|
||||
packet.set(pos, word);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
int POCSAGProcessor::OnDataFrame(int len, int baud)
|
||||
{
|
||||
if (len > 0)
|
||||
{
|
||||
packet.set_bitrate(baud);
|
||||
packet.set_flag(pocsag::PacketFlag::NORMAL);
|
||||
packet.set_timestamp(Timestamp::now());
|
||||
const POCSAGPacketMessage message(packet);
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void POCSAGProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::POCSAGConfigure)
|
||||
configure();
|
||||
}
|
||||
|
||||
void POCSAGProcessor::configure() {
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
|
||||
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
|
||||
|
||||
const size_t demod_input_fs = channel_filter_output_fs;
|
||||
|
||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||
demod.configure(demod_input_fs, 4500);
|
||||
// Smoothing should be roughly sample rate over max baud
|
||||
// 24k / 3.2k is 7.5
|
||||
smooth.SetSize(8);
|
||||
audio_output.configure(false);
|
||||
|
||||
// Set up the frame extraction, limits of baud
|
||||
setFrameExtractParams(demod_input_fs, 4000, 300, 32);
|
||||
|
||||
// Mark the class as ready to accept data
|
||||
configured = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------
|
||||
// Frame extractraction methods
|
||||
// -----------------------------
|
||||
#define BAUD_STABLE (104)
|
||||
#define MAX_CONSEC_SAME (32)
|
||||
#define MAX_WITHOUT_SINGLE (64)
|
||||
#define MAX_BAD_TRANS (10)
|
||||
|
||||
#define M_SYNC (0x7cd215d8)
|
||||
#define M_NOTSYNC (0x832dea27)
|
||||
|
||||
#define M_IDLE (0x7a89c197)
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
inline int bitsDiff(unsigned long left, unsigned long right)
|
||||
{
|
||||
unsigned long xord = left ^ right;
|
||||
int count = 0;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if ((xord & 0x01) != 0) ++count;
|
||||
xord = xord >> 1;
|
||||
}
|
||||
return(count);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
void POCSAGProcessor::initFrameExtraction()
|
||||
{
|
||||
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
|
||||
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
|
||||
|
||||
m_badTransitions = 0;
|
||||
m_bitsStart = 0;
|
||||
m_bitsEnd = 0;
|
||||
m_inverted = false;
|
||||
|
||||
resetVals();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
void POCSAGProcessor::resetVals()
|
||||
{
|
||||
// Reset the parameters
|
||||
// --------------------
|
||||
m_goodTransitions = 0;
|
||||
m_badTransitions = 0;
|
||||
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
|
||||
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
|
||||
m_valMid = 0;
|
||||
|
||||
// And reset the counts
|
||||
// --------------------
|
||||
m_lastTransPos_1024 = 0;
|
||||
m_lastBitPos_1024 = 0;
|
||||
m_lastSample = 0;
|
||||
m_sampleNo = 0;
|
||||
m_nextBitPos_1024 = m_maxSymSamples_1024;
|
||||
m_nextBitPosInt = (long)m_nextBitPos_1024;
|
||||
|
||||
// Extraction
|
||||
m_fifo.numBits = 0;
|
||||
m_gotSync = false;
|
||||
m_numCode = 0;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue)
|
||||
{
|
||||
m_samplesPerSec = a_samplesPerSec;
|
||||
m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud);
|
||||
m_maxSymSamples_1024 = (uint32_t)(1024.0f*(float)a_samplesPerSec / (float)a_minBaud);
|
||||
m_maxRunOfSameValue = maxRunOfSameValue;
|
||||
|
||||
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
|
||||
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
|
||||
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
|
||||
|
||||
m_nextBitPos_1024 = m_averageSymbolLen_1024 / 2;
|
||||
m_nextBitPosInt = m_nextBitPos_1024 >> 10;
|
||||
|
||||
initFrameExtraction();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
int POCSAGProcessor::processDemodulatedSamples(float * sampleBuff, int noOfSamples)
|
||||
{
|
||||
bool transition = false;
|
||||
uint32_t samplePos_1024 = 0;
|
||||
uint32_t len_1024 = 0;
|
||||
|
||||
// Loop through the block of data
|
||||
// ------------------------------
|
||||
for (int pos = 0; pos < noOfSamples; ++pos)
|
||||
{
|
||||
m_sample = sampleBuff[pos];
|
||||
m_valMid += (m_sample - m_valMid) / 1024.0f;
|
||||
|
||||
++m_sampleNo;
|
||||
|
||||
// Detect Transition
|
||||
// -----------------
|
||||
transition = ! ((m_lastSample < m_valMid) ^ (m_sample >= m_valMid)); // use XOR for speed
|
||||
|
||||
// If this is a transition
|
||||
// -----------------------
|
||||
if (transition)
|
||||
{
|
||||
// Calculate samples since last trans
|
||||
// ----------------------------------
|
||||
int32_t fractional_1024 = (int32_t)(((m_sample - m_valMid)*1024) / (m_sample - m_lastSample));
|
||||
if (fractional_1024 < 0) { fractional_1024 = -fractional_1024; }
|
||||
|
||||
samplePos_1024 = (m_sampleNo<<10)-fractional_1024;
|
||||
len_1024 = samplePos_1024 - m_lastTransPos_1024;
|
||||
m_lastTransPos_1024 = samplePos_1024;
|
||||
|
||||
// If symbol is large enough to be valid
|
||||
// -------------------------------------
|
||||
if (len_1024 > m_minSymSamples_1024)
|
||||
{
|
||||
// Check for shortest good transition
|
||||
// ----------------------------------
|
||||
if ((len_1024 < m_shortestGoodTrans_1024) &&
|
||||
(m_goodTransitions < BAUD_STABLE)) // detect change of symbol size
|
||||
{
|
||||
int32_t fractionOfShortest_1024 = (len_1024<<10) / m_shortestGoodTrans_1024;
|
||||
|
||||
// If currently at half the baud rate
|
||||
// ----------------------------------
|
||||
if ((fractionOfShortest_1024 > 410) && (fractionOfShortest_1024 < 614)) // 0.4 and 0.6
|
||||
{
|
||||
m_averageSymbolLen_1024 /= 2;
|
||||
m_shortestGoodTrans_1024 = len_1024;
|
||||
}
|
||||
// If currently at the wrong baud rate
|
||||
// -----------------------------------
|
||||
else if (fractionOfShortest_1024 < 768) // 0.75
|
||||
{
|
||||
m_averageSymbolLen_1024 = len_1024;
|
||||
m_shortestGoodTrans_1024 = len_1024;
|
||||
m_goodTransitions = 0;
|
||||
m_lastSingleBitPos_1024 = samplePos_1024 - len_1024;
|
||||
}
|
||||
}
|
||||
|
||||
// Calc the number of bits since events
|
||||
// ------------------------------------
|
||||
int32_t halfSymbol_1024 = m_averageSymbolLen_1024 / 2;
|
||||
int bitsSinceLastTrans = max((uint32_t)1, (len_1024+halfSymbol_1024) / m_averageSymbolLen_1024 );
|
||||
int bitsSinceLastSingle = (((m_sampleNo<<10)-m_lastSingleBitPos_1024) + halfSymbol_1024) / m_averageSymbolLen_1024;
|
||||
|
||||
// Check for single bit
|
||||
// --------------------
|
||||
if (bitsSinceLastTrans == 1)
|
||||
{
|
||||
m_lastSingleBitPos_1024 = samplePos_1024;
|
||||
}
|
||||
|
||||
// If too long since last transition
|
||||
// ---------------------------------
|
||||
if (bitsSinceLastTrans > MAX_CONSEC_SAME)
|
||||
{
|
||||
resetVals();
|
||||
}
|
||||
// If too long sice last single bit
|
||||
// --------------------------------
|
||||
else if (bitsSinceLastSingle > MAX_WITHOUT_SINGLE)
|
||||
{
|
||||
resetVals();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is a good transition
|
||||
// ----------------------------
|
||||
int32_t offsetFromExtectedTransition_1024 = len_1024 - (bitsSinceLastTrans*m_averageSymbolLen_1024);
|
||||
if (offsetFromExtectedTransition_1024 < 0) { offsetFromExtectedTransition_1024 = -offsetFromExtectedTransition_1024; }
|
||||
if (offsetFromExtectedTransition_1024 < ((int32_t)m_averageSymbolLen_1024 / 4)) // Has to be within 1/4 of symbol to be good
|
||||
{
|
||||
++m_goodTransitions;
|
||||
uint32_t bitsCount = min((uint32_t)BAUD_STABLE, m_goodTransitions);
|
||||
|
||||
uint32_t propFromPrevious = m_averageSymbolLen_1024*bitsCount;
|
||||
uint32_t propFromCurrent = (len_1024 / bitsSinceLastTrans);
|
||||
m_averageSymbolLen_1024 = (propFromPrevious + propFromCurrent) / (bitsCount + 1);
|
||||
m_badTransitions = 0;
|
||||
//if ( len < m_shortestGoodTrans ){m_shortestGoodTrans = len;}
|
||||
// Store the old symbol size
|
||||
if (m_goodTransitions >= BAUD_STABLE)
|
||||
{
|
||||
m_lastStableSymbolLen_1024 = m_averageSymbolLen_1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the point of the last bit if not yet stable
|
||||
// -----------------------------------------------
|
||||
if ((m_goodTransitions < BAUD_STABLE) || (m_badTransitions > 0))
|
||||
{
|
||||
m_lastBitPos_1024 = samplePos_1024 - (m_averageSymbolLen_1024 / 2);
|
||||
}
|
||||
|
||||
// Calculate the exact positiom of the next bit
|
||||
// --------------------------------------------
|
||||
int32_t thisPlusHalfsymbol_1024 = samplePos_1024 + (m_averageSymbolLen_1024/2);
|
||||
int32_t lastPlusSymbol = m_lastBitPos_1024 + m_averageSymbolLen_1024;
|
||||
m_nextBitPos_1024 = lastPlusSymbol + ((thisPlusHalfsymbol_1024 - lastPlusSymbol) / 16);
|
||||
|
||||
// Check for bad pos error
|
||||
// -----------------------
|
||||
if (m_nextBitPos_1024 < samplePos_1024) m_nextBitPos_1024 += m_averageSymbolLen_1024;
|
||||
|
||||
// Calculate integer sample after next bit
|
||||
// ---------------------------------------
|
||||
m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1;
|
||||
|
||||
} // symbol is large enough to be valid
|
||||
else
|
||||
{
|
||||
// Bad transition, so reset the counts
|
||||
// -----------------------------------
|
||||
++m_badTransitions;
|
||||
if (m_badTransitions > MAX_BAD_TRANS)
|
||||
{
|
||||
resetVals();
|
||||
}
|
||||
}
|
||||
} // end of if transition
|
||||
|
||||
// Reached the point of the next bit
|
||||
// ---------------------------------
|
||||
if (m_sampleNo >= m_nextBitPosInt)
|
||||
{
|
||||
// Everything is good so extract a bit
|
||||
// -----------------------------------
|
||||
if (m_goodTransitions > 20)
|
||||
{
|
||||
// Store value at the center of bit
|
||||
// --------------------------------
|
||||
storeBit();
|
||||
}
|
||||
// Check for long 1 or zero
|
||||
// ------------------------
|
||||
uint32_t bitsSinceLastTrans = ((m_sampleNo<<10) - m_lastTransPos_1024) / m_averageSymbolLen_1024;
|
||||
if (bitsSinceLastTrans > m_maxRunOfSameValue)
|
||||
{
|
||||
resetVals();
|
||||
}
|
||||
|
||||
// Store the point of the last bit
|
||||
// -------------------------------
|
||||
m_lastBitPos_1024 = m_nextBitPos_1024;
|
||||
|
||||
// Calculate the exact point of the next bit
|
||||
// -----------------------------------------
|
||||
m_nextBitPos_1024 += m_averageSymbolLen_1024;
|
||||
|
||||
// Look for the bit after the next bit pos
|
||||
// ---------------------------------------
|
||||
m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1;
|
||||
|
||||
} // Reached the point of the next bit
|
||||
|
||||
m_lastSample = m_sample;
|
||||
|
||||
} // Loop through the block of data
|
||||
|
||||
return getNoOfBits();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
void POCSAGProcessor::storeBit()
|
||||
{
|
||||
if (++m_bitsStart >= BIT_BUF_SIZE) { m_bitsStart = 0; }
|
||||
|
||||
// Calculate the bit value
|
||||
float sample = (m_sample + m_lastSample) / 2;
|
||||
//int32_t sample_1024 = m_sample_1024;
|
||||
bool bit = sample > m_valMid;
|
||||
|
||||
// If buffer not full
|
||||
if (m_bitsStart != m_bitsEnd)
|
||||
{
|
||||
// Decide on output val
|
||||
if (bit)
|
||||
{
|
||||
m_bits[m_bitsStart] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bits[m_bitsStart] = 1;
|
||||
}
|
||||
}
|
||||
// Throw away bits if the buffer is full
|
||||
else
|
||||
{
|
||||
if (--m_bitsStart <= -1)
|
||||
{
|
||||
m_bitsStart = BIT_BUF_SIZE - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
int POCSAGProcessor::extractFrames()
|
||||
{
|
||||
int msgCnt = 0;
|
||||
// While there is unread data in the bits buffer
|
||||
//----------------------------------------------
|
||||
while (getNoOfBits() > 0)
|
||||
{
|
||||
m_fifo.codeword = (m_fifo.codeword << 1) + getBit();
|
||||
m_fifo.numBits++;
|
||||
|
||||
// If number of bits in fifo equals 32
|
||||
//------------------------------------
|
||||
if (m_fifo.numBits >= 32)
|
||||
{
|
||||
// Not got sync
|
||||
// ------------
|
||||
if (!m_gotSync)
|
||||
{
|
||||
if (bitsDiff(m_fifo.codeword, M_SYNC) <= 2)
|
||||
{
|
||||
m_inverted = false;
|
||||
m_gotSync = true;
|
||||
m_numCode = -1;
|
||||
m_fifo.numBits = 0;
|
||||
}
|
||||
else if (bitsDiff(m_fifo.codeword, M_NOTSYNC) <= 2)
|
||||
{
|
||||
m_inverted = true;
|
||||
m_gotSync = true;
|
||||
m_numCode = -1;
|
||||
m_fifo.numBits = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cause it to load one more bit
|
||||
m_fifo.numBits = 31;
|
||||
}
|
||||
} // Not got sync
|
||||
else
|
||||
{
|
||||
// Increment the word count
|
||||
// ------------------------
|
||||
++m_numCode; // It got set to -1 when a sync was found, now count the 16 words
|
||||
uint32_t val = m_inverted ? ~m_fifo.codeword : m_fifo.codeword;
|
||||
OnDataWord(val, m_numCode);
|
||||
|
||||
// If at the end of a 16 word block
|
||||
// --------------------------------
|
||||
if (m_numCode >= 15)
|
||||
{
|
||||
msgCnt += OnDataFrame(m_numCode+1, (m_samplesPerSec<<10) / m_lastStableSymbolLen_1024);
|
||||
m_gotSync = false;
|
||||
m_numCode = -1;
|
||||
}
|
||||
m_fifo.numBits = 0;
|
||||
}
|
||||
} // If number of bits in fifo equals 32
|
||||
} // While there is unread data in the bits buffer
|
||||
return msgCnt;
|
||||
} // extractFrames
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
short POCSAGProcessor::getBit()
|
||||
{
|
||||
if (m_bitsEnd != m_bitsStart)
|
||||
{
|
||||
if (++m_bitsEnd >= BIT_BUF_SIZE)
|
||||
{
|
||||
m_bitsEnd = 0;
|
||||
}
|
||||
return m_bits[m_bitsEnd];
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
int POCSAGProcessor::getNoOfBits()
|
||||
{
|
||||
int bits = m_bitsEnd - m_bitsStart;
|
||||
if (bits < 0) { bits += BIT_BUF_SIZE; }
|
||||
return bits;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
uint32_t POCSAGProcessor::getRate()
|
||||
{
|
||||
return ((m_samplesPerSec<<10)+512) / m_lastStableSymbolLen_1024;
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
//
|
||||
// ====================================================================
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<POCSAGProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user