From 0098e8dff5883acaae26a022f81b36dbb8808c58 Mon Sep 17 00:00:00 2001 From: wittend99 Date: Mon, 10 Sep 2012 16:45:37 +0000 Subject: [PATCH] git-svn-id: https://svn.code.sf.net/p/freetel/code@667 01035d8c-6547-0410-b346-abe4f91aad63 --- fdmdv2/build/fdmdv2.mk | 15 +- fdmdv2/build/fdmdv2.project | 1 - fdmdv2/build/fdmdv2.txt | 2 +- fdmdv2/src/fdmdv2_defines.h | 6 +- fdmdv2/src/fdmdv2_main.cpp | 79 ++--- fdmdv2/src/fdmdv2_main.h | 54 ++- fdmdv2/src/fdmdv2_pa_wrapper.cpp | 195 ++++++++++- fdmdv2/src/fdmdv2_pa_wrapper.h | 177 ++++++---- fdmdv2/src/fdmdv2_plot.cpp | 36 +- fdmdv2/src/fdmdv2_plot.h | 92 ++--- fdmdv2/src/fdmdv2_plot_scalar.cpp | 11 - fdmdv2/src/fdmdv2_plot_scalar.h | 11 - fdmdv2/src/fdmdv2_plot_spectrum.cpp | 48 ++- fdmdv2/src/fdmdv2_plot_waterfall.cpp | 147 ++++++-- fdmdv2/src/fdmdv2_plot_waterfall.h | 7 +- fdmdv2/src/fdmdv2_process_audio.cpp | 487 --------------------------- 16 files changed, 635 insertions(+), 733 deletions(-) delete mode 100644 fdmdv2/src/fdmdv2_process_audio.cpp diff --git a/fdmdv2/build/fdmdv2.mk b/fdmdv2/build/fdmdv2.mk index 734f6ed3..b230f0b6 100644 --- a/fdmdv2/build/fdmdv2.mk +++ b/fdmdv2/build/fdmdv2.mk @@ -13,7 +13,7 @@ CurrentFileName := CurrentFilePath := CurrentFileFullPath := User :=OFA-Staff -Date :=9/4/2012 +Date :=9/9/2012 CodeLitePath :="C:\bin\CodeLite" LinkerName :=g++ SharedObjectLinkerName :=g++ -shared -fPIC @@ -63,7 +63,7 @@ PATH:=$(WXWIN)\lib\gcc_dll;$(PATH) WXCFG:=gcc_dll\mswu UNIT_TEST_PP_SRC_DIR:=C:\bin\UnitTest++-1.3 Objects=$(IntermediateDirectory)/src_dlg_about$(ObjectSuffix) $(IntermediateDirectory)/src_dlg_audio$(ObjectSuffix) $(IntermediateDirectory)/src_dlg_comports$(ObjectSuffix) $(IntermediateDirectory)/src_dlg_options$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_main$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot$(ObjectSuffix) $(IntermediateDirectory)/src_topFrame$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_scatter$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_waterfall$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_spectrum$(ObjectSuffix) \ - $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) + $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) ## ## Main Build Targets @@ -174,14 +174,6 @@ $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(DependSuffix): ../src/fdmdv2_pa_ $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(PreprocessSuffix): ../src/fdmdv2_pa_wrapper.cpp @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(PreprocessSuffix) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_pa_wrapper.cpp" -$(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix): ../src/fdmdv2_process_audio.cpp $(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix) - $(CXX) $(IncludePCH) $(SourceSwitch) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_process_audio.cpp" $(CXXFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) $(IncludePath) -$(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix): ../src/fdmdv2_process_audio.cpp - @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) -MF$(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix) -MM "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_process_audio.cpp" - -$(IntermediateDirectory)/src_fdmdv2_process_audio$(PreprocessSuffix): ../src/fdmdv2_process_audio.cpp - @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_fdmdv2_process_audio$(PreprocessSuffix) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_process_audio.cpp" - $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix): ../src/fdmdv2_plot_scalar.cpp $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(DependSuffix) $(CXX) $(IncludePCH) $(SourceSwitch) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_plot_scalar.cpp" $(CXXFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) $(IncludePath) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(DependSuffix): ../src/fdmdv2_plot_scalar.cpp @@ -229,9 +221,6 @@ clean: $(RM) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(ObjectSuffix) $(RM) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(DependSuffix) $(RM) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(PreprocessSuffix) - $(RM) $(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) - $(RM) $(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix) - $(RM) $(IntermediateDirectory)/src_fdmdv2_process_audio$(PreprocessSuffix) $(RM) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) $(RM) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(DependSuffix) $(RM) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(PreprocessSuffix) diff --git a/fdmdv2/build/fdmdv2.project b/fdmdv2/build/fdmdv2.project index 5e984cbb..c84814c0 100644 --- a/fdmdv2/build/fdmdv2.project +++ b/fdmdv2/build/fdmdv2.project @@ -19,7 +19,6 @@ - diff --git a/fdmdv2/build/fdmdv2.txt b/fdmdv2/build/fdmdv2.txt index 8abe5d12..444adc7e 100644 --- a/fdmdv2/build/fdmdv2.txt +++ b/fdmdv2/build/fdmdv2.txt @@ -1 +1 @@ -./Release/src_dlg_about.o ./Release/src_dlg_audio.o ./Release/src_dlg_comports.o ./Release/src_dlg_options.o ./Release/src_fdmdv2_main.o ./Release/src_fdmdv2_plot.o ./Release/src_topFrame.o ./Release/src_fdmdv2_plot_scatter.o ./Release/src_fdmdv2_plot_waterfall.o ./Release/src_fdmdv2_plot_spectrum.o ./Release/src_fdmdv2_pa_wrapper.o ./Release/src_fdmdv2_process_audio.o ./Release/src_fdmdv2_plot_scalar.o +./Release/src_dlg_about.o ./Release/src_dlg_audio.o ./Release/src_dlg_comports.o ./Release/src_dlg_options.o ./Release/src_fdmdv2_main.o ./Release/src_fdmdv2_plot.o ./Release/src_topFrame.o ./Release/src_fdmdv2_plot_scatter.o ./Release/src_fdmdv2_plot_waterfall.o ./Release/src_fdmdv2_plot_spectrum.o ./Release/src_fdmdv2_pa_wrapper.o ./Release/src_fdmdv2_plot_scalar.o diff --git a/fdmdv2/src/fdmdv2_defines.h b/fdmdv2/src/fdmdv2_defines.h index 39c23658..f4fd7bc5 100644 --- a/fdmdv2/src/fdmdv2_defines.h +++ b/fdmdv2/src/fdmdv2_defines.h @@ -24,7 +24,7 @@ //========================================================================== #ifndef __FDMDV2_DEFINES__ #define __FDMDV2_DEFINES__ - +#include "wx/wx.h" /* FFT points */ #define FDMDV_NSPEC 512 @@ -71,8 +71,8 @@ #define N48 (N8*FDMDV_OS) // processing buffer size at 48 kHz #define NUM_CHANNELS 2 // I think most sound cards prefer stereo we will convert to mono -#define BITS_PER_CODEC_FRAME (2*FDMDV_BITS_PER_FRAME) -#define BYTES_PER_CODEC_FRAME (BITS_PER_CODEC_FRAME/8) +#define BITS_PER_CODEC_FRAME (2 * FDMDV_BITS_PER_FRAME) +#define BYTES_PER_CODEC_FRAME (BITS_PER_CODEC_FRAME / 8) /* 8 to 48 kHz sample rate conversion */ #define FDMDV_OS 6 /* oversampling rate */ diff --git a/fdmdv2/src/fdmdv2_main.cpp b/fdmdv2/src/fdmdv2_main.cpp index 4cbd0f3e..c8264bfb 100644 --- a/fdmdv2/src/fdmdv2_main.cpp +++ b/fdmdv2/src/fdmdv2_main.cpp @@ -31,24 +31,7 @@ #define wxUSE_PCX 1 #define wxUSE_LIBTIFF 1 -static int rxCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); -static int txCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - -float av_mag[FDMDV_NSPEC]; // shared between a few classes +//float av_mag[FDMDV_NSPEC]; // shared between a few classes // initialize the application IMPLEMENT_APP(MainApp); @@ -65,29 +48,15 @@ bool MainApp::OnInit() // Create the main application window MainFrame *frame = new MainFrame(NULL); - frame->m_panelDefaultA = new PlotPanel((wxFrame*) frame->m_auiNbookCtrl ); - frame->m_auiNbookCtrl->AddPage(frame->m_panelDefaultA, _("Test A"), true, wxNullBitmap ); - -// frame->m_panelDefaultB = new PlotPanel((wxFrame*) frame->m_auiNbookCtrl ); -// frame->m_auiNbookCtrl->AddPage(frame->m_panelDefaultB, _("Test B"), true, wxNullBitmap ); - - frame->m_panelSpectrum = new PlotSpectrum((wxFrame*) frame->m_auiNbookCtrl ); - frame->m_auiNbookCtrl->AddPage(frame->m_panelSpectrum, _("Spectrum"), true, wxNullBitmap ); - - frame->m_panelWaterfall = new PlotWaterfall((wxFrame*) frame->m_auiNbookCtrl ); - frame->m_auiNbookCtrl->AddPage(frame->m_panelWaterfall, _("Waterfall"), true, wxNullBitmap ); - -// frame->m_panelScatter = new PlotScatter((wxFrame*) frame->m_auiNbookCtrl ); -// frame->m_auiNbookCtrl->AddPage(frame->m_panelWaterfall, _("Scatter"), true, wxNullBitmap ); - -// frame->m_panelScalar = new PlotScalar((wxFrame*) frame->m_auiNbookCtrl, 500, 500); -// frame->m_auiNbookCtrl->AddPage(frame->m_panelWaterfall, _("Scalar"), true, wxNullBitmap ); + SetTopWindow(frame); + // Should guarantee that the first plot tab defined is the one + // displayed. But it doesn't when built from command line. Why? frame->m_auiNbookCtrl->ChangeSelection(0); - SetTopWindow(frame); frame->Layout(); frame->Show(); + return true; } @@ -104,6 +73,23 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) { wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); } + // Add Waterfall Plot window + m_panelWaterfall = new PlotWaterfall((wxFrame*) m_auiNbookCtrl ); + m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap ); + + // Add Spectrum Plot window + m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl ); + m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap ); + +// m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl ); +// m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Scatter"), true, wxNullBitmap ); + +// m_panelScalar = new PlotScalar((wxFrame*) m_auiNbookCtrl, 500, 500); +// m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Scalar"), true, wxNullBitmap ); + + // Add generic plot window + m_panelDefaultA = new PlotPanel((wxFrame*) m_auiNbookCtrl ); + m_auiNbookCtrl->AddPage(m_panelDefaultA, _("Test A"), true, wxNullBitmap ); } //------------------------------------------------------------------------- @@ -144,7 +130,7 @@ void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(event)) } dc.SetUserScale(m_zoom, m_zoom); const wxSize size = GetClientSize(); - dc.DrawBitmap(m_bitmap, dc.DeviceToLogicalX((size.x - m_zoom * m_bitmap.GetWidth()) / 2), dc.DeviceToLogicalY((size.y - m_zoom * m_bitmap.GetHeight()) / 2), true); +// dc.DrawBitmap(m_bitmap, dc.DeviceToLogicalX((size.x - m_zoom * m_bitmap.GetWidth()) / 2), dc.DeviceToLogicalY((size.y - m_zoom * m_bitmap.GetHeight()) / 2), true); } //------------------------------------------------------------------------- @@ -251,11 +237,12 @@ void MainFrame::OnTogBtnALCClick(wxCommandEvent& event) wxMessageBox(wxT("Got Click!"), wxT("OnTogBtnALCClick"), wxOK); event.Skip(); } +/* //------------------------------------------------------------------------- // rxCallback() //------------------------------------------------------------------------- -static int rxCallback( +int MainFrame::rxCallback( const void *inBuffer, void *outBuffer, unsigned long framesPerBuffer, @@ -283,12 +270,13 @@ static int rxCallback( *out++ = 0.5f * (leftIn + rightIn); // mixing } return paContinue; // 0; -} - +} + * */ +/* //------------------------------------------------------------------------- // txCallback() //------------------------------------------------------------------------- -static int txCallback( +int MainFrame::txCallback( const void *inBuffer, void *outBuffer, unsigned long framesPerBuffer, @@ -317,6 +305,7 @@ static int txCallback( } return paContinue; // 0; } +*/ //------------------------------------------------------------------------- // OnTogBtnOnOff() @@ -529,14 +518,12 @@ void MainFrame::OnClose( wxCommandEvent& event ) { if(m_sound != NULL) { - //if(m_sound->IsOk()IsPlaying()) - //{ + if(m_sound->IsOk()) + { m_sound->Stop(); m_sound = NULL; - //} + } } -// wxMessageBox("Got Click!", "OnClose", wxOK); -// event.Skip(); } //------------------------------------------------------------------------- diff --git a/fdmdv2/src/fdmdv2_main.h b/fdmdv2/src/fdmdv2_main.h index 986743cc..5c2b059e 100644 --- a/fdmdv2/src/fdmdv2_main.h +++ b/fdmdv2/src/fdmdv2_main.h @@ -63,7 +63,7 @@ class MainApp : public wxApp DECLARE_APP(MainApp) //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainApp +// Class MainFrame //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= class MainFrame : public TopFrame { @@ -91,15 +91,32 @@ class MainFrame : public TopFrame PaError m_rxErr; PaError m_txErr; wxSound *m_sound; - float av_mag[FDMDV_NSPEC]; - - void DoStartThread(); - void DoPauseThread(); +// static int (*rxCallback(void const*, void*, unsigned long, PaStreamCallbackTimeInfo const*, unsigned long, void*)); +/* + static int rxCallback( + const void *inBuffer, + void *outBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags statusFlags, + void *userData + ); + static int txCallback( + const void *inBuffer, + void *outBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags statusFlags, + void *userData + ); +*/ +// void DoStartThread(); +// void DoPauseThread(); //void DoResumeThread() { ... } - void OnThreadUpdate(wxThreadEvent&); - void OnThreadCompletion(wxThreadEvent&); +// void OnThreadUpdate(wxThreadEvent&); +// void OnThreadCompletion(wxThreadEvent&); protected: // Fdmdv2ThreadAudio *m_pThread; @@ -169,13 +186,30 @@ class MainFrame : public TopFrame bool m_useMemory; wxTextCtrl* m_tc; - wxBitmap m_bitmap; - wxBitmap m_alphaBitmap; +// wxBitmap m_bitmap; +// wxBitmap m_alphaBitmap; int m_zoom; // any class wishing to process wxWidgets events must use this macro // DECLARE_EVENT_TABLE() }; - +/* +typedef int (ClassName::*CallbackType)(float); + +void DoWork(CallbackType callback) +{ + //Class instance to invoke it through + ClassName instance; + + //Invocation + int result = instance->*callback(1.0f); +} + +int main(int argc, char ** argv) +{ + //Pass in SomeCallback to the DoWork + DoWork(&ClassName::Method); +} +*/ #endif //__FDMDV2_MAIN__ diff --git a/fdmdv2/src/fdmdv2_pa_wrapper.cpp b/fdmdv2/src/fdmdv2_pa_wrapper.cpp index fb4610bc..35121f6b 100644 --- a/fdmdv2/src/fdmdv2_pa_wrapper.cpp +++ b/fdmdv2/src/fdmdv2_pa_wrapper.cpp @@ -32,7 +32,8 @@ PortAudioWrap::PortAudioWrap() statusFlags = 0; streamCallback = NULL; streamFinishedCallback = NULL; - timeInfo = 0; + timeInfo = 0; + loadData(); } PortAudioWrap::~PortAudioWrap() @@ -68,7 +69,6 @@ PaError PortAudioWrap::streamClose() if(isOpen()) { PaError rv = Pa_CloseStream(stream); -// stream = NULL; return rv; } else @@ -216,14 +216,12 @@ PaError PortAudioWrap::setStreamFlags(PaStreamFlags flags) PaError PortAudioWrap::setInputDevice(PaDeviceIndex index) { -// inputDevice = index; inputBuffer.device = index; return paNoError; } PaError PortAudioWrap::setOutputDevice(PaDeviceIndex index) { -// outputDevice = index; outputBuffer.device = index; return paNoError; } @@ -233,3 +231,192 @@ PaError PortAudioWrap::setCallback(PaStreamCallback *callback) streamCallback = callback; return paNoError; } + +typedef struct +{ + float in48k[FDMDV_OS_TAPS + N48]; + float in8k[MEM8 + N8]; +} paCallBackData; + + +//---------------------------------------------------------------- +// per_frame_rx_processing() +//---------------------------------------------------------------- +void PortAudioWrap::per_frame_rx_processing( + short output_buf[], /* output buf of decoded speech samples */ + int *n_output_buf, /* how many samples currently in output_buf[] */ + int codec_bits[], /* current frame of bits for decoder */ + short input_buf[], /* input buf of modem samples input to demod */ + int *n_input_buf, /* how many samples currently in input_buf[] */ + int *nin, /* amount of samples demod needs for next call */ + int *state, /* used to collect codec_bits[] halves */ + struct CODEC2 *c2 /* Codec 2 states */ + ) +{ + struct FDMDV_STATS stats; + int sync_bit; + float rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME]; + int rx_bits[FDMDV_BITS_PER_FRAME]; + unsigned char packed_bits[BYTES_PER_CODEC_FRAME]; + float rx_spec[FDMDV_NSPEC]; + int i; + int nin_prev; + int bit; + int byte; + int next_state; + + assert(*n_input_buf <= (2 * FDMDV_NOM_SAMPLES_PER_FRAME)); + + /* + This while loop will run the demod 0, 1 (nominal) or 2 times: + + 0: when tx sample clock runs faster than rx, occasionally we + will run out of samples + + 1: normal, run decoder once, every 2nd frame output a frame of + speech samples to D/A + + 2: when tx sample clock runs slower than rx, occasionally we will + have enough samples to run demod twice. + + With a +/- 10 Hz sample clock difference at FS=8000Hz (+/- 1250 + ppm), case 0 or 1 occured about once every 30 seconds. This is + no problem for the decoded audio. + */ + while(*n_input_buf >= *nin) + { + // demod per frame processing + for(i = 0; i < *nin; i++) + { + rx_fdm[i] = (float)input_buf[i]/FDMDV_SCALE; + } + nin_prev = *nin; + fdmdv_demod(fdmdv_state, rx_bits, &sync_bit, rx_fdm, nin); + *n_input_buf -= nin_prev; + assert(*n_input_buf >= 0); + + // shift input buffer + for(i=0; i<*n_input_buf; i++) + { + input_buf[i] = input_buf[i+nin_prev]; + } + + // compute rx spectrum & get demod stats, and update GUI plot data + fdmdv_get_rx_spectrum(fdmdv_state, rx_spec, rx_fdm, nin_prev); + fdmdv_get_demod_stats(fdmdv_state, &stats); + averageData(rx_spec); +// aScatter->add_new_samples(stats.rx_symbols); +// aTimingEst->add_new_sample(stats.rx_timing); +// aFreqEst->add_new_sample(stats.foff); +// aSNR->add_new_sample(stats.snr_est); + /* + State machine to: + + + Mute decoded audio when out of sync. The demod is synced + when we are using the fine freq estimate and SNR is above + a thresh. + + + Decode codec bits only if we have a 0,1 sync bit + sequence. Collects two frames of demod bits to decode + one frame of codec bits. + */ + next_state = *state; + switch(*state) + { + case 0: + // mute output audio when out of sync + if(*n_output_buf < 2 * codec2_samples_per_frame(c2) - N8) + { + for(i=0; i 3.0)) + { + next_state = 1; + } + break; + + case 1: + if(sync_bit == 0) + { + next_state = 2; + // first half of frame of codec bits + memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME * sizeof(int)); + } + else + { + next_state = 1; + } + if(stats.fest_coarse_fine == 0) + { + next_state = 0; + } + break; + + case 2: + next_state = 1; + if(stats.fest_coarse_fine == 0) + { + next_state = 0; + } + if(sync_bit == 1) + { + // second half of frame of codec bits + memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int)); + // pack bits, MSB received first + bit = 7; + byte = 0; + memset(packed_bits, 0, BYTES_PER_CODEC_FRAME); + for(i = 0; i < BITS_PER_CODEC_FRAME; i++) + { + packed_bits[byte] |= (codec_bits[i] << bit); + bit--; + if(bit < 0) + { + bit = 7; + byte++; + } + } + assert(byte == BYTES_PER_CODEC_FRAME); + // add decoded speech to end of output buffer + if(*n_output_buf <= codec2_samples_per_frame(c2)) + { + codec2_decode(c2, &output_buf[*n_output_buf], packed_bits); + *n_output_buf += codec2_samples_per_frame(c2); + } + assert(*n_output_buf <= (2 * codec2_samples_per_frame(c2))); + } + break; + } + *state = next_state; + } +} + +//---------------------------------------------------------------- +// update average of each spectrum point +//---------------------------------------------------------------- +void PortAudioWrap::averageData(float mag_dB[]) +{ + int i; + + for(i = 0; i < FDMDV_NSPEC; i++) + { + m_av_mag[i] = (1.0 - BETA) * m_av_mag[i] + BETA * mag_dB[i]; + } +} + +//---------------------------------------------------------------- +// create Dummy Data +//---------------------------------------------------------------- +void PortAudioWrap::loadData() +{ + int i; + for(i = 0; i < FDMDV_NSPEC; i++) + { + m_av_mag[i] = i; + } +} diff --git a/fdmdv2/src/fdmdv2_pa_wrapper.h b/fdmdv2/src/fdmdv2_pa_wrapper.h index 0076f5b4..657baa05 100644 --- a/fdmdv2/src/fdmdv2_pa_wrapper.h +++ b/fdmdv2/src/fdmdv2_pa_wrapper.h @@ -1,14 +1,14 @@ //========================================================================== // Name: fdmdv2_pa_wrapper.h // Purpose: Defines a wrapper class around PortAudio -// Created: August 12, 2012 +// Created: August 12, 2012 // Initial author: David Witten // License: // -// Copyright (C) 2012 David Witten -// -// All rights reserved. -// +// Copyright (C) 2012 David Witten +// +// All rights reserved. +// // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License version 2.1, // as published by the Free Software Foundation. This program is @@ -20,82 +20,139 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program; if not, see . // -//========================================================================== +//========================================================================== #include #include +#include "fdmdv2_defines.h" +#include "fdmdv.h" +#include "codec2.h" #include "portaudio.h" - + // Note that many of the older ISA sound cards on PCs do NOT support // full duplex audio (simultaneous record and playback). // And some only support full duplex at lower sample rates. //#define SAMPLE_RATE (44100) +// #define PA_SAMPLE_TYPE paFloat32 //paInt16 #define FRAMES_PER_BUFFER (64) typedef float SAMPLE; -float CubicAmplifier(float input); - -class PortAudioWrap -{ - public: - PortAudioWrap(); +class PortAudioWrap +{ + public: + PortAudioWrap(); ~PortAudioWrap(); + + float m_av_mag[FDMDV_NSPEC]; - private: - PaStream *stream; + private: + PaStream *stream; PaStreamParameters inputBuffer; PaStreamParameters outputBuffer; - void *userData; - int samplerate; + void *userData; + int samplerate; unsigned long framesPerBuffer; - PaStreamCallbackFlags statusFlags; - PaStreamCallback *streamCallback; - PaStreamFinishedCallback *streamFinishedCallback; + PaStreamCallbackFlags statusFlags; + PaStreamCallback *streamCallback; + PaStreamFinishedCallback *streamFinishedCallback; const PaStreamCallbackTimeInfo *timeInfo; -// PaDeviceIndex inputDevice; -// PaDeviceIndex outputDevice; + struct FDMDV *fdmdv_state; public: - - PaError streamOpen(); + void per_frame_rx_processing( + short output_buf[], /* output buf of decoded speech samples */ + int *n_output_buf, /* how many samples currently in output_buf[] */ + int codec_bits[], /* current frame of bits for decoder */ + short input_buf[], /* input buf of modem samples input to demod */ + int *n_input_buf, /* how many samples currently in input_buf[] */ + int *nin, /* amount of samples demod needs for next call */ + int *state, /* used to collect codec_bits[] halves */ + struct CODEC2 *c2 /* Codec 2 states */ + ); - PaDeviceIndex getDefaultInputDevice(); - PaDeviceIndex getDefaultOutputDevice(); + /* + static int Callback( + const void *inBuffer, + void *outBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags statusFlags, + void *userData + ); + static int txCallback( + const void *inBuffer, + void *outBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags statusFlags, + void *userData + ); + */ - PaStreamParameters *getDeviceInfo(PaDeviceIndex idx); - PaError setFramesPerBuffer(unsigned long size); - PaError setSampleRate(unsigned long size); - PaError setStreamFlags(PaStreamFlags flags); - PaError setCallback(PaStreamCallback *streamCallback); - PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback ) { streamCallback = callback; return 0;} - PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); - - PaError setInputDevice(PaDeviceIndex dev); - PaError setInputChannelCount(int count); - int getInputChannelCount(); - PaError setInputSampleFormat(PaSampleFormat format); - PaError setInputSampleRate(PaSampleFormat format); - PaError setInputLatency(PaTime latency); - void setInputHostApiStreamInfo(void *info = NULL); - PaTime getInputDefaultLowLatency(); - const char *getDeviceName(PaDeviceIndex dev); - - PaError setOutputDevice(PaDeviceIndex dev); - PaError setOutputChannelCount(int count); - const int getOutputChannelCount(); - PaError setOutputSampleFormat(PaSampleFormat format); - PaError setOutputLatency(PaTime latency); - void setOutputHostApiStreamInfo(void *info = NULL); - PaTime getOutputDefaultLowLatency(); + PaDeviceIndex getDefaultInputDevice(); + PaDeviceIndex getDefaultOutputDevice(); + PaStreamParameters *getDeviceInfo(PaDeviceIndex idx); + + PaError setFramesPerBuffer(unsigned long size); + PaError setSampleRate(unsigned long size); + PaError setStreamFlags(PaStreamFlags flags); + PaError setCallback(PaStreamCallback *streamCallback); + PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback) { streamCallback = callback; return 0;} + PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback); + PaError streamOpen(); + + PaError setInputDevice(PaDeviceIndex dev); + PaError setInputChannelCount(int count); + int getInputChannelCount(); + PaError setInputSampleFormat(PaSampleFormat format); + PaError setInputSampleRate(PaSampleFormat format); + PaError setInputLatency(PaTime latency); + void setInputHostApiStreamInfo(void *info = NULL); + PaTime getInputDefaultLowLatency(); + const char *getDeviceName(PaDeviceIndex dev); + + PaError setOutputDevice(PaDeviceIndex dev); + PaError setOutputChannelCount(int count); + const int getOutputChannelCount(); + PaError setOutputSampleFormat(PaSampleFormat format); + PaError setOutputLatency(PaTime latency); + PaError streamStart(); + PaError streamClose(); + void setOutputHostApiStreamInfo(void *info = NULL); + PaTime getOutputDefaultLowLatency(); + void averageData(float mag_dB[]); + + void setFdmdvState(FDMDV* fdmdv_state) {this->fdmdv_state = fdmdv_state;} +// void setFramesPerBuffer(unsigned long framesPerBuffer) {this->framesPerBuffer = framesPerBuffer;} + void setInputBuffer(const PaStreamParameters& inputBuffer) {this->inputBuffer = inputBuffer;} +// void setAvMag(float av_mag) {this->m_av_mag = av_mag;} + void setOutputBuffer(const PaStreamParameters& outputBuffer) {this->outputBuffer = outputBuffer;} + void setSamplerate(int samplerate) {this->samplerate = samplerate;} + void setStatusFlags(const PaStreamCallbackFlags& statusFlags) {this->statusFlags = statusFlags;} + void setStream(PaStream* stream) {this->stream = stream;} +// void setStreamCallback(PaStreamCallback* streamCallback) {this->streamCallback = streamCallback;} + void setStreamFinishedCallback(PaStreamFinishedCallback* streamFinishedCallback) {this->streamFinishedCallback = streamFinishedCallback;} + void setTimeInfo(PaStreamCallbackTimeInfo* timeInfo) {this->timeInfo = timeInfo;} + void setUserData(void* userData) {this->userData = userData;} + FDMDV* getFdmdvState() {return fdmdv_state;} + unsigned long getFramesPerBuffer() const {return framesPerBuffer;} + const PaStreamParameters& getInputBuffer() const {return inputBuffer;} +// const float *getAvMag(int idx) const {return (float *)&m_av_mag[idx];} + const PaStreamParameters& getOutputBuffer() const {return outputBuffer;} + int getSamplerate() const {return samplerate;} + const PaStreamCallbackFlags& getStatusFlags() const {return statusFlags;} + PaStream* getStream() {return stream;} +// PaStreamCallback* getStreamCallback() {return streamCallback;} +// PaStreamFinishedCallback* getStreamFinishedCallback() {return streamFinishedCallback;} +// PaStreamCallbackTimeInfo* getTimeInfo() {return timeInfo;} + void *getUserData() {return userData;} -// PaError init(); - PaError streamStart(); - PaError streamClose(); - void terminate(); - bool isOpen() const; - void stop(); - void abort(); - bool isStopped() const; - bool isActive() const; + void terminate(); + void stop(); + void abort(); + bool isOpen() const; + bool isStopped() const; + bool isActive() const; + void loadData(); }; diff --git a/fdmdv2/src/fdmdv2_plot.cpp b/fdmdv2/src/fdmdv2_plot.cpp index a1d4e9c9..dfabe00a 100644 --- a/fdmdv2/src/fdmdv2_plot.cpp +++ b/fdmdv2/src/fdmdv2_plot.cpp @@ -8,8 +8,6 @@ // components of this project) //========================================================================== #include -#include "wx/wx.h" -#include "fdmdv2_main.h" #include "fdmdv2_plot.h" BEGIN_EVENT_TABLE(PlotPanel, wxPanel) @@ -27,13 +25,31 @@ END_EVENT_TABLE() //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent) { + m_pNoteBook = (wxAuiNotebook *) parent; + m_pTopFrame = (MainFrame *)m_pNoteBook->GetParent(); + m_bmp = new wxBitmap(MAX_BMP_X, MAX_BMP_Y, wxBITMAP_SCREEN_DEPTH); + wxNativePixelData m_pBmp(*m_bmp); + if ( !m_pBmp ) + { + // ... raw access to bitmap data unavailable, do something else ... + return; + } + + if ( m_pBmp.GetWidth() < 20 || m_pBmp.GetHeight() < 20 ) + { + // ... complain: the bitmap it too small ... + return; + } + + m_zoomFactor = 1.0; m_clip = false; m_use_bitmap = true; - m_zoomFactor = 1.0; + m_newdata = false; m_rubberBand = false; m_mouseDown = false; m_penShortDash = wxPen(wxColor(0xA0, 0xA0, 0xA0), 1, wxPENSTYLE_SHORT_DASH); m_penDotDash = wxPen(wxColor(0xD0, 0xD0, 0xD0), 1, wxPENSTYLE_DOT_DASH); + m_penSolid = wxPen(wxColor(0x00, 0x00, 0x00), 1, wxPENSTYLE_SOLID); // m_gridLeftOffset = 20; // m_gridRightOffset = 5; // m_gridTopOffset = 5; @@ -42,6 +58,17 @@ PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent) SetLabelSize(10.0); } +//------------------------------------------------------------------------- +// ~PlotPanel() +//------------------------------------------------------------------------- +PlotPanel::~PlotPanel() +{ + if(!m_bmp->IsNull()) + { + delete m_bmp; + } +} + //------------------------------------------------------------------------- // GetLabelSize() //------------------------------------------------------------------------- @@ -150,7 +177,7 @@ void PlotPanel::drawGraticule(wxAutoBufferedPaintDC& dc) // Vertical gridlines dc.SetPen(m_penShortDash); - for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < m_w; p += GRID_INCREMENT) + for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_w - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT) { dc.DrawLine(p, (m_h + PLOT_BORDER), p, PLOT_BORDER); } @@ -197,7 +224,6 @@ void PlotPanel::draw(wxAutoBufferedPaintDC& dc) dc.SetBrush(ltBlueBrush); dc.SetPen(wxPen(BLACK_COLOR, 1)); dc.DrawRectangle(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_w, m_h); - drawGraticule(dc); } diff --git a/fdmdv2/src/fdmdv2_plot.h b/fdmdv2/src/fdmdv2_plot.h index 48200bfc..c6086897 100644 --- a/fdmdv2/src/fdmdv2_plot.h +++ b/fdmdv2/src/fdmdv2_plot.h @@ -7,14 +7,19 @@ // License: BSD License (other licenses may apply to other // components of this project) //========================================================================== +//#include "fdmdv2_main.h" #ifndef __FDMDV2_PLOT__ #define __FDMDV2_PLOT__ -//#include "codec2.h" -//#include "fdmdv.h" +#include +#include #include #include #include +#define MAX_ZOOM 7 +#define MAX_BMP_X (400 * MAX_ZOOM) +#define MAX_BMP_Y (400 * MAX_ZOOM) + #define wxUSE_FILEDLG 1 #define wxUSE_LIBPNG 1 #define wxUSE_LIBJPEG 1 @@ -24,7 +29,7 @@ #define PLOT_BORDER 3 #define XLEFT_OFFSET 30 -#define XLEFT_TEXT_OFFSET 7 +#define XLEFT_TEXT_OFFSET 8 #define YBOTTOM_OFFSET 25 #define GRID_INCREMENT 50 #define GREY_COLOR wxColor(0x80, 0x80, 0x80) @@ -62,6 +67,8 @@ wxWHITE_BRUSH */ +class MainFrame; + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= // Class PlotPanel //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= @@ -69,56 +76,61 @@ class PlotPanel : public wxPanel { public: PlotPanel(wxFrame* parent); - void paintEvent(wxPaintEvent & evt); - void draw(wxAutoBufferedPaintDC& dc); - void drawGraticule(wxAutoBufferedPaintDC& dc); + ~PlotPanel(); wxPen m_penShortDash; wxPen m_penDotDash; + wxPen m_penSolid; wxRect m_rectCtrl; wxRect m_rectGrid; wxRect m_rectPlot; -// int m_gridLeftOffset; -// int m_gridRightOffset; -// int m_gridTopOffset; -// int m_gridBottomOffset; + MainFrame *m_pTopFrame; + wxAuiNotebook *m_pNoteBook; double m_label_size; wxSize m_Bufsz; + bool m_newdata; wxBitmap *m_bmp; - wxImagePixelData *m_pBmp; - //wxNativePixelData *m_pBmp; - //wxAlphaPixelData *m_pBmp; + wxNativePixelData *m_pBmp; // some useful events - void OnMouseMove(wxMouseEvent& event); - void OnMouseDown(wxMouseEvent& event); - void OnMouseUp(wxMouseEvent& event); - void OnMouseWheelMoved(wxMouseEvent& event); - void OnClose(wxCloseEvent& event ){ event.Skip(); } - void OnSize( wxSizeEvent& event ); - void OnErase(wxEraseEvent& event); - void OnPaint(wxPaintEvent& event); - - double SetZoomFactor(double zf); - double GetZoomFactor(double zf); + void OnMouseMove(wxMouseEvent& event); + void OnMouseDown(wxMouseEvent& event); + void OnMouseUp(wxMouseEvent& event); + void OnMouseWheelMoved(wxMouseEvent& event); + void OnClose(wxCloseEvent& event ){ event.Skip(); } + void OnSize( wxSizeEvent& event ); + void OnErase(wxEraseEvent& event); + void OnPaint(wxPaintEvent& event); //void OnUpdateUI( wxUpdateUIEvent& event ){ event.Skip(); } - void OnShow(wxShowEvent& event); - double GetLabelSize(); - void SetLabelSize(double size); + + void paintEvent(wxPaintEvent & evt); + virtual void draw(wxAutoBufferedPaintDC& dc); + virtual void drawGraticule(wxAutoBufferedPaintDC& dc); + virtual double SetZoomFactor(double zf); + virtual double GetZoomFactor(double zf); + virtual void OnShow(wxShowEvent& event); + virtual double GetLabelSize(); + virtual void SetLabelSize(double size); protected: - int m_x; - int m_y; - int m_w; - int m_h; - int m_prev_w; - int m_prev_h; - int m_prev_x; - int m_prev_y; - bool m_use_bitmap; - bool m_clip; - bool m_rubberBand; - bool m_mouseDown; - double m_zoomFactor; + int m_x; + int m_y; + int m_w; + int m_h; + int m_left; + int m_top; + int m_prev_w; + int m_prev_h; + int m_prev_x; + int m_prev_y; + bool m_use_bitmap; + bool m_clip; + bool m_rubberBand; + bool m_mouseDown; + double m_zoomFactor; +// int m_gridLeftOffset; +// int m_gridRightOffset; +// int m_gridTopOffset; +// int m_gridBottomOffset; DECLARE_EVENT_TABLE() }; #endif //__FDMDV2_PLOT__ diff --git a/fdmdv2/src/fdmdv2_plot_scalar.cpp b/fdmdv2/src/fdmdv2_plot_scalar.cpp index 3ccfb423..4d238b5b 100644 --- a/fdmdv2/src/fdmdv2_plot_scalar.cpp +++ b/fdmdv2/src/fdmdv2_plot_scalar.cpp @@ -183,17 +183,6 @@ void PlotScalar::draw(wxAutoBufferedPaintDC& dc) //fl_pop_clip(); } -// // update average of each spectrum point -// void new_data(float mag_dB[]) -// { -// int i; -// -// for(i=0; i PLOT_BORDER; p -= GRID_INCREMENT) + for(p = 0; (y_zero + p) < m_h ; p += GRID_INCREMENT) { - dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (p + PLOT_BORDER), (m_w + PLOT_BORDER + XLEFT_OFFSET), (p + PLOT_BORDER)); + if(p > 0) + { + dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (y_zero + p), (m_w + PLOT_BORDER + XLEFT_OFFSET), (y_zero + p)); + sprintf(buf, "%6.0f", (double)(p) * -10); + dc.DrawText(buf, XLEFT_TEXT_OFFSET, (y_zero + p + TEXT_BASELINE_OFFSET_Y)); + + dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (y_zero - p), (m_w + PLOT_BORDER + XLEFT_OFFSET), (y_zero - p)); + sprintf(buf, "%6.0f", (double)(p) * 10); + dc.DrawText(buf, XLEFT_TEXT_OFFSET, (y_zero - p + TEXT_BASELINE_OFFSET_Y)); + } } + // Label the X-Axis dc.SetPen(wxPen(GREY_COLOR, 1)); for(p = GRID_INCREMENT; p < (m_w - YBOTTOM_OFFSET); p += GRID_INCREMENT) @@ -75,19 +95,14 @@ void PlotSpectrum::drawGraticule(wxAutoBufferedPaintDC& dc) sprintf(buf, "%1.1f Hz",(double)(p / 10)); dc.DrawText(buf, p - PLOT_BORDER + XLEFT_OFFSET, m_h + YBOTTOM_OFFSET/2); } - // Label the Y-Axis - for(p = (m_h - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - sprintf(buf, "%1.0f", (double)((m_h - p) * -10)); - dc.DrawText(buf, XLEFT_TEXT_OFFSET, p); - } } //---------------------------------------------------------------- // draw() //---------------------------------------------------------------- void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) -{ +{ +/* float x_px_per_point = 0.0; float y_px_per_dB = 0.0; int i; @@ -98,7 +113,7 @@ void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) float mag1, mag2; char label[20]; float px_per_hz; - +*/ m_rectCtrl = GetClientRect(); m_rectGrid = m_rectCtrl; @@ -114,10 +129,13 @@ void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) dc.Clear(); // Draw a filled rectangle with aborder - wxBrush ltBlueBrush = wxBrush(LIGHT_YELLOW_COLOR); - dc.SetBrush(ltBlueBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - dc.DrawRectangle(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_w, m_h); + wxBrush ltYellowBrush = wxBrush(LIGHT_YELLOW_COLOR); + dc.SetBrush(ltYellowBrush); +// dc.SetBrush(BLACK_COLOR); +// dc.SetPen(wxPen(BLACK_COLOR, 1)); + m_top = PLOT_BORDER; + m_left = PLOT_BORDER + XLEFT_OFFSET; + dc.DrawRectangle(m_left, m_top, m_w, m_h); drawGraticule(dc); /* diff --git a/fdmdv2/src/fdmdv2_plot_waterfall.cpp b/fdmdv2/src/fdmdv2_plot_waterfall.cpp index 8454cbda..d799896b 100644 --- a/fdmdv2/src/fdmdv2_plot_waterfall.cpp +++ b/fdmdv2/src/fdmdv2_plot_waterfall.cpp @@ -24,9 +24,11 @@ //========================================================================== #include #include "wx/wx.h" +#include "fdmdv2_main.h" #include "fdmdv2_plot_waterfall.h" -extern float *av_mag; +//extern float *av_mag; + /* Notes: @@ -56,14 +58,16 @@ PlotWaterfall::PlotWaterfall(wxFrame* parent): PlotPanel(parent) { int i; - m_bmp = new wxBitmap(MAX_BMP_X, MAX_BMP_Y, wxBITMAP_SCREEN_DEPTH); +// m_bmp = new wxBitmap(MAX_BMP_X, MAX_BMP_Y, wxBITMAP_SCREEN_DEPTH); for(i = 0; i < 255; i++) { m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0); } m_greyscale = 0; SetLabelSize(10.0); - m_Bufsz = GetMaxClientSize(); + m_Bufsz = GetMaxClientSize(); +// m_newdata = true; + m_newdata = false; }; //---------------------------------------------------------------- @@ -71,10 +75,6 @@ PlotWaterfall::PlotWaterfall(wxFrame* parent): PlotPanel(parent) //---------------------------------------------------------------- PlotWaterfall::~PlotWaterfall() { - if(!m_bmp->IsNull()) - { - delete m_bmp; - } } /* @@ -95,7 +95,7 @@ void PlotWaterfall::new_pixel_buf(int w, int h) delete m_bmp; //m_Bufsz = h * w; - //m_img = new wxImage(sz, pdata, alpha,false); + //m_img = new wxImage(sz, pdata, alpha, false); m_bmp = new wxBitmap(w, h, wxBITMAP_SCREEN_DEPTH); //m_pBmp = m_bmp->GetBitmapData(); } @@ -171,7 +171,7 @@ void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc) // Vertical gridlines dc.SetPen(m_penShortDash); - for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < m_w; p += GRID_INCREMENT) + for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_w - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT) { dc.DrawLine(p, (m_h + PLOT_BORDER), p, PLOT_BORDER); } @@ -207,9 +207,6 @@ void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc) m_rectGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); m_rectGrid.Offset(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); -// m_rectGrid.Deflate(PLOT_BORDER, (PLOT_BORDER + (YBOTTOM_OFFSET/2))); -// m_rectGrid.Offset(PLOT_BORDER, PLOT_BORDER); - m_h = m_rectGrid.GetHeight(); m_w = m_rectGrid.GetWidth(); @@ -223,6 +220,11 @@ void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc) drawGraticule(dc); //plotData(dc); + if(m_newdata) + { + m_newdata = false; + plotPixelData(dc); + } } //---------------------------------------------------------------- @@ -244,6 +246,7 @@ void PlotWaterfall::plotData(wxAutoBufferedPaintDC& dc) unsigned *last_row; unsigned *pdest; unsigned *psrc; +// float *av_mag = ((MainFrame *)GetParent())->m_av_mag; /* detect resizing of window */ /* if ((m_h != m_prev_h) || (m_w != m_prev_w)) @@ -262,19 +265,21 @@ void PlotWaterfall::plotData(wxAutoBufferedPaintDC& dc) bytes_in_row_of_blocks = dy * m_w * sizeof(unsigned); for(b = 0; b < dy_blocks - 1; b++) { - pdest = m_pixel_buf + b * m_w * dy; - psrc = m_pixel_buf + (b + 1) * m_w * dy; + pdest = (unsigned int *)m_pBmp + b * m_w * dy; + psrc = (unsigned int *)m_pBmp + (b + 1) * m_w * dy; memcpy(pdest, psrc, bytes_in_row_of_blocks); } // create a new row of blocks at bottom spec_index_per_px = (float)FDMDV_NSPEC / (float) m_w; intensity_per_dB = (float)256 /(MAX_DB - MIN_DB); - last_row = m_pixel_buf + dy *(dy_blocks - 1)* m_w; + last_row = (unsigned int *)m_pBmp + dy *(dy_blocks - 1)* m_w; for(px = 0; px < m_w; px++) { index = px * spec_index_per_px; - intensity = intensity_per_dB * (av_mag[index] - MIN_DB); - if (intensity > 255) + intensity = intensity_per_dB * (m_pTopFrame->m_rxPa->m_av_mag[index] - MIN_DB); + //intensity = intensity_per_dB * m_pTopFrame->m_rxPa->getAvMag(index) - MIN_DB); + + if(intensity > 255) { intensity = 255; } @@ -282,7 +287,7 @@ void PlotWaterfall::plotData(wxAutoBufferedPaintDC& dc) { intensity = 0; } - if (m_greyscale) + if(m_greyscale) { for(py = 0; py < dy; py++) { @@ -297,12 +302,112 @@ void PlotWaterfall::plotData(wxAutoBufferedPaintDC& dc) } } } - // update bit map //fl_draw_image((unsigned char*)m_pixel_buf, m_x, m_y, m_w, m_h, 4, 0); - //dc.DrawLines(4, m_pBmp, 0, 0 ); -} + //dc.DrawLines(4, m_pBmp, 0, 0 ); +} + +//------------------------------------------------------------------------- +// plotPixelData() +//------------------------------------------------------------------------- +void PlotWaterfall::plotPixelData(wxAutoBufferedPaintDC& dc) +{ + float spec_index_per_px; + float intensity_per_dB; + int px_per_sec; + int index; + int dy; + int dy_blocks; + int bytes_in_row_of_blocks; + int b; + int px; + int py; + int intensity; + unsigned *last_row; + unsigned *pdest; + unsigned *psrc; + + // determine dy, the height of one "block" + px_per_sec = (float)m_h / WATERFALL_SECS_Y; + dy = DT * px_per_sec; + // number of dy high blocks in spectrogram + dy_blocks = m_h / dy; + // shift previous bit map + bytes_in_row_of_blocks = dy * m_w * sizeof(unsigned); + for(b = 0; b < dy_blocks - 1; b++) + { + pdest = (unsigned int *)m_pBmp + b * m_w * dy; + psrc = (unsigned int *)m_pBmp + (b + 1) * m_w * dy; + memcpy(pdest, psrc, bytes_in_row_of_blocks); + } + // create a new row of blocks at bottom + spec_index_per_px = (float)FDMDV_NSPEC / (float) m_w; + intensity_per_dB = (float)256 /(MAX_DB - MIN_DB); + last_row = (unsigned int *)m_pBmp + dy *(dy_blocks - 1)* m_w; + + wxNativePixelData data(*m_bmp); + if(!data) + { + // ... raw access to bitmap data unavailable, do something else ... + return; + } + if(data.GetWidth() < 20 || data.GetHeight() < 20) + { + // ... complain: the bitmap it too small ... + return; + } + wxNativePixelData::Iterator p(data); + // we draw a (10, 10)-(20, 20) rect manually using the given r, g, b + p.Offset(data, 10, 10); + for(px = 0; px < m_w; px++) + { + index = px * spec_index_per_px; + // intensity = intensity_per_dB * (m_av_mag[index] - MIN_DB); + intensity = intensity_per_dB * (((MainFrame *)GetParent())->m_rxPa->m_av_mag[index] - MIN_DB); + if(intensity > 255) + { + intensity = 255; + } + if(intensity > 255) + { + intensity = 255; + } + if (intensity < 0) + { + intensity = 0; + } + if(m_greyscale) + { + for(py = 0; py < dy; py++) + { + last_row[px + py * m_w] = intensity << 8; + } + } + else + { + for(py = 0; py < dy; py++) + { + last_row[px + py * m_w] = m_heatmap_lut[intensity]; + } + } + } +/* + for(int y = 0; y < 10; ++y) + { + wxNativePixelData::Iterator rowStart = p; + for(int x = 0; x < 10; ++x, ++p) + { + p.Red() = r; + p.Green() = g; + p.Blue() = b; + } + p = rowStart; + p.OffsetY(data, 1); + } +*/ +} + //---------------------------------------------------------------- // paintEvent() // diff --git a/fdmdv2/src/fdmdv2_plot_waterfall.h b/fdmdv2/src/fdmdv2_plot_waterfall.h index be28fcde..6337096c 100644 --- a/fdmdv2/src/fdmdv2_plot_waterfall.h +++ b/fdmdv2/src/fdmdv2_plot_waterfall.h @@ -28,10 +28,6 @@ #include "fdmdv2_plot.h" #include "fdmdv2_defines.h" -#define MAX_ZOOM 7 -#define MAX_BMP_X (400 * MAX_ZOOM) -#define MAX_BMP_Y (400 * MAX_ZOOM) - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= // Class PlotWaterfall //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= @@ -42,7 +38,7 @@ public: ~PlotWaterfall(); protected: - unsigned *m_pixel_buf; +// unsigned *m_pixel_buf; unsigned m_heatmap_lut[256]; int m_greyscale; @@ -54,6 +50,7 @@ protected: void drawGraticule(wxAutoBufferedPaintDC& dc); void draw(wxAutoBufferedPaintDC& dc); void plotData(wxAutoBufferedPaintDC& dc); + void plotPixelData(wxAutoBufferedPaintDC& dc); DECLARE_EVENT_TABLE() }; diff --git a/fdmdv2/src/fdmdv2_process_audio.cpp b/fdmdv2/src/fdmdv2_process_audio.cpp deleted file mode 100644 index f6866446..00000000 --- a/fdmdv2/src/fdmdv2_process_audio.cpp +++ /dev/null @@ -1,487 +0,0 @@ -//========================================================================== -// Name: fdmdv2_process_audio.cpp -// Purpose: Implements processing of data received from audio interfaces. -// Created: August 12, 2012 -// Initial author: David Rowe -// Derived from: code trivially converted for integration with C++ code -// by Dave Witten -// License: -// -// Copyright (C) 2012 David Witten -// -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License version 2.1, -// as published by the Free Software Foundation. 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 Lesser General Public License -// along with this program; if not, see . -// -//========================================================================== -/* - - FUNCTION: per_frame_rx_processing() - AUTHOR..: David Rowe - DATE....: July 2012 - - Called every rx frame to take a buffer of input modem samples and - convert them to a buffer of output speech samples. - - The sample source could be a sound card or file. The sample source - supplies a fixed number of samples with each call. However - fdmdv_demod requires a variable number of samples for each call. - This function will buffer as appropriate and call fdmdv_demod with - the correct number of samples. - - The processing sequence is: - - collect demod input samples from sound card 1 A/D - while we have enough samples: - demod samples into bits - decode bits into speech samples - output a buffer of speech samples to sound card 2 D/A - - Note that sound card 1 and sound card 2 will have slightly different - sample rates, as their sample clocks are not syncronised. We - effectively lock the system to the demod A/D (sound card 1) sample - rate. This ensures the demod gets a continuous sequence of samples, - maintaining sync. Sample underflow or overflow will instead occur on - the sound card 2 D/A. This is acceptable as a buffer of lost or - extra speech samples is unlikely to be noticed. - - The situation is actually a little more complex than that. Through - the demod timing estimation the buffers supplied to sound card D/A 2 - are effectively clocked at the remote modulator sound card D/A clock - rate. We slip/gain buffers supplied to sound card 2 to compensate. - - The current demod handles varying clock rates by having a variable - number of input samples, e.g. 120 160 (nominal) or 200. However the - A/D always delivers a fixed number of samples. - - So we currently need some logic between the A/D and the demod: - + A/D delivers fixed number of samples - + demod processes a variable number of samples - + this means we run demod 0,1 or 2 times, depending - on number of buffered A/D samples - + demod always outputs 1 frame of bits - + so run demod and speech decoder 0, 1 or 2 times - - The ouput of the demod is codec voice data so it's OK if we miss or - repeat a frame every now and again. - -*/ -#include "fdmdv2_main.h" -#include "portaudio.h" - -//#include "fdmdv.h" -#include "codec2.h" - -//-- Globals: Eliminate!! -------------------------------- -//char *fin_name = NULL; -//char *fout_name = NULL; -//char *sound_dev_name = NULL; -//FILE *fin = NULL; -//FILE *fout = NULL; -struct FDMDV *fdmdv_state; -struct CODEC2 *codec2; -//float av_mag[FDMDV_NSPEC]; // shared between a few classes -extern float *av_mag; -//-------------------------------------------------------- - -void new_data(float *); -float Ts = 0.0; -short input_buf[2*FDMDV_NOM_SAMPLES_PER_FRAME]; -int n_input_buf = 0; -int nin = FDMDV_NOM_SAMPLES_PER_FRAME; -short *output_buf; -int n_output_buf = 0; -int codec_bits[2*FDMDV_BITS_PER_FRAME]; -int state = 0; - -// Portaudio states ----------------------------- - -PaStream *stream = NULL; -PaError err; - -typedef struct -{ - float in48k[FDMDV_OS_TAPS + N48]; - float in8k[MEM8 + N8]; -} paCallBackData; - -//---------------------------------------------------------------- -// per_frame_rx_processing() -//---------------------------------------------------------------- -void per_frame_rx_processing( - short output_buf[], /* output buf of decoded speech samples */ - int *n_output_buf, /* how many samples currently in output_buf[] */ - int codec_bits[], /* current frame of bits for decoder */ - short input_buf[], /* input buf of modem samples input to demod */ - int *n_input_buf, /* how many samples currently in input_buf[] */ - int *nin, /* amount of samples demod needs for next call */ - int *state, /* used to collect codec_bits[] halves */ - struct CODEC2 *c2 /* Codec 2 states */ - ) -{ - struct FDMDV_STATS stats; - int sync_bit; - float rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME]; - int rx_bits[FDMDV_BITS_PER_FRAME]; - unsigned char packed_bits[BYTES_PER_CODEC_FRAME]; - float rx_spec[FDMDV_NSPEC]; - int i; - int nin_prev; - int bit; - int byte; - int next_state; - - assert(*n_input_buf <= (2 * FDMDV_NOM_SAMPLES_PER_FRAME)); - - /* - This while loop will run the demod 0, 1 (nominal) or 2 times: - - 0: when tx sample clock runs faster than rx, occasionally we - will run out of samples - - 1: normal, run decoder once, every 2nd frame output a frame of - speech samples to D/A - - 2: when tx sample clock runs slower than rx, occasionally we will - have enough samples to run demod twice. - - With a +/- 10 Hz sample clock difference at FS=8000Hz (+/- 1250 - ppm), case 0 or 1 occured about once every 30 seconds. This is - no problem for the decoded audio. - */ - while(*n_input_buf >= *nin) - { - // demod per frame processing - for(i = 0; i < *nin; i++) - { - rx_fdm[i] = (float)input_buf[i]/FDMDV_SCALE; - } - nin_prev = *nin; - fdmdv_demod(fdmdv_state, rx_bits, &sync_bit, rx_fdm, nin); - *n_input_buf -= nin_prev; - assert(*n_input_buf >= 0); - - // shift input buffer - for(i=0; i<*n_input_buf; i++) - { - input_buf[i] = input_buf[i+nin_prev]; - } - - // compute rx spectrum & get demod stats, and update GUI plot data - fdmdv_get_rx_spectrum(fdmdv_state, rx_spec, rx_fdm, nin_prev); - fdmdv_get_demod_stats(fdmdv_state, &stats); - new_data(rx_spec); -// aScatter->add_new_samples(stats.rx_symbols); -// aTimingEst->add_new_sample(stats.rx_timing); -// aFreqEst->add_new_sample(stats.foff); -// aSNR->add_new_sample(stats.snr_est); - /* - State machine to: - - + Mute decoded audio when out of sync. The demod is synced - when we are using the fine freq estimate and SNR is above - a thresh. - - + Decode codec bits only if we have a 0,1 sync bit - sequence. Collects two frames of demod bits to decode - one frame of codec bits. - */ - next_state = *state; - switch(*state) - { - case 0: - // mute output audio when out of sync - if(*n_output_buf < 2 * codec2_samples_per_frame(c2) - N8) - { - for(i=0; i 3.0)) - { - next_state = 1; - } - break; - - case 1: - if(sync_bit == 0) - { - next_state = 2; - // first half of frame of codec bits - memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME * sizeof(int)); - } - else - { - next_state = 1; - } - if(stats.fest_coarse_fine == 0) - { - next_state = 0; - } - break; - - case 2: - next_state = 1; - if(stats.fest_coarse_fine == 0) - { - next_state = 0; - } - if(sync_bit == 1) - { - // second half of frame of codec bits - memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int)); - // pack bits, MSB received first - bit = 7; - byte = 0; - memset(packed_bits, 0, BYTES_PER_CODEC_FRAME); - for(i = 0; i < BITS_PER_CODEC_FRAME; i++) - { - packed_bits[byte] |= (codec_bits[i] << bit); - bit--; - if(bit < 0) - { - bit = 7; - byte++; - } - } - assert(byte == BYTES_PER_CODEC_FRAME); - // add decoded speech to end of output buffer - if(*n_output_buf <= codec2_samples_per_frame(c2)) - { - codec2_decode(c2, &output_buf[*n_output_buf], packed_bits); - *n_output_buf += codec2_samples_per_frame(c2); - } - assert(*n_output_buf <= (2 * codec2_samples_per_frame(c2))); - } - break; - } - *state = next_state; - } -} - -//---------------------------------------------------------------- -// update average of each spectrum point -//---------------------------------------------------------------- -void new_data(float mag_dB[]) -{ - int i; - - for(i = 0; i < FDMDV_NSPEC; i++) - { - av_mag[i] = (1.0 - BETA) * av_mag[i] + BETA * mag_dB[i]; - } -} - -//---------------------------------------------------------------- -// Redraw windows every DT seconds. -//---------------------------------------------------------------- -void update_gui(int nin, float *Ts) -{ - *Ts += (float)nin/FS; - *Ts += (float)nin/FS; - if(*Ts >= DT) - { -/* - *Ts -= DT; - if(!zoomSpectrumWindow->shown() && !zoomWaterfallWindow->shown()) - { - aSpectrum->redraw(); - aWaterfall->redraw(); - aScatter->redraw(); - aTimingEst->redraw(); - aFreqEst->redraw(); - aSNR->redraw(); - } - if(zoomSpectrumWindow->shown()) - { - aZoomedSpectrum->redraw(); - } - if(zoomWaterfallWindow->shown()) - { - aZoomedWaterfall->redraw(); - } -*/ - } -} - -//---------------------------------------------------------------- -// idle() is the FLTK function that gets continusouly called when FLTK -// is not doing GUI work. We use this function for providing file -// input to update the GUI when simulating real time operation. -//---------------------------------------------------------------- -/* -void idle(void*) -{ - int ret, i; - if(fin_name != NULL) - { - ret = fread(&input_buf[n_input_buf], sizeof(short), FDMDV_NOM_SAMPLES_PER_FRAME, fin); - n_input_buf += FDMDV_NOM_SAMPLES_PER_FRAME; - per_frame_rx_processing(output_buf, &n_output_buf, codec_bits, input_buf, &n_input_buf, &nin, &state, codec2); - if(fout_name != NULL) - { - if(n_output_buf >= N8) - { - ret = fwrite(output_buf, sizeof(short), N8, fout); - n_output_buf -= N8; - assert(n_output_buf >= 0); - // shift speech sample output buffer - for(i=0; iin8k; - float *in48k = cbData->in48k; - float out8k[N8]; - float out48k[N48]; - short out48k_short[N48]; - - (void) timeInfo; - (void) statusFlags; - - assert(inputBuffer != NULL); - // Convert input model samples from 48 to 8 kHz - // just use left channel - for(i = 0; i < framesPerBuffer; i++, rptr += 2) - { - in48k[i + FDMDV_OS_TAPS] = *rptr; - } - // downsample and update filter memory - fdmdv_48_to_8(out8k, &in48k[FDMDV_OS_TAPS], N8); - for(i = 0; i < FDMDV_OS_TAPS; i++) - { - in48k[i] = in48k[i+framesPerBuffer]; - } - // run demod, decoder and update GUI info - for(i=0; i= N8) - { - if(state == 0) - { - for(i=0; i= 0); - // shift speech samples in output buffer - for(i = 0; i < (unsigned int)n_output_buf; i++) - { - output_buf[i] = output_buf[i+N8]; - } - // Convert output speech to 48 kHz sample rate - // upsample and update filter memory - fdmdv_8_to_48(out48k, &in8k[MEM8], N8); - for(i = 0; i < MEM8; i++) - { - in8k[i] = in8k[i+N8]; - } - assert(outputBuffer != NULL); - // write signal to both channels - for(i = 0; i < N48; i++) - { - out48k_short[i] = (short)out48k[i]; - } - for(i=0; i= argc) - { - return 0; - } - fin_name = argv[i+1]; - i += 2; - return 2; - } - if(argv[i][1] == 'o') - { - if((i+1) >= argc) - { - return 0; - } - fout_name = argv[i+1]; - i += 2; - return 2; - } - if(argv[i][1] == 's') - { - if((i+1) >= argc) - { - return 0; - } - sound_dev_name = argv[i+1]; - i += 2; - return 2; - } - return 0; -} -*/ -- 2.25.1