/* * 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 "audio_dma.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(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); decim_1.configure(taps_11k0_decim_1.taps); 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() { audio::dma::init_audio_out(); EventDispatcher event_dispatcher{std::make_unique()}; event_dispatcher.run(); return 0; }