/* * Copyright (C) 2024 PortaPack-MAYHEM Contributors * * 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 of the License, 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. If not, see . */ #include "proc_p25_tx.hpp" #include "portapack_shared_memory.hpp" #include "sine_table_int8.hpp" #include "event_m4.hpp" #include // Gray-coded dibit → FM deviation level: // dibit 0 → +1 unit (+600 Hz) // dibit 1 → +3 units (+1800 Hz) // dibit 2 → -1 unit (-600 Hz) // dibit 3 → -3 units (-1800 Hz) static constexpr int8_t dibit_to_level[4] = {1, 3, -1, -3}; void P25TxProcessor::execute(const buffer_c8_t& buffer) { if (!configured) { // Zero output - never emit garbage while idle for (size_t i = 0; i < buffer.count; i++) buffer.p[i] = {0, 0}; return; } for (size_t i = 0; i < buffer.count; i++) { if (sample_count == 0) { if (dibit_index >= frame_length) { // Frame complete - notify A7 and stop txprogress_message.done = true; shared_memory.application_queue.push(txprogress_message); configured = false; for (size_t j = i; j < buffer.count; j++) buffer.p[j] = {0, 0}; return; } } // Current dibit → frequency level uint8_t dibit = shared_memory.bb_data.data[dibit_index] & 0x03; int8_t level = dibit_to_level[dibit]; // Accumulate FM phase at ±level × base_phase_step per sample phase += (uint32_t)((int32_t)level * (int32_t)base_phase_step); int8_t re = sine_table_i8[(phase + 0x40000000) >> 24]; // cos int8_t im = sine_table_i8[phase >> 24]; // sin buffer.p[i] = {re, im}; sample_count++; if (sample_count >= P25_SAMPLES_PER_DIBIT) { sample_count = 0; dibit_index++; } } } void P25TxProcessor::on_message(const Message* const msg) { if (msg->id == Message::ID::P25TxConfigure) { const auto& m = *reinterpret_cast(msg); if (m.frame_length == 0) { configured = false; return; } frame_length = m.frame_length; dibit_index = 0; sample_count = 0; txprogress_message.done = false; configured = true; } } int main() { EventDispatcher event_dispatcher{std::make_unique()}; event_dispatcher.run(); return 0; }