/* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek * Copyright (C) 2014 zilog80 * * 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. */ /* Notes to self (or others, welcome !): * Sharebrained wrote in matched_filter.hpp that taps should be those of a complex low-pass filter combined with a complex sinusoid, so * that the filter shifts the spectrum where we want (signal of interest around 0Hz). * * In this baseband processor, after decim_0 and decim_1, the signal ends up being sampled at 38400Hz (2457600 / 8 / 8) * Since the applied shift in ui_sonde.cpp is -fs/4 = -2457600/4 = -614400Hz to avoid the DC spike, the FSK signal ends up being * shifted by 614400 / 8 / 8 = 9600Hz. So decim_1_out should look like this: * * _______________|______/'\______ * -C A B C * * A is the DC spike at 0Hz * B is the FSK signal shifted right at 9600Hz * C is the bandwidth edge at 19200Hz * * Taps should be computed to shift the whole spectrum by -9600Hz ("left") so that it looks like this: * * ______________/'\______________ * -C D C * * Anything unwanted (like A) should have been filtered off * D is B around 0Hz now * * Then the clock_recovery function should be happy :) * * Mathworks.com says: * In the case of a single-rate FIR design, we simply multiply each set of coefficients by (aka 'heterodyne with') a complex exponential. * * Can SciPy's remez function be used for this ? See tools/firtest.py * GnuRadio's firdes only outputs an odd number of taps * * --------------------------------------------------------------------- * * Looking at the AIS baseband processor: * * Copied everything necessary to get decim_1_out (so same 8 * 8 = 64 decimation factor) * The samplerate is also the same (2457600) * After the matching filter, the data is decimated by 2 so the final samplerate for clock_recovery is 38400 / 2 = 19200Hz. * Like here, the shift used is fs/4, so decim_1_out should be looking similar. * The AIS signal deviates by 2400 (4800Hz signal width), the symbol rate is 9600. * * The matched filter's input samplerate is 38400Hz, to get a 9600Hz shift it must use 4 taps ? * To obtain unity gain, the sinusoid length must be / by the number of taps ? * * See ais_baseband.hpp * * */ #ifndef __PROC_SONDE_H__ #define __PROC_SONDE_H__ #include "baseband_processor.hpp" #include "baseband_thread.hpp" #include "rssi_thread.hpp" #include "proc_ais.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 "portapack_shared_memory.hpp" #include "audio_output.hpp" #include "tone_gen.hpp" #include "buffer.hpp" #include #include #include #define BEEP_MIN_DURATION 60 #define BEEP_DURATION_RANGE 100 #define BEEP_BASE_FREQ 200 #define RSSI_CEILING 1000 #define PROPORTIONAL_BEEP_THRES 0.8 #define RSSI_PITCH_WEIGHT 0.5 #define AUDIO_SAMPLE_RATE 24000 class SondeProcessor : public BasebandProcessor { public: SondeProcessor(); void execute(const buffer_c8_t& buffer) override; void on_message(const Message* const msg); private: static constexpr size_t baseband_fs = 2457600; std::array audio { }; const buffer_s16_t audio_buffer { (int16_t*) audio.data(), sizeof(audio) / sizeof(int16_t) }; AudioOutput audio_output { }; bool beep_play { false }; bool silence_play { false }; bool pitch_rssi_enabled { false }; uint32_t last_rssi { 0 }; ToneGen tone_gen { }; BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; RSSIThread rssi_thread { NORMALPRIO + 10 }; std::array 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 }; // Actually 4800bits/s but the Manchester coding doubles the symbol rate clock_recovery::ClockRecovery clock_recovery_fsk_9600 { 19200, 9600, { 0.0555f }, [this](const float raw_symbol) { const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; this->packet_builder_fsk_9600_Meteomodem.execute(sliced_symbol); } }; PacketBuilder packet_builder_fsk_9600_Meteomodem { { 0b00110011001100110101100110110011, 32, 1 }, { }, { 88 * 2 * 8 }, [this](const baseband::Packet& packet) { const SondePacketMessage message { sonde::Packet::Type::Meteomodem_unknown, packet }; shared_memory.application_queue.push(message); } }; clock_recovery::ClockRecovery clock_recovery_fsk_4800 { 19200, 4800, { 0.0555f }, [this](const float raw_symbol) { const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; this->packet_builder_fsk_4800_Vaisala.execute(sliced_symbol); } }; PacketBuilder packet_builder_fsk_4800_Vaisala { { 0b00001000011011010101001110001000, 32, 1 }, //euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet) //{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes. { }, { 320 * 8 }, [this](const baseband::Packet& packet) { const SondePacketMessage message { sonde::Packet::Type::Vaisala_RS41_SG, packet }; shared_memory.application_queue.push(message); } }; void play_beep(); void stop_beep(); /** * Used for filling the audio buffer with the waveform * generated by the ToneGen class: * */ void generate_beep(); /** * Used for filling the audio buffer with silence: */ void generate_silence(); void pitch_rssi_config(const PitchRSSIConfigureMessage& message); }; #endif/*__PROC_ERT_H__*/