// ----------------------------------------------------------------------------
// wefax.cxx  --  Weather Fax modem
//
// Copyright (C) 2010
//		Remi Chateauneu, F4ECW
//
// This file is part of fldigi.  Adapted from code contained in HAMFAX source code 
// distribution.
//  Hamfax Copyright (C) Christof Schmitt
//
// fldigi 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.
//
// fldigi 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 fldigi; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// ----------------------------------------------------------------------------

#include <config.h>

#include <cstdlib>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <valarray>
#include <cmath>
#include <cstddef>
#include <queue>
#include <map>
#include <libgen.h>

#include "debug.h"
#include "gettext.h"
#include "wefax.h"
#include "modem.h"
#include "main.h"

#include "misc.h"
#include "fl_digi.h"
#include "configuration.h"
#include "status.h"
#include "filters.h"

#include "wefax-pic.h"

#include "ascii.h"

#include "qrunner.h"

using namespace std;

//=============================================================================
//
//=============================================================================

#define PUT_STATUS( A )                             \
{                                                   \
	std::stringstream strm_status ;              \
	strm_status << A ;                           \
	put_status( strm_status.str().c_str() );     \
	LOG_DEBUG("%s", strm_status.str().c_str() ); \
}

//=============================================================================
// Core of the code taken from Hamfax.
//=============================================================================

/// No reasonable FIR filter should have more coefficients than that.
#define MAX_FILT_SIZE 256

struct fir_coeffs
{
	const char * _name ;
	int          _size ;
	const double _coefs[MAX_FILT_SIZE];
};

// Narrow, middle and wide fir low pass filter from ACfax
static const fir_coeffs input_filters[] = {
	{ "Narrow", 17,
		{ -7,-18,-15, 11, 56,116,177,223,240,223,177,116, 56, 11,-15,-18, -7}
	},
	{ "Middle", 17,
		{  0,-18,-38,-39,  0, 83,191,284,320,284,191, 83,  0,-39,-38,-18,  0}
	},
	{ "Wide",   17,
		{  6, 20,  7,-42,-74,-12,159,353,440,353,159,-12,-74,-42,  7, 20,  6}
	},
	{ "Blackman", 17,
		{
			-2.7756e-15,
			2.9258e+00,
			1.3289e+01,
			3.4418e+01,
			6.8000e+01,
			1.1095e+02,
			1.5471e+02,
			1.8770e+02,
			2.0000e+02,
			1.8770e+02,
			1.5471e+02,
			1.1095e+02,
			6.8000e+01,
			3.4418e+01,
			1.3289e+01,
			2.9258e+00,
			-2.7756e-15
		}
	},
	{ "Hanning", 17,
		{
     			0.00000,
     			7.61205,
    			29.28932,
    			61.73166,
   			100.00000,
   			138.26834,
   			170.71068,
   			192.38795,
   			200.00000,
   			192.38795,
   			170.71068,
   			138.26834,
   			100.00000,
    			61.73166,
    			29.28932,
     			7.61205,
     			0.00000
		}
	},
	{ "Hamming", 17,
		{
			16.000,
			23.003,
			42.946,
			72.793,
			108.000,
			143.207,
			173.054,
			192.997,
			200.000,
			192.997,
			173.054,
			143.207,
			108.000,
			72.793,
			42.946,
			23.003,
			16.000
		}
	}
};
static const size_t nb_filters = sizeof(input_filters)/sizeof(input_filters[0]); ;

/// This contains all possible reception filters.
class fir_filter_pair_set : public std::vector< C_FIR_filter >
{
	/// This, because C_FIR_filter cannot be copied.
	fir_filter_pair_set(const fir_filter_pair_set &);
	fir_filter_pair_set & operator=(const fir_filter_pair_set &);
public:
	static const char ** filters_list(void)
	{
		/// There will be a small memory leak when leaving. No problem at all.
		static const char ** stt_filters = NULL ;
		if( stt_filters == NULL ) {
			stt_filters = new const char * [nb_filters + 1];
			for( size_t ix_filt = 0 ; ix_filt < nb_filters ; ++ix_filt ) {
				stt_filters[ix_filt] = input_filters[ix_filt]._name ;
			}
			stt_filters[nb_filters] = NULL ;
		}
		return stt_filters ;
	}

	fir_filter_pair_set()
	{
		/// Beware that C_FIR_filter cannot be copied with its content.
		resize( nb_filters );
		for( size_t ix_filt = 0 ; ix_filt < nb_filters ; ++ix_filt )
		{
			// Same filter for real and imaginary.
			const fir_coeffs * ptr_filt = input_filters + ix_filt ;
			// init() should take const double pointers.
			operator[]( ix_filt ).init( ptr_filt->_size, 1,
					const_cast< double * >( ptr_filt->_coefs ),
					const_cast< double * >( ptr_filt->_coefs ) );
		}
	}
}; // fir_filter_pair_set

/// Speed-up of trigonometric calculations.
template <class T> class lookup_table {
	T    * m_table;
	size_t m_table_size;
	size_t m_next;
	size_t m_increment;

	/// No default constructor because it would invalidate the buffer pointer.
	lookup_table();
	lookup_table(const lookup_table &);
	lookup_table & operator=(const lookup_table &);
public:
	lookup_table(const size_t N)
	: m_table_size(N), m_next(0), m_increment(0)
	{
		assert( N != 0 );

		/// If no more memory, an exception will be thrown.
		m_table=new T[m_table_size];
	}

	~lookup_table(void)
	{
		delete[] m_table;
	}

	T& operator[](size_t i)
	{
		return m_table[i];
	}

	void set_increment(size_t i)
	{
		m_increment=i;
	}

	T next_value(void)
	{
		m_next+=m_increment;
		if(m_next>=m_table_size) {
			m_next%=m_table_size;
		}
		return m_table[m_next];
	}

	size_t size(void) const
	{
		return m_table_size;
	}

	void reset(void)
	{
		m_next=0;
	}
}; // lookup_table

typedef enum {
	RXAPTSTART,
	RXAPTSTOP,
	RXPHASING,
	RXIMAGE,
	TXAPTSTART,
	TXPHASING,
	ENDPHASING,
	TXIMAGE,
	TXAPTSTOP,
	IDLE } fax_state;

static const char * state_to_str(fax_state a_state)
{
	switch( a_state )
	{
		case RXAPTSTART : return "APT reception start" ;
		case RXAPTSTOP  : return "APT reception stop" ;
		case RXPHASING  : return "Phasing reception" ;
		case RXIMAGE    : return "Receiving" ;
		case TXAPTSTART : return "APT transmission start" ;
		case TXAPTSTOP  : return "APT stop" ;
		case TXPHASING  : return "Phasing transmission" ;
		case ENDPHASING : return "End phasing" ;
		case TXIMAGE    : return "Sending image" ;
		case IDLE       : return "Idle" ;
	}
	return "UNKNOWN" ;
};

/// TODO: This should be hidden to this class.
static const int bytes_per_pixel = 3;

/// This should match the default value of wefax_pic::normalize_lpm.
#define LPM_DEFAULT 120

#define IOC_576 576
#define IOC_288 288

/// Index of correlation to image width.
static int ioc_to_width( int ioc )
{
	return ioc * M_PI ;
};

/// Used by bandwidth.
static const int fm_deviation = 400 ;

class fax_implementation {
	wefax * m_ptr_wefax ;  // Points to the modem of which this is the implementation.
	fax_state m_rx_state ; // RXPHASING, RXIMAGE etc...
	int m_sample_rate;     // Set at startup: 8000, 11025 etc...
	int m_current_value;   // Latest received pixel value.
	bool m_apt_high;
	int m_apt_trans;       // APT low-high transitions
	int m_apt_count;       // samples counted for m_apt_trans
	int m_apt_start_freq;  // Standard APT frequency. Set at startup.
	int m_apt_stop_freq;   // Standard APT frequency.
	bool m_phase_high;     // When state=RXPHASING
	int m_curr_phase_len;
	int m_curr_phase_high;
	int m_phase_lines;
	int m_num_phase_lines;
	int m_phasing_calls_nb;// Number of calls to decode_phasing for the current image.
	bool m_phase_inverted; // Default is false.
	double m_lpm_img;      // Lines per minute.
	double m_lpm_sum_rx;   // Sum of the latest LPM values, when RXPHASING.
	int m_img_width;       // Calculated with IOC=576 or 288.
	int m_img_sample;      // Current received samples number when in RXIMAGE.
	int m_last_col;        // Current col based on samples number, decides to set a pixel when changes.
	int m_pixel_val;       // Accumulates received samples, then averaged to set a pixel.
	int m_pix_samples_nb;
	bool m_img_color;      // Whether this is a color image or not.
	fax_state m_tx_state;  // Modem state when transmitting.
	int m_tx_phasing_lin;  // Nb of phasing lines sent when transmitting an image.
	int m_start_duration;  // Number of seconds for sending ATP start.
	int m_stop_duration;   // Number of seconds for sending APT stop.
	int m_img_tx_cols;     // Number of columns when transmitting.
	int m_img_tx_rows;     // Number of rows when transmitting.
	int m_carrier;         // Normalised fax carrier frequency.
	int m_fax_pix_num;     // Index of current pixel in received image.
	int m_xmt_bytes ;      // Total number of bytes to send.
	const unsigned char * m_xmt_pic_buf ; // Bytes to send. Size is m_xmt_bytes.
	bool m_freq_mod ;      // Frequency modulation or AM.
	int m_max_fax_rows ;   // Max admissible number of received lines.
	bool m_manual_mode ;   // Tells whether everything is read, or apt+phasing detection.

	/// The number of samples sent for one line. The LPM is given by the GUI. Typically 5512.
	double m_smpl_per_lin ;// Recalculated each time m_lpm_img is updated.

	int m_ix_filt ;        // Index of the current reception filter.

	/// This is always the same filter for all objects.
	static fir_filter_pair_set m_rx_filters ;

	/// These are used for transmission.
	lookup_table<double> m_dbl_sine;
	lookup_table<double> m_dbl_cosine;
	lookup_table<double> m_dbl_arc_sine;

	/// Stores a result based on the previous received sample.
	double m_i_fir_old;
	double m_q_fir_old;

	void decode(const int* buf, int nb_samples);

	/// Used for transmission.
	lookup_table<short> m_short_sine;

	fax_implementation();

	/// Needed when starting image reception, after phasing.
	void reset_counters(void)
	{
		m_last_col       = 0 ;
		m_fax_pix_num    = 0 ;
		m_img_sample     = 0;
		m_pix_samples_nb = 0;
	}

	/// Called at init time or when the carrier changes.
	void reset_increments(void)
	{
		/// This might happen at startup. Not a problem.
		if(m_sample_rate == 0) {
			return;
		}
		m_dbl_sine.set_increment(m_dbl_sine.size()*m_carrier/m_sample_rate);
		m_dbl_cosine.set_increment(m_dbl_cosine.size()*m_carrier/m_sample_rate);
		m_dbl_sine.reset();
		m_dbl_cosine.reset();

		m_short_sine.reset();
		if(!m_freq_mod ) {
			m_short_sine.set_increment(m_short_sine.size()*m_carrier/m_sample_rate);
		}
	}

public:
	fax_implementation(int fax_mode, wefax * ptr_wefax );
	void init_rx(int the_smpl_rate);
	void skip_apt_rx(void);
	void skip_phasing_to_image(bool auto_center);
	void skip_phasing_rx(bool auto_center);
	void end_rx(void);
	void rx_new_samples(const double* audio_ptr, int audio_sz);
        void init_tx(int the_smpl_rate);
	void modulate(const double* buffer, int n);
	void tx_params_set(
		int the_lpm,
		const unsigned char * xmtpic_buffer,
		bool is_color,
		int img_w,
		int img_h,
		int xmt_bytes );
	bool trx_do_next(void);
	void tx_apt_stop(void);

	double carrier(void) const
	{
		return m_carrier ;
	}

	/// The trigo tables have an increment which depends on the carrier frequency.
	void set_carrier(double freq)
	{
		m_carrier = freq ;
		reset_increments();
	}

	int fax_width(void) const
	{
		return m_img_width ;
	}

	void set_filter_rx( int idx_filter )
	{
		m_ix_filt = idx_filter ;
	}

	/// When set, starts receiving faxes without interruption other than manual.
	void manual_mode_set( bool manual_flag )
	{
		m_manual_mode = manual_flag ;
	}

	/// Called by the GUI.
	bool manual_mode_get(void) const
	{
		return m_manual_mode ;
	}

	/// Called by the GUI.
	void max_lines_set( int max_lines )
	{
		m_max_fax_rows = max_lines ;
	}

	/// Called by the GUI.
	int max_lines_get(void) const
	{
		return m_max_fax_rows ;
	}

	int lpm_to_samples( int the_lpm ) const {
		return m_sample_rate * 60.0 / the_lpm ;
	}

	/// Called by the GUI.
	void lpm_set( int the_lpm )
	{
		m_lpm_img = the_lpm ;
		m_smpl_per_lin = lpm_to_samples( m_lpm_img );
	}

	/// This generates a filename based on the frequency, current time, internal state etc...
	std::string generate_filename( const char *extra_msg ) const ;

	std::string state_rx_str(void) const
	{
		return state_to_str(m_rx_state);
	}

	/// Returns a string telling the state. Informational purpose.
	std::string state_string(void) const
	{
		std::stringstream result ;
		result	<< "tx:" << state_to_str(m_tx_state)
			<< " "
			<< "rx:" << state_to_str(m_rx_state);
		return result.str();
	};

private:
	/// Centered around the frequency.
	double power_usb_noise(void) const
	{
		static double avg_pwr = 0.0 ;
       		double pwr = wf->powerDensity(m_carrier, 2 * fm_deviation) + 1e-10;

       		return decayavg( avg_pwr, pwr, 10 );
	}

	/// This evaluates the power signal when APT start frequency. This frequency pattern
	/// can be observed on the waterfall.
	double power_usb_apt_start(void) const
	{
		static double avg_pwr = 0.0 ;
		/// Value approximated by watching the waterfall.
		static const int bandwidth_apt_start = 10 ;
       		double pwr
			= wf->powerDensity(m_carrier - 2 * m_apt_start_freq, bandwidth_apt_start)
			+ wf->powerDensity(m_carrier -     m_apt_start_freq, bandwidth_apt_start)
			+ wf->powerDensity(m_carrier                       , bandwidth_apt_start)
			+ wf->powerDensity(m_carrier +     m_apt_start_freq, bandwidth_apt_start);
			+ wf->powerDensity(m_carrier + 2 * m_apt_start_freq, bandwidth_apt_start);

       		return decayavg( avg_pwr, pwr, 10 );
	}

	/// Estimates the signal power when the phasing signal is received.
	double power_usb_phasing(void) const
	{
		static double avg_pwr = 0.0 ;
		/// Rough estimate based on waterfall observation.
		static const int bandwidth_phasing = 1 ;
       		double pwr = wf->powerDensity(m_carrier - fm_deviation, bandwidth_phasing);

       		return decayavg( avg_pwr, pwr, 10 );
	}

	/// There is some power at m_carrier + fm_deviation but this is neglictible.
	double power_usb_image(void) const
	{
		static double avg_pwr = 0.0 ;
		/// This value is obtained by watching the waterfall.
		static const int bandwidth_image = 100 ;
       		double pwr = wf->powerDensity(m_carrier + fm_deviation, bandwidth_image);

       		return decayavg( avg_pwr, pwr, 10 );
	}

	/// Hambourg Radio sends a constant freq which looks like an image.
	/// We eliminate it by comparing with a black signal.
	/// If this is a real image, bith powers will be close.
	/// If not, the image is very important, but not the black.
	double power_usb_black(void) const
	{
		static double avg_pwr = 0.0 ;
		/// This value is obtained by watching the waterfall.
		static const int bandwidth_black = 20 ;
       		double pwr = wf->powerDensity(m_carrier + fm_deviation, bandwidth_black);

       		return decayavg( avg_pwr, pwr, 10 );
	}

	/// Evaluates the signal power for APT stop frequency.
	double power_usb_apt_stop(void) const
	{
		static double avg_pwr = 0.0 ;
		/// This value is obtained by watching the waterfall.
		static const int bandwidth_apt_stop = 50 ;
       		double pwr = wf->powerDensity(m_carrier - m_apt_stop_freq, bandwidth_apt_stop);

       		return decayavg( avg_pwr, pwr, 10 );
	}

	/// This evaluates the signal/noise ratio for some specific bandwidths.
	class fax_signal {
		const fax_implementation * _ptr_fax ;
		int                        _cnt ; /// The value can be reused a couple of times.

		double                     _apt_start ;
		double                     _phasing ;
		double                     _image ;
		double                     _black ;
		double                     _apt_stop ;

		fax_state                  _state ; /// Deduction made based on signal power.
		const char *               _text;

		/// Finer tests can be added. These are all rule-of-thumb values based
		/// on observation of real signals.
		/// TODO: Adds a learning mode using the Hamfax detector to train the signal-based one.
		void set_state(void)
		{
			_state = IDLE ;
			_text = "";

			if( 	( _apt_start   > 20.0 ) &&
				( _phasing     < 10.0 ) &&
				( _image       < 10.0 ) &&
				( _apt_stop    < 10.0 ) ) {
				_state = RXAPTSTART ;
				_text = _("Strong APT start signal: Skip to phasing.");
			}
			if(     /// The image signal may be weak if the others signals are even weaker.
			(	( _apt_start   <  1.0 ) &&
				( _phasing     <  1.0 ) &&
				( _image       > 10.0 ) &&
				( _apt_stop    <  1.0 ) ) ||
			(	( _apt_start   <  1.0 ) &&
				( _phasing     <  0.5 ) &&
				( _image       >  8.0 ) &&
				( _apt_stop    <  0.5 ) ) ||
			(	( _apt_start   <  3.0 ) &&
				( _phasing     <  1.0 ) &&
				( _image       > 15.0 ) &&
				( _apt_stop    <  1.0 ) ) ) {
				_state = RXIMAGE ;
				_text = _("Strong image signal when getting APT start: Starting phasing.");
			}
			if( 	( _apt_start   < 10.0 ) &&
				( _phasing     > 20.0 ) &&
				( _image       < 10.0 ) &&
				( _apt_stop    < 10.0 ) ) {
				_state = RXPHASING ;
				_text = _("Strong phasing signal when getting APT start: Starting phasing.");
			}
			if(
			(	( _apt_start   <  2.0 ) &&
				( _phasing     <  2.0 ) &&
				( _image       <  2.0 ) &&
				( _apt_stop    >  8.0 ) ) ||
			(	( _apt_start   <  1.0 ) &&
				( _phasing     <  1.0 ) &&
				( _image       <  1.0 ) &&
				( _apt_stop    >  6.0 ) ) ) {
				_state = RXAPTSTOP ;
				_text = _("Strong APT stop signal: Stopping reception.");
			}

			/// Maybe this is a constant freq. If so, the power is very big
			/// for the image, but the center, which should be important,
			/// is very weak: This is a constant freq (Hambourg).
			if( _image > 5 * _black ) {
				_state = IDLE ;
			}
			if( _image > 100 * _black ) {
				_state = RXAPTSTOP ;
				_text = _("Constant frequency detected: Stopping reception.");
			}

			/// Consecutive lines in a wefax image have a strong statistical correlation.
			fax_state state_corr = _ptr_fax->correlation_state();
			if( ( _state == RXIMAGE ) || ( _state == IDLE ) ) {
				if( state_corr == RXAPTSTOP ) {
					_state = RXAPTSTOP ;
					_text = _("No significant line-to-line correlation: Stopping reception.");
				}
			}

			if( ( _state == IDLE ) || ( _state == RXAPTSTART ) || ( _state == RXPHASING ) ) {
				if( state_corr == RXIMAGE ) {
					_state = RXIMAGE ;
					_text = _("Significant line-to-line correlation: Starting reception.");
				}
			}
		}

		void recalc(void)
		{
			/// Adds a small value to avoid division by zero.
			double noise = _ptr_fax->power_usb_noise() + 1e-10 ;

			/// Multiplications are faster than divisions.
			double inv_noise = 1.0 / noise ;

			_apt_start = _ptr_fax->power_usb_apt_start() * inv_noise ;
			_phasing   = _ptr_fax->power_usb_phasing()   * inv_noise ;
			_image     = _ptr_fax->power_usb_image()     * inv_noise ;
			_black     = _ptr_fax->power_usb_black()     * inv_noise ;
			_apt_stop  = _ptr_fax->power_usb_apt_stop()  * inv_noise ;

			set_state();
		}

		public:
		/// This recomputes, if needed, the various signal power ratios.
		void refresh(void)
		{
			/// Values reevaluated every X input samples. Must be smaller
			/// than the typical audio input frames (512 samples).
			static const int validity = 1000 ;

			/// Calculated at least the first time.
			if( ( _cnt % validity ) == 0 ) {
				recalc();
			}
			/// Does not matter if wrapped beyond 2**31.
			++_cnt ;
		}

		fax_signal( const fax_implementation * ptr_fax)
			: _ptr_fax(ptr_fax), _cnt(0) {}

		fax_state signal_state(void) const { return _state ; }
		const char * signal_text(void) const { return _text; };

		double image_noise_ratio(void) const { return _image ; }

		/// This updates a Fl_Chart widget.
		void display(void) const
		{
			// Protected with REQ, otherwise will segfault. Do not use REQ_SYNC
			// otherwise it hangs when switching to another mode with a macro.
			REQ( wefax_pic::power,
				_apt_start,
				_phasing,
				_image,
				_black,
				_apt_stop );
		}

		/// For debugging only.
		friend std::ostream & operator<<( std::ostream & refO, const fax_signal & ref_sig )
		{
			refO
				<< " start=" << std::setw(10) << ref_sig._apt_start
				<< " phasing=" << std::setw(10) << ref_sig._phasing
				<< " image=" << std::setw(10) << ref_sig._image
				<< " black=" << std::setw(10) << ref_sig._black
				<< " stop=" << std::setw(10) << ref_sig._apt_stop
				<< " pwr_state=" << state_to_str(ref_sig._state);
			return refO ;
		}

		std::string to_string(void) const
		{
			std::stringstream tmp_strm ;
			tmp_strm << *this ;
			return tmp_strm.str();
		}
	};

	/// We tolerate a small difference between the frequencies.
	bool is_near_freq( int f1, int f2, int margin ) const
	{
		int delta = f1 - f2 ;
		if( delta < 0 )
		{
			delta = -delta ;
		}
		if( delta < margin ) {
			return true ;
		} else {
			return false ;
		}
	}

	void save_automatic(const char * extra_msg);

	void decode_apt(int x, const fax_signal & the_signal );
	void decode_phasing(int x, const fax_signal & the_signal );
	bool decode_image(int x);

private:
	/// Each received file has its name pushed in this queue.
	syncobj m_sync_rx ;
	std::queue< std::string > m_received_files ;
public:
	/// Called by the engine each time a file is saved.
	void put_received_file( const std::string &filnam )
	{
		guard_lock g( m_sync_rx.mtxp() );
		LOG_INFO("%s", filnam.c_str() );
		m_received_files.push( filnam );
		m_sync_rx.signal();
	}

	/// Returns a received file name, by chronological order.
	std::string get_received_file( double max_seconds )
	{
		guard_lock g( m_sync_rx.mtxp() );

		LOG_INFO(_("delay=%f"), max_seconds );
		if( m_received_files.empty() )
		{
			if( ! m_sync_rx.wait(max_seconds) ) return std::string();
		}
		std::string filnam = m_received_files.front();
		m_received_files.pop();
		return filnam ;
	}
private:
	/// No unicity if the same filename is sent by several clients at the same time.
	/// This does not really matter because the error codes should be the same.
	syncobj m_sync_tx_fil ;
	std::string m_tx_fil ; // Filename being transmitted.

	syncobj m_sync_tx_msg ;
	typedef std::map< std::string, std::string > sent_files_type ;
	sent_files_type m_sent_files ;
public:
	/// If the delay is exceeded, returns with an error message.
	std::string send_file( const std::string & filnam, double max_seconds )
	{
		LOG_INFO("%s", filnam.c_str() );
		bool is_acquired = transmit_lock_acquire(filnam, max_seconds);
		if( ! is_acquired ) return "Timeout sending " + filnam ;

		REQ( wefax_pic::send_image, filnam );
		
		guard_lock g( m_sync_tx_fil.mtxp() );

		sent_files_type::iterator itFi ;
		while(true)
		{
			itFi = m_sent_files.find( filnam );
			if( itFi != m_sent_files.end() )
			{
				break ;
			}
			if( ! m_sync_tx_msg.wait( max_seconds ) )
			{
				return "Timeout";
			}
		}
		std::string err_msg = itFi->second ;
		m_sent_files.erase( itFi );
		return err_msg ;
	}

	/// Called when loading a file from the GUI, or indirectly from XML-RPC.
	bool transmit_lock_acquire(const std::string & filnam, double delay = wefax::max_delay )
	{
		LOG_INFO("%s", filnam.c_str() );
		guard_lock g( m_sync_tx_fil.mtxp() );
		if ( ! m_tx_fil.empty() )
		{
			if( ! m_sync_tx_fil.wait( delay ) ) return false ;
		}
		m_tx_fil = filnam ;
		return true ;
	}

	/// Allows to send another file. Called by XML-RPC and the GUI.
	void transmit_lock_release( const std::string & err_msg )
	{
		guard_lock g( m_sync_tx_msg.mtxp() );
		LOG_INFO("%s %s", m_tx_fil.c_str(), err_msg.c_str() );
		if( m_tx_fil.empty() )
		{
			LOG_WARN(_("%s: File name should not be empty"), err_msg.c_str() );
		}
		else
		{
			m_sent_files[ m_tx_fil ] = err_msg ;
			m_tx_fil.clear();
		}
		m_sync_tx_msg.signal();
	}

	/// Maybe we could reset this buffer when we change the state so that we could
	/// use the current LPM width.
	mutable std::vector<unsigned char> m_correlation_buffer ;
	mutable double m_current_corr ;
	mutable double m_curr_corr_avg ;
	mutable size_t m_corr_calls_nb;
	static const size_t m_min_corr_lines = 20 ;

	/// Absolute value of statistical correlation between the current line and the previous one.
	/// Called once per maximum sample line.
	void correlation_calc(void) const {
		++m_corr_calls_nb;
		/// We should in fact take the smallest one, 60.
		size_t corr_samples_line = lpm_to_samples( LPM_DEFAULT );
		size_t corr_samples_line_plus_img_sample = corr_samples_line + m_img_sample ;
		size_t corr_buff_sz = 2 * corr_samples_line ;

		int avg_pred = 0, avg_curr = 0 ;
		for( size_t i = m_img_sample ; i < corr_samples_line_plus_img_sample ; ++i ) {
			int pix_pred = m_correlation_buffer[ ( i                     ) % corr_buff_sz ];
			int pix_curr = m_correlation_buffer[ ( i + corr_samples_line ) % corr_buff_sz ];
			avg_pred += pix_pred;
			avg_curr += pix_curr ;
		}
		avg_pred /= corr_samples_line;
		avg_curr /= corr_samples_line;

		/// Use integers because it is faster. Samples are chars, so no overflow possible.
		int numerator = 0, denom_pred = 0, denom_curr = 0 ;
		for( size_t i = m_img_sample ; i < corr_samples_line_plus_img_sample ; ++i ) {
			int pix_pred = m_correlation_buffer[ ( i                     ) % corr_buff_sz ];
			int pix_curr = m_correlation_buffer[ ( i + corr_samples_line ) % corr_buff_sz ];
			int delta_pred = pix_pred - avg_pred ;
			int delta_curr = pix_curr - avg_curr ;
			numerator += delta_pred * delta_curr ;
			denom_pred += delta_pred * delta_pred ;
			denom_curr += delta_curr * delta_curr ;
		}
		double denominator = sqrt( (double)denom_pred * (double)denom_curr );
		if( denominator == 0.0 ) {
			m_current_corr = 0.0 ;
		} else {
			m_current_corr = fabs( numerator / (double)denominator );
		}

		/// This never happened, but who knows (Inaccuracy etc...)?
		if( m_current_corr > 1.0 ) {
			LOG_WARN("Inconsistent correlation:%lf", m_current_corr );
			m_current_corr = 1.0;
		}

		if( m_corr_calls_nb < m_min_corr_lines ) {
			m_curr_corr_avg = m_current_corr ;
		} else {
			m_curr_corr_avg = ( m_curr_corr_avg * m_min_corr_lines + m_current_corr ) / ( m_min_corr_lines + 1 );
		}
		/// Debugging purpose only.
		if( ( m_corr_calls_nb % 10000 ) == 0 ) {
			LOG_INFO( "m_current_corr=%lf m_curr_corr_avg=%lf m_corr_calls_nb=%d",
				m_current_corr, m_curr_corr_avg, m_corr_calls_nb );
		}
		double metric = m_curr_corr_avg * 100.0 ;
		m_ptr_wefax->display_metric(metric);
	}

	/// This is called quite often. It estimates, based on the mobile
	/// average of statistical correlation between consecutive lines, whether this is a wefax
	/// signal or not.
	fax_state correlation_state(void) const {

		static fax_state stable_state = IDLE ;

		/// If the mobile abverage is not computed on enough lines, returns IDLE
		/// which means "Do not know" in this context.
		if( m_corr_calls_nb >= m_min_corr_lines ) {
			/// The threshold are very approximate.
			if( m_curr_corr_avg < 0.05 ) {
					stable_state = RXAPTSTOP ;
			} else if( m_curr_corr_avg > 0.20 ) {
					stable_state = RXIMAGE ;
			} else {
				stable_state = IDLE ;
			}
		}

		/// Message for first detection.
		if( (stable_state == IDLE) && (m_corr_calls_nb == m_min_corr_lines) ) {
			LOG_INFO("Correlation average %lf: Detected %s", m_curr_corr_avg, state_to_str(stable_state) );
		}

		return stable_state;
	}

	/// We compute about the same when plotting a pixel.
	void correlation_update( int the_sample ) {
		size_t corr_samples_line = lpm_to_samples( LPM_DEFAULT );
		size_t corr_buff_sz = 2 * corr_samples_line ;

		static size_t cnt_upd = 0 ;

		if( cnt_upd == 0 ) {
 			if( m_correlation_buffer.size() != corr_buff_sz ) {
				m_correlation_buffer.resize( corr_buff_sz, 0 );
				m_current_corr = 0.0 ;
				m_curr_corr_avg = 0.0 ;
				return ;
			}
		}

		if( ( cnt_upd % corr_samples_line ) == 0 ) {
			correlation_calc();
		}
		++cnt_upd ;

		m_correlation_buffer[ m_img_sample % corr_buff_sz ] = the_sample ;
	}
}; // class fax_implementation

/// Narrow, middle etc... input filters. Constructed at program startup. Readonly.
fir_filter_pair_set fax_implementation::m_rx_filters ;

// http://www.newearth.demon.co.uk/radio/hfwefax1.htm
// "...the total offset is 1900Hz, so to tune a fax station on 4610kHz, tune to 4608.1kHz USB."
static const double wefax_default_carrier = 1900;

fax_implementation::fax_implementation( int fax_mode, wefax * ptr_wefax  )
	: m_ptr_wefax( ptr_wefax )
	, m_dbl_sine(8192),m_dbl_cosine(8192),m_dbl_arc_sine(256)
	, m_short_sine(8192)
{
	m_freq_mod       = true;
	m_apt_stop_freq  = 450 ;
	m_phase_inverted = false ;
	m_img_color      = false ;
	m_tx_phasing_lin = 20 ;
	m_carrier        = wefax_default_carrier ;
	m_start_duration = 5 ;
	m_stop_duration  = 5 ;
	m_manual_mode    = false ;
	m_rx_state       = IDLE ;
	m_tx_state       = IDLE ;
	m_sample_rate    = 0 ;

	int index_of_correlation ;
	/// http://en.wikipedia.org/wiki/Radiofax
	switch( fax_mode )
	{
	case MODE_WEFAX_576:
		m_apt_start_freq     = 300 ;
		index_of_correlation = IOC_576 ;
		break;
	case MODE_WEFAX_288:
		m_apt_start_freq     = 675 ;
		index_of_correlation = IOC_288 ;
		break;
	default:
		LOG_ERROR(_("Invalid fax mode:%d"), fax_mode);
		abort();
	}

	m_img_width = ioc_to_width( index_of_correlation );

	for(size_t i=0; i<m_dbl_sine.size(); i++) {
		m_dbl_sine[i]=32768.0 * std::sin(2.0*M_PI*i/m_dbl_sine.size());
	}
	for(size_t i=0; i<m_dbl_cosine.size(); i++) {
		m_dbl_cosine[i]=32768.0 * std::cos(2.0*M_PI*i/m_dbl_cosine.size());
	}
	for(size_t i=0; i<m_dbl_arc_sine.size(); i++) {
		m_dbl_arc_sine[i]=std::asin(2.0*i/m_dbl_arc_sine.size()-1.0)/2.0/M_PI;
	}
	for(size_t i=0; i<m_short_sine.size(); i++) {
		m_short_sine[i]=static_cast<short>(32767*std::sin(2.0*M_PI*i/m_short_sine.size()));
	}
};

void fax_implementation::init_rx(int the_smpl_rat)
{
	m_sample_rate=the_smpl_rat;
	m_rx_state=RXAPTSTART;
	m_apt_count=m_apt_trans=0;
	m_apt_high=false;
	/// Centers the carriers on the GUI and reinits the trigonometric tables.
	m_ptr_wefax->set_freq(wefax_default_carrier);

	/// No weather fax can have such a huge number of rows.
	m_max_fax_rows = 5000 ;

	/// Default value, can be the with the GUI.
	m_ix_filt = 0 ; // 0=narrow, 1=middle, 2=wide.

	reset_increments();
	m_i_fir_old=m_q_fir_old=0;
	m_corr_calls_nb = 0;
}

/// Values are between 0 and 255.
void fax_implementation::decode(const int* buf, int nb_samples)
{
	if(nb_samples==0)
	{
		LOG_WARN(_("Empty buffer."));
		end_rx();
	}
	fax_signal my_signal(this);
	for(int i=0; i<nb_samples; i++) {
		int crr_val = buf[i];
		my_signal.refresh();
		m_current_value = crr_val;

		{
			static int n = 0 ;

			/// TODO: It could be displayed less often.
			if( 0 == ( n++ % 10000 ) ) {
				/// Must create a temp string otherwise the char pointer may be freed.
				std::string tmp_str = my_signal.to_string();
				LOG_VERBOSE( "%s state=%s", tmp_str.c_str(), state_to_str(m_rx_state) );
				my_signal.display();
			}
		}

		correlation_update(crr_val);

		if( m_manual_mode ) {
			m_rx_state = RXIMAGE;
			bool is_max_lines_reached = decode_image(crr_val);
			if( is_max_lines_reached ) {
				skip_apt_rx();
				skip_phasing_rx(false);
				LOG_INFO(_("Max lines reached in manual mode: Resuming reception."));
				if( m_manual_mode == false ) {
					LOG_ERROR(_("Inconsistent manual mode."));
				}
			}
		} else {
			decode_apt(crr_val,my_signal);
			if( m_rx_state==RXPHASING ) {
				decode_phasing(crr_val,my_signal);
			}
			if( ( m_rx_state==RXPHASING || m_rx_state==RXIMAGE ) && m_lpm_img > 0 ) {
				/// If the maximum number of lines is reached, we stop the reception.
				decode_image(crr_val);
			}
		}
		m_img_sample++;
	}
}

// The number of transitions between black and white is counted. After 1/2 
// second, the frequency is calculated. If it matches the APT start frequency,
// the state skips to the detection of phasing lines, if it matches the apt
// stop frequency two times, the reception is ended.
void fax_implementation::decode_apt(int x, const fax_signal & the_signal )
{
	if(x>229 && !m_apt_high) {
		m_apt_high=true;
		++m_apt_trans;
	} else if(x<25 && m_apt_high) {
		m_apt_high=false;
	}
	++m_apt_count ;
	if( m_apt_count >= m_sample_rate/2 ) {
		int curr_freq=m_sample_rate*m_apt_trans/m_apt_count;

		/// This writes the S/R level on the status bar.
		double tmp_snr = the_signal.image_noise_ratio();
		char snr_buffer[64];
        	snprintf(snr_buffer, sizeof(snr_buffer), "s/n %3.0f dB", 20.0 * log10(tmp_snr));
		put_Status1(snr_buffer);

		m_apt_count=m_apt_trans=0;

		if(m_rx_state==RXAPTSTART) {
			if( is_near_freq(curr_freq,m_apt_start_freq, 8 ) ) {
				skip_apt_rx();
				PUT_STATUS( state_rx_str() << ", " << _("frequency") << ": "
						<< curr_freq << " Hz. " << _("Skipping.") );
				return ;
			}
			if( is_near_freq(curr_freq,m_apt_stop_freq, 2 ) ) {
				LOG_INFO(_("Spurious APT stop frequency=%d Hz as waiting for APT start. SNR=%f"),
						curr_freq, tmp_snr );
				return ;
			}

			PUT_STATUS( state_rx_str() << ", " << _("frequency") << ": " << curr_freq << " Hz." );
		}

		if( is_near_freq(curr_freq,m_apt_stop_freq, 12 ) ) {
			PUT_STATUS( state_rx_str() << " " << _("Apt stop frequency") << ": "
					<< curr_freq << " Hz. " << _("Stopping.") );
			save_automatic("ok");
			end_rx();
			return ;
		}

		switch( the_signal.signal_state() ) {
		case RXAPTSTART :
			switch( m_rx_state ) {
				case RXAPTSTART :
					skip_apt_rx();
					LOG_INFO( the_signal.signal_text() );
					break ;
				default : break ;
			}
			break ;
		case RXPHASING :
			switch( m_rx_state ) {
				case RXAPTSTART :
					skip_apt_rx();
					LOG_INFO( the_signal.signal_text() );
					break ;
				default : break ;
			}
			break ;
		case RXIMAGE :
			switch( m_rx_state ) {
				case RXAPTSTART :
					skip_apt_rx();
					/// The phasing step will start receiving the image later. First we try
					/// to phase the image correctly.
					LOG_INFO( the_signal.signal_text() );
					break ;
				default : break ;
			}
			break ;
		case RXAPTSTOP :
			switch( m_rx_state ) {
				case RXIMAGE    :
					LOG_INFO( the_signal.signal_text() );
					save_automatic("stop");
					end_rx();
					break;
				default : break ;
			}
			break ;
		default : break ;
		}
	}
}

/// This generates a file name with the reception time and the frequency.
std::string fax_implementation::generate_filename( const char *extra_msg ) const
{
	time_t tmp_time = time(NULL);
	struct tm tmp_tm ;
	localtime_r( &tmp_time, &tmp_tm );

	char buf_fil_nam[256] ;
	long long tmp_fl = wf->rfcarrier() ;
	snprintf( buf_fil_nam, sizeof(buf_fil_nam),
		"wefax_%04d%02d%02d_%02d%02d%02d_%lld_%s.png",
		1900 + tmp_tm.tm_year,
		1 + tmp_tm.tm_mon,
		tmp_tm.tm_mday,
		tmp_tm.tm_hour,
		tmp_tm.tm_min,
		tmp_tm.tm_sec,
		tmp_fl,
		extra_msg );

	return buf_fil_nam ;
}

/// This saves an image and adds the right comments.
void fax_implementation::save_automatic(const char * extra_msg)
{
	/// Minimum pixel numbers for a valid image.
	static const int max_fax_pix_num = 200000 ;
	if( m_fax_pix_num < max_fax_pix_num )
	{
		LOG_INFO( _("Do not save small image (%d bytes). Manual=%d"), m_fax_pix_num, m_manual_mode );
		return ;
	}

	std::string new_filnam = generate_filename(extra_msg);

	LOG_INFO( "Saving %d bytes in %s.", m_fax_pix_num, new_filnam.c_str() );

	std::stringstream extra_comments ;
	extra_comments << "ControlMode:"   << ( m_manual_mode ? "Manual" : "APT control" ) << "\n" ;
	extra_comments << "LPM:"           << m_lpm_img << "\n" ;
	extra_comments << "FrequencyMode:" << ( m_freq_mod ? "FM" : "AM" ) << "\n" ;
	extra_comments << "Carrier:"       << m_carrier << "\n" ;
	extra_comments << "Inversion:"     << ( m_phase_inverted ? "Inverted" : "Normal" ) << "\n" ;
	extra_comments << "Color:"         << ( m_img_color ? "Color" : "BW" ) << "\n" ;
	extra_comments << "SampleRate:"    << m_sample_rate << "\n" ;
	wefax_pic::save_image( new_filnam, extra_comments.str() );
};

// Phasing lines consist of 2.5% white at the beginning, 95% black and again
// 2.5% white at the end (or inverted). In normal phasing lines we try to
// count the length between the white-black transitions. If the line has
// a reasonable amount of black (4.8%--5.2%) and the length fits in the 
// range of 60--360lpm (plus some tolerance) it is considered a valid
// phasing line. Then the start of a line and the lpm is calculated.
void fax_implementation::decode_phasing(int x, const fax_signal & the_signal )
{
	m_curr_phase_len++;
	++m_phasing_calls_nb ;
	if(x>128) {
		m_curr_phase_high++;
	}
	if((!m_phase_inverted && x>229 && !m_phase_high) ||
	   ( m_phase_inverted && x<25  && m_phase_high)) {
		m_phase_high=m_phase_inverted?false:true;
	} else if((!m_phase_inverted && x<25 && m_phase_high) ||
		  ( m_phase_inverted && x>229 && !m_phase_high)) {
		m_phase_high=m_phase_inverted?true:false;
		if(m_curr_phase_high>=(m_phase_inverted?0.948:0.048)*m_curr_phase_len &&
		   m_curr_phase_high<=(m_phase_inverted?0.952:0.052)*m_curr_phase_len &&
		   m_curr_phase_len <= 1.1  * m_sample_rate &&
		   m_curr_phase_len >= 0.15 * m_sample_rate) {
			double tmp_lpm=60.0*m_sample_rate/m_curr_phase_len;

			m_lpm_sum_rx += tmp_lpm;
			++m_phase_lines;

			PUT_STATUS( state_rx_str()
				<< ". " << _("Decoding phasing line") << ", lpm = " << tmp_lpm
				<< " " << _("count") << "=" << m_phase_lines );

			/// The precision cannot really increase because there cannot 
			// be more than a couple of loops. This is used for guessing
			// whether the LPM is around 120 or 60.
			lpm_set( m_lpm_sum_rx / m_phase_lines );

			double smpl_per_lin = m_smpl_per_lin;
			/// Half of the band of the phasing line.
			m_img_sample=static_cast<int>(1.025 * smpl_per_lin );

			double tmp_pos=std::fmod(m_img_sample,smpl_per_lin) / smpl_per_lin;
			m_last_col=static_cast<int>(tmp_pos*m_img_width);

			/// Now the image will start at the right column offset.
			m_fax_pix_num = m_last_col * bytes_per_pixel ;

			m_num_phase_lines=0;
			/// NOTE: If five or more, blacks stripes appear on the image ???
			if( m_phase_lines >= 4 ) {
				LOG_INFO( _("Skipping to reception: m_phase_lines=%d m_num_phase_lines=%d. LPM=%f"),
					m_phase_lines, m_num_phase_lines, m_lpm_img );
				skip_phasing_to_image(false);
			}
		} else if(m_phase_lines>0 && ++m_num_phase_lines>=5) {
			/// TODO: Compare with m_tx_phasing_lin which indicates the number of phasing
			/// lines sent when transmitting an image.
			LOG_INFO( _("Missed last phasing line m_phase_lines=%d m_num_phase_lines=%d. LPM=%f"),
				m_phase_lines, m_num_phase_lines, m_lpm_img );
			/// Phasing header is finished but could not get the center.
			skip_phasing_to_image(true);
		} else if(m_curr_phase_len>5*m_sample_rate) {
			m_curr_phase_len=0;
			PUT_STATUS( state_rx_str() << ". " << _("Decoding phasing line, resetting.") );
		} else {
			/// Here, if no phasing is detected. Must be very fast.
		}
		PUT_STATUS( state_rx_str() << ". " << _("Decoding phasing line, reset.") );
		m_curr_phase_len=m_curr_phase_high=0;
	}
	else
	{
		/// We do not the LPM so we assume the default.
		/// TODO: We could take the one given by the GUI.
		double smpl_per_lin = lpm_to_samples( LPM_DEFAULT );
		int smpl_per_lin_int = smpl_per_lin ;
		int nb_tested_phasing_lines = m_phasing_calls_nb / smpl_per_lin ;

		if(
			(m_phase_lines == 0) &&
			(m_num_phase_lines == 0) &&
		 	(nb_tested_phasing_lines >= 30) &&
			( (m_phasing_calls_nb % smpl_per_lin_int) == 0 ) ) {
			switch( the_signal.signal_state() ) {
			case RXIMAGE :
				LOG_INFO( _("Starting reception when phasing:%s"), the_signal.signal_text() );
				skip_phasing_to_image(true);
				break ;
			/// If RXPHASING, we stay in phasing mode.
			case RXAPTSTOP :
				LOG_INFO( _("Strong APT stop when phasing:%s"), the_signal.signal_text() );
				end_rx();
				skip_apt_rx();
			default : break ;
			}
		}
	}
}

bool fax_implementation::decode_image(int x)
{
	double current_row_dbl = m_img_sample / m_smpl_per_lin ;
	int current_row = current_row_dbl ;
	int curr_col= m_img_width * (current_row_dbl - current_row) ;

	if(curr_col==m_last_col) {
		m_pixel_val+=x;
		m_pix_samples_nb++;
	} else {
		if(m_pix_samples_nb>0) {
			m_pixel_val/=m_pix_samples_nb;

			/// TODO: Put m_fax_pix_num in wefax_pic so that the saving of an image
			/// will only be the number of added pixels. And it will hide
			/// the storage of B/W pixels in three contiguous ones.
			//
			// TODO: If the machine is heavily loaded, it loses a couple of pixel.
			// The consequence is that the image is shifted.
			// We could use the local clock to deduce where the pixel must be written:
			// - Store the first pixel reception time.
			// - Compute m_fax_pix_num = (reception_time * LPM * image_width)
			REQ( wefax_pic::update_rx_pic_bw, m_pixel_val, m_fax_pix_num );
			m_fax_pix_num += bytes_per_pixel ;
		}
		m_last_col=curr_col;
		m_pixel_val=x;
		m_pix_samples_nb=1;
	}

	/// Prints the status from time to time.
	if( (m_img_sample % 10000) == 0 ) {
		PUT_STATUS( state_rx_str()
			<< ". " << _("Image reception")
			<< ", " << _("sample") << "=" << m_img_sample );
	}

	/// Hard-limit to the number of rows.
	if( current_row >= m_max_fax_rows ) {
		LOG_INFO( _("Maximum number of rows reached:%d. Manual=%d"), current_row, m_manual_mode );
		save_automatic("max");
		end_rx();
		return true ;
	} else {
		return false ;
	}
}

/// Called automatically or by the GUI, when clicking "Skip APT"
void fax_implementation::skip_apt_rx(void)
{
	wefax_pic::skip_rx_apt();
	if(m_rx_state!=RXAPTSTART) {
		LOG_ERROR( _("Should be in APT state. State=%s. Manual=%d"), state_rx_str().c_str(), m_manual_mode );
	}
	lpm_set( 0 );
	m_lpm_sum_rx = 0;
	m_rx_state=RXPHASING;
	m_phasing_calls_nb = 0 ;
	m_phase_high = m_current_value>=128 ? true : false;
	m_curr_phase_len=m_curr_phase_high=0;
	m_phase_lines=m_num_phase_lines=0;
	m_img_sample=0; /// Used for correlation between consecutive lines.
}

/// Called by the user when skipping phasing,
/// or automatically when phasing is detected.
void fax_implementation::skip_phasing_to_image(bool auto_center)
{
	m_ptr_wefax->qso_rec_init();
	m_ptr_wefax->qso_rec().putField( TX_PWR, "0");

	REQ( wefax_pic::skip_rx_phasing, auto_center );
	if(m_rx_state!=RXPHASING) {
		LOG_ERROR( _("Should be in phasing state. State=%s"), state_rx_str().c_str() );
	}
	m_rx_state=RXIMAGE;

	/// For monochrome, LPM=60, 90, 100, 120, 180, 240. For colour, LPM =120, 240
	/// So we round to the nearest integer to avoid slanting.
	int lpm_integer = wefax_pic::normalize_lpm( m_lpm_img );
	if( m_lpm_img != lpm_integer )
	{
		LOG_INFO( _("LPM rounded from %f to %d. Manual=%d"), m_lpm_img, lpm_integer, m_manual_mode );
	}

	reset_counters();

	/// From now on, m_lpm_img will never change and has a normalized value.
	REQ( wefax_pic::update_rx_lpm, lpm_integer);
	PUT_STATUS( state_rx_str() << ". " << _("Decoding phasing line LPM=") << lpm_integer );
	lpm_set( lpm_integer );
}

/// Called by the user when clicking button. Never called automatically.
void fax_implementation::skip_phasing_rx(bool auto_center)
{
	if(m_rx_state!=RXPHASING) {
		LOG_ERROR( _("Should be in phasing state. State=%s"), state_rx_str().c_str() );
	}
	skip_phasing_to_image(auto_center);

	/// We force these two values because these could not be detected automatically.
	if( m_lpm_img != LPM_DEFAULT ) {
		lpm_set( LPM_DEFAULT );
		LOG_INFO( _("Forcing m_lpm_img=%f. Manual=%d"), m_lpm_img, m_manual_mode );
	}
	m_img_sample=0; /// The image start may not be what the phasing would have told.
}

// Here we want to remove the last detected phasing line and the following
// non phasing line from the beginning of the image and one second of apt stop
// from the end
void fax_implementation::end_rx(void)
{
	/// Synchronized otherwise there might be a crash if something tries to access the data.
	REQ(wefax_pic::abort_rx_viewer );
	m_rx_state=RXAPTSTART;
	reset_counters();
}

/// Receives data from the soundcard.
void fax_implementation::rx_new_samples(const double* audio_ptr, int audio_sz)
{
	int demod[audio_sz];
	static const double half_255 = 255.0 * 0.5 ;
	const double ratio_sam_devi = half_255 * static_cast<double>(m_sample_rate)/fm_deviation;

	/// The reception filter may have been changed by the GUI.
	C_FIR_filter & ref_fir_filt_pair = m_rx_filters[ m_ix_filt ];

	const double half_arc_sine_size = m_dbl_arc_sine.size() / 2.0 ;

	for(int i=0; i<audio_sz; i++) {
		double idx_aux = audio_ptr[i] ;

		complex firin( idx_aux*m_dbl_cosine.next_value(), idx_aux*m_dbl_sine.next_value() );
		complex firout ;

		/// This returns zero if the filter is not yet stable.
		/* int run_status = */ ref_fir_filt_pair.run( firin, firout );

		double ifirout = firout.real();
		double qfirout = firout.imag();

		if(m_freq_mod ) {
			/// Normalize values.
			double abs=std::sqrt(qfirout*qfirout+ifirout*ifirout);
			/// cosine(a)
			ifirout/=abs;
			/// sine(a)
			qfirout/=abs;

			/// Does it mean the signal should not be too strong ? Max is 32767.
			if(abs>10000) {
				/// Real part of the product of current vector,
				/// by previous vector rotated of 90 degrees.
				/// It makes something like sine(a-b).
				/// Maybe the derivative of the phase, that is,
				/// the instantaneous frequency ?
				double y = m_q_fir_old * ifirout - m_i_fir_old * qfirout ;

				/// Mapped to the interval [0 .. size]
				/// m_dbl_arc_sine[i] = asin(2*i/m_dbl_arc_sine.size-1)/2/Pi.
				/// TODO: Therefore it could be simplified ?
				y = ( y + 1.0 ) * half_arc_sine_size;

				/// TODO: y might be rounded with more accuracy: (int)(Y+0.5)
				double x = ratio_sam_devi * m_dbl_arc_sine[static_cast<size_t>(y)];

				int scaled_x = x + half_255 ;
				if(scaled_x < 0) {
					scaled_x=0;
				} else if(scaled_x > 255) {
					scaled_x=255;
				}
				demod[i]=scaled_x;
			} else {
				demod[i]=0;
			}
		} else {
			/// Why 96000 ? bytes_per_pixel * 32000 because color ?
			ifirout/=96000;
			qfirout/=96000;
			demod[i]=static_cast<int>
				(std::sqrt(ifirout*ifirout+qfirout*qfirout));
		}

		m_i_fir_old=ifirout;
		m_q_fir_old=qfirout;
	}
	decode( demod, audio_sz);

#ifdef WEFAX_DISPLAY_SCOPE
	/// Nothing really meaningful to display.
	/// Beware that some pixels are lost if too many things are displayed
	double scope_demod[ audio_sz ];
	/// TODO: Do this in the loop, it will avoid conversions.
	for( int i = 0 ; i < audio_sz ; ++i ) {
		scope_demod[ i ] = demod[i];
	}
	set_scope( (double *)scope_demod, audio_sz , true );
#endif // WEFAX_DISPLAY_SCOPE
}

// Init transmission. Called once only.
void fax_implementation::init_tx(int the_smpl_rat)
{
	m_sample_rate=the_smpl_rat;
	set_carrier(wefax_default_carrier);
	/// These reset increments of trigo tables.
	m_ptr_wefax->set_freq(wefax_default_carrier);
	m_tx_state=TXAPTSTART;
}

/// Elements of buffer are between 0.0 and 1.0
void fax_implementation::modulate(const double* buffer, int number)
{
	/// TODO: This should be in m_short_sine
	static const double dbl_max_short_invert = 1.0 / 32768.0 ;

	double stack_xmt_buf[number] ;
	if( m_freq_mod ) {
		for(int i = 0; i < number; i++) {
			double tmp_freq = m_carrier + 2. * ( buffer[i] - 0.5 ) * fm_deviation ;
			m_short_sine.set_increment(m_short_sine.size() * tmp_freq / m_sample_rate);
			stack_xmt_buf[i] = m_short_sine.next_value() * dbl_max_short_invert ;
		}
	} else {
		/// Beware: Not tested !
		for(int i = 0; i < number; i++) {
			stack_xmt_buf[i] = m_short_sine.next_value() * buffer[i] * dbl_max_short_invert ;
		}
	}
	m_ptr_wefax->ModulateXmtr( stack_xmt_buf, number );
}

/// Returns true if succesful
bool fax_implementation::trx_do_next(void)
{
	LOG_DEBUG("m_xmt_bytes=%d m_lpm_img=%f", m_xmt_bytes, m_lpm_img );

	/// The number of samples sent for one line. The LPM is given by the GUI.
	const int smpl_per_lin= m_smpl_per_lin;

	/// Should not be too big because it is allocated on the stack, gcc feature.
	static const int block_len = 256 ;
	double buf[block_len];

	bool end_of_loop = false ;
	bool tx_completed = true ;
	int curr_sample_idx = 0 , nb_samples_to_send  = 0 ;

	for(int num_bytes_to_write=0; ; ++num_bytes_to_write ) {
		bool disp_msg = ( ( num_bytes_to_write % block_len ) == 0 ) && ( num_bytes_to_write > 0 );
	       
		if( disp_msg ) {
			modulate( buf, num_bytes_to_write);

			const char * curr_status_msg = state_to_str( m_tx_state );
			/// TODO: Should be multiplied by 3 when sending in BW ?
			if( m_ptr_wefax->is_tx_finished(curr_sample_idx, nb_samples_to_send, curr_status_msg ) ) {
				end_of_loop = true ;
				tx_completed = false;
				continue ;
			};
			num_bytes_to_write = 0 ;
		};
		if( end_of_loop == true ) {
			break ;
		}
		if(m_tx_state==TXAPTSTART) {
			nb_samples_to_send = m_sample_rate * m_start_duration ;
			if( curr_sample_idx < nb_samples_to_send ) {
				buf[num_bytes_to_write]=(curr_sample_idx*2*m_apt_start_freq/m_sample_rate)%2;
				curr_sample_idx++;
			} else {
				m_tx_state=TXPHASING;
				curr_sample_idx=0;
			}
		}
		if(m_tx_state==TXPHASING) {
			nb_samples_to_send = smpl_per_lin * m_tx_phasing_lin ;
			if( curr_sample_idx < nb_samples_to_send ) {
				double pos= (double)(curr_sample_idx % smpl_per_lin) / (double)smpl_per_lin;
				buf[num_bytes_to_write] = (pos<0.025||pos>=0.975 )
					? (m_phase_inverted?0.0:1.0) 
					: (m_phase_inverted?1.0:0.0);
				curr_sample_idx++;
			} else {
				m_tx_state=ENDPHASING;
				curr_sample_idx=0;
			}
		}
		if(m_tx_state==ENDPHASING) {
			nb_samples_to_send = smpl_per_lin ;
			if( curr_sample_idx < nb_samples_to_send ) {
				buf[num_bytes_to_write]= m_phase_inverted?0.0:1.0;
				curr_sample_idx++;
			} else {
				m_tx_state=TXIMAGE;
				curr_sample_idx=0;
			}
		}
		if(m_tx_state==TXIMAGE) {
			/// The image must be stretched so that its width matches the fax width.
			/// Accordingly the height is stretched. This could be computed in advance.
			double ratio_img_to_fax = (double)smpl_per_lin / m_img_tx_cols ;

			int fax_rows_nb = m_img_tx_rows * ratio_img_to_fax ;

			/// The number of samples for the whole image.
			/// TODO: It could be computed once and for all.
			/// NOT SURE AT ALL.
			// nb_samples_to_send = (m_img_color?bytes_per_pixel:1) * smpl_per_lin * fax_rows_nb ;
			nb_samples_to_send = smpl_per_lin * fax_rows_nb ;

			/// TODO: Maybe we are oversampling the image and losing definition.
			if( curr_sample_idx < nb_samples_to_send ) {
				int tmp_col = ( ( curr_sample_idx / bytes_per_pixel ) % smpl_per_lin ) / ratio_img_to_fax ;
				if( tmp_col >= m_img_tx_cols ) {
					LOG_ERROR(
						"Inconsistent tmp_col=%d m_img_tx_cols=%d ratio_img_to_fax=%f "
						"curr_sample_idx=%d smpl_per_lin=%d fax_rows_nb=%d m_img_color=%d",
							tmp_col, m_img_tx_cols, ratio_img_to_fax,
							curr_sample_idx, smpl_per_lin, fax_rows_nb, (int)m_img_color );
					exit(EXIT_FAILURE);
				}

				int tmp_row= curr_sample_idx / ( smpl_per_lin * ratio_img_to_fax );

				if( tmp_row >= m_img_tx_rows ) {
					LOG_ERROR( "Inconsistent tmp_row=%d m_img_tx_rows=%d "
						"curr_sample_idx=%d smpl_per_lin=%d fax_rows_nb=%d "
						"ratio_img_to_fax=%f nb_samples_to_send=%d",
							tmp_row, m_img_tx_rows,
							curr_sample_idx, smpl_per_lin, fax_rows_nb,
							ratio_img_to_fax, nb_samples_to_send );
					exit(EXIT_FAILURE);
				}

				int image_offset = tmp_row * m_img_tx_cols + tmp_col ;
				if( image_offset >= m_xmt_bytes )
				{
					LOG_ERROR( "Inconsistent image_offset=%d m_xmt_bytes=%d "
						"tmp_row=%d m_img_tx_rows=%d "
						"curr_sample_idx=%d smpl_per_lin=%d fax_rows_nb=%d",
							image_offset, m_xmt_bytes,
							tmp_row, m_img_tx_rows,
							curr_sample_idx, smpl_per_lin, fax_rows_nb );
					exit(EXIT_FAILURE);
				};

				/// TODO: NOT SURE AT ALL ...
				/*
				if( m_img_color ) {
					image_offset /= bytes_per_pixel ;
				}
				unsigned char temp_pix = m_xmt_pic_buf[ image_offset ];
				REQ(wefax_pic::set_tx_pic, temp_pix, tmp_col, tmp_row, m_img_tx_cols, m_img_color );
				buf[num_bytes_to_write]= (double)temp_pix / 256.0 ;
				curr_sample_idx++;
				*/
				unsigned char temp_pix = m_xmt_pic_buf[ image_offset ];
				wefax_pic::set_tx_pic( temp_pix, tmp_col, tmp_row, m_img_tx_cols, m_img_color );
				buf[num_bytes_to_write]= (double)temp_pix / 256.0 ;
				if( m_img_color ) {
					curr_sample_idx++;
				} else {
					/// Takes one byte out of three because they have the same value
					/// for a monochrome image.
					curr_sample_idx += bytes_per_pixel ;
				}
			} else {
				m_tx_state=TXAPTSTOP;
				curr_sample_idx=0;
			}
		}
		if(m_tx_state==TXAPTSTOP) {
			nb_samples_to_send = m_sample_rate * m_stop_duration ;
			if( curr_sample_idx < nb_samples_to_send ) {
				buf[num_bytes_to_write]=curr_sample_idx*2*m_apt_stop_freq/m_sample_rate%2;
				curr_sample_idx++;
			} else {
				m_tx_state=IDLE;
				end_of_loop = true ;
				continue ;
			}
		}
	} // loop
	return tx_completed ;
}

void fax_implementation::tx_params_set(
	int the_lpm,
	const unsigned char * xmtpic_buffer,
	bool is_color,
	int img_w,
	int img_h,
	int xmt_bytes )
{
	LOG_DEBUG("img_w=%d img_h=%d xmt_bytes=%d the_lpm=%d is_color=%d",
			img_w, img_h, xmt_bytes, the_lpm, (int)is_color);
	m_img_tx_rows=img_h;
	m_img_tx_cols=img_w;
	m_xmt_bytes = xmt_bytes ;
	m_img_color = is_color ;
	lpm_set( the_lpm );
	m_xmt_pic_buf = xmtpic_buffer ;

	PUT_STATUS( _("Sending picture.") << " " << _("Bytes") << "=" << m_xmt_bytes
			<< _(" rows=") << m_img_tx_rows
			<< _(" cols=") << m_img_tx_cols );
}

void fax_implementation::tx_apt_stop(void)
{
	m_tx_state=TXAPTSTOP;
}

//=============================================================================
//
//=============================================================================

/// Called by trx_trx_transmit_loop
void  wefax::tx_init(SoundBase *sc)
{
	modem::scard = sc; // SoundBase
	
	videoText(); // In trx/modem.cxx
	m_impl->init_tx(modem::samplerate) ;
}

/// This updates the window label according to the state.
void wefax::update_rx_label(void) const
{
	std::string tmp_label( _("Reception ") );
	tmp_label += mode_info[modem::mode].name ;

	if( m_impl->manual_mode_get() ) {
		tmp_label += _(" - Manual") ;
	} else {
		tmp_label += _(" - APT control") ;
	}

	REQ( wefax_pic::set_rx_label, tmp_label );
}

void  wefax::rx_init()
{
	put_MODEstatus(modem::mode);
	m_impl->init_rx(modem::samplerate) ;

	/// This updates the window label.
	set_rx_manual_mode(false);

	REQ( wefax_pic::resize_rx_viewer, m_impl->fax_width() );
	update_rx_label();
}

void wefax::init()
{
	modem::init();

	/// TODO: Maybe remove, probably not necessary because called by trx_trx_loop
	rx_init();

	/// TODO: Display scope.
	set_scope_mode(Digiscope::SCOPE);
}

void wefax::shutdown()
{
}

wefax::~wefax()
{
	modem::stopflag = true;

	REQ( wefax_pic::rx_hide );

	/// Maybe we are receiving an image, this must be stopped.
	end_reception();

	/// Maybe we are sending an image.
	REQ( wefax_pic::abort_tx_viewer );

	activate_wefax_image_item(false);

	delete m_impl ;

	/// Not really useful, just to help debugging.
	m_impl = 0 ;
}

wefax::wefax(trx_mode wefax_mode) : modem()
{
	/// Beware that it is already set by modem::modem
	modem::cap |= CAP_AFC | CAP_REV | CAP_IMG ;
	LOG_DEBUG("wefax_mode=%d", (int)wefax_mode);

	wefax::mode = wefax_mode;

	modem::samplerate = WEFAXSampleRate ;

	m_impl = new fax_implementation(wefax_mode, this);

	/// Now this object is usable by wefax_pic.
	wefax_pic::setpicture_link(this);

	modem::bandwidth = fm_deviation * 2 ;
	
	m_abortxmt = false;
	modem::stopflag = false;

	/// By default, images are not logged to adif file.
	m_adif_log = false ;

	// There is only one instance of the reception and transmission
	// windows, therefore only static methods.
	wefax_pic::create_tx_viewer();

	/// TODO: Temp only, later remove sizing info, do it once only in resize.
	wefax_pic::create_rx_viewer();
	update_rx_label();

	activate_wefax_image_item(true);

	init();
}


//=====================================================================
// receive processing
//=====================================================================

#ifdef __linux__

#include <sys/timeb.h>
/// This must return the current time in seconds with high precision.
static double current_time(void)
{
	struct timeb tmp_timb ;
	ftime( &tmp_timb );

	return (double)tmp_timb.time + tmp_timb.millitm / 1000.0 ;
}

#else
#include <ctime>
/// This is much less accurate.
static double current_time(void)
{
	clock_t clks = clock();

	return clks * 1.0 / CLOCKS_PER_SEC;
}
#endif


/// Callback continuously called by fldigi modem class.
int wefax::rx_process(const double *buf, int len)
{
	if( len == 0 )
	{
		return 0 ;
	}

	static const int avg_buf_size = 256 ;

	static int idx = 0 ;

	static double buf_tim[avg_buf_size];
	static int    buf_len[avg_buf_size];

	int idx_mod = idx % avg_buf_size ;

	/// Here we estimate the average number of pixels per second.
	buf_tim[idx_mod] = current_time();
	buf_len[idx_mod] = len ;

	++idx ;

	/// Wait some seconds otherwise not significant.
	if( idx >= avg_buf_size ) {
		if( idx == avg_buf_size ) {
			LOG_INFO( _("Starting samples loss control avg_buf_size=%d"), avg_buf_size);
		}
		int idx_mod_first = idx % avg_buf_size ;
		double total_tim = buf_tim[idx_mod] - buf_tim[idx_mod_first];
		int total_len = 0 ;
		for( int ix = 0 ; ix < avg_buf_size ; ++ix ) {
			total_len += buf_len[ix] ;
		}

		/// Estimate the real sample rate.
		double estim_smpl_rate = (double)total_len / total_tim ;

		/// If too far from what it should be, it means that pixels were lost.
		if( estim_smpl_rate < 0.95 * modem::samplerate ) {
			int expected_samples = (int)( modem::samplerate * total_tim + 0.5 );
			int missing_samples = expected_samples - total_len ;

			LOG_INFO(_("Lost %d samples idx=%d estim_smpl_rate=%f total_tim=%f total_len=%d"),
				missing_samples,
				idx,
				estim_smpl_rate,
				total_tim,
				total_len );
			
			if( missing_samples <= 0 ) {
				/// This should practically never happen.
				LOG_WARN(_("Cannot compensate"));
			} else {
				/// Adjust the number of received pixels,
				/// so the lost frames are signaled once only.
				buf_len[idx_mod] += missing_samples ;
			}
		}
	}

	/// Back to normal processing.
	m_impl->rx_new_samples( buf, len );
	return 0;
}

//=====================================================================
// transmit processing
//=====================================================================

/// This is called by wefax-pix.cxx before entering transmission loop.
void wefax::set_tx_parameters(
	int the_lpm,
	const unsigned char * xmtpic_buffer,
	bool is_color,
	int img_w,
	int img_h,
	int xmt_bytes )
{
	m_impl->tx_params_set(
		the_lpm,
		xmtpic_buffer,
		is_color,
		img_w,
		img_h,
		xmt_bytes );
}

/// Callback continuously called by fldigi modem class.
int wefax::tx_process()
{
	bool tx_was_completed = m_impl->trx_do_next();
	std::string status ;
	if( false == tx_was_completed ) {
		status = _("Transmission cancelled");
		LOG_INFO("Sending cancelled" );
		m_qso_rec.putField(NOTES, status.c_str() );
	}
	qso_rec_save();

	REQ_FLUSH(GET_THREAD_ID());

	FL_LOCK_E();
	wefax_pic::restart_tx_viewer();
	transmit_lock_release(status);
	m_abortxmt = false;
	FL_UNLOCK_E();
	m_impl->tx_apt_stop();
	return -1;
}

void wefax::skip_apt(void)
{
	m_impl->skip_apt_rx();
}

/// auto_center indicates whether we try to find the margin of the image
/// automatically. This is the fact when skipping to image reception
/// is triggered manually or based on the signal power.
void wefax::skip_phasing(bool auto_center)
{
	m_impl->skip_phasing_rx(auto_center);
}

void wefax::end_reception(void)
{
	m_impl->end_rx();
}

// Continuous reception or APT control.
void wefax::set_rx_manual_mode( bool manual_flag )
{
	m_impl->manual_mode_set( manual_flag );
	update_rx_label();
}

// Maximum admissible number of lines for a fax, adjustable by the user.
void wefax::set_max_lines( int max_lines )
{
	m_impl->max_lines_set( max_lines );
}

int wefax::get_max_lines(void) const
{
	return m_impl->max_lines_get();
}

void wefax::set_lpm( int the_lpm )
{
	return m_impl->lpm_set( the_lpm );
}

/// Transmission time in seconds. Factor 3 if b/w image.
int wefax::tx_time( int nb_bytes ) const
{
	return (double)nb_bytes / ( modem::samplerate * 3.0 );
}

/// This prints a message about the progress of image sending,
/// then tells whether the user has requested the end.
bool wefax::is_tx_finished( int ix_sample, int nb_sample, const char * msg ) const
{
	static char wefaxmsg[256];
	double fraction_done = nb_sample ? 100.0 * (double)ix_sample / nb_sample : 0.0 ;
	int tm_left = tx_time( nb_sample - ix_sample );
	snprintf(
			wefaxmsg, sizeof(wefaxmsg),
			"%s : %04.1f%% done. Time left: %dm %ds",
			msg,
			fraction_done,
			tm_left / 60,
			tm_left % 60 );
	put_status(wefaxmsg);

	bool is_finished = modem::stopflag || m_abortxmt ;
	if( is_finished ) {
		LOG_INFO("Transmit finished");
	}
	return is_finished ;
}

/// This returns the names of the possible reception filters.
const char ** wefax::rx_filters(void)
{
	return fir_filter_pair_set::filters_list();
}

/// Allows to choose the reception filter.
void wefax::set_rx_filter( int idx_filter )
{
	m_impl->set_filter_rx( idx_filter );
}

std::string wefax::suggested_filename(void) const
{
	return m_impl->generate_filename( "gui" );
};

/// This creates a QSO record to be written to an adif file.
void wefax::qso_rec_init(void)
{
	if( m_adif_log == false ) {
		return ;
	}

	/// Ideally we should find out the name of the fax station.
	m_qso_rec.putField(CALL, "Wefax");
	m_qso_rec.putField(NAME, "Weather fax");

	time_t tmp_time = time(NULL);
	struct tm tmp_tm ;
	localtime_r( &tmp_time, &tmp_tm );

	char buf_date[64] ;
	snprintf( buf_date, sizeof(buf_date),
		"%04d%02d%02d",
		1900 + tmp_tm.tm_year,
		1 + tmp_tm.tm_mon,
		tmp_tm.tm_mday );
	m_qso_rec.putField(QSO_DATE, buf_date);

	char buf_timeon[64] ;
	snprintf( buf_timeon, sizeof(buf_timeon),
		"%02d%02d",
		tmp_tm.tm_hour,
		tmp_tm.tm_min );
	m_qso_rec.putField(TIME_ON, buf_timeon);

	long long tmp_fl = wf->rfcarrier() ;
	double freq_dbl = tmp_fl / 1000000.0 ;
	char buf_freq[64];
	snprintf( buf_freq, sizeof(buf_freq), "%lf", freq_dbl );
	m_qso_rec.putField(FREQ, buf_freq );

	/// The method get_mode should be const.
	m_qso_rec.putField(MODE, mode_info[ const_cast<wefax*>(this)->get_mode()].adif_name );

	// m_qso_rec.putField(QTH, inpQth_log->value());
	// m_qso_rec.putField(STATE, inpState_log->value());
	// m_qso_rec.putField(VE_PROV, inpVE_Prov_log->value());
	// m_qso_rec.putField(COUNTRY, inpCountry_log->value());
	// m_qso_rec.putField(GRIDSQUARE, inpLoc_log->value());
	// m_qso_rec.putField(QSLRDATE, inpQSLrcvddate_log->value());
	// m_qso_rec.putField(QSLSDATE, inpQSLsentdate_log->value());
	// m_qso_rec.putField(RST_RCVD, inpRstR_log->value ());
	// m_qso_rec.putField(RST_SENT, inpRstS_log->value ());
	// m_qso_rec.putField(SRX, inpSerNoIn_log->value());
	// m_qso_rec.putField(STX, inpSerNoOut_log->value());
	// m_qso_rec.putField(XCHG1, inpXchgIn_log->value());
	// m_qso_rec.putField(MYXCHG, inpMyXchg_log->value());
	// m_qso_rec.putField(IOTA, inpIOTA_log->value());
	// m_qso_rec.putField(DXCC, inpDXCC_log->value());
	// m_qso_rec.putField(CONT, inpCONT_log->value());
	// m_qso_rec.putField(CQZ, inpCQZ_log->value());
	// m_qso_rec.putField(ITUZ, inpITUZ_log->value());
	// m_qso_rec.putField(TX_PWR, inpTX_pwr_log->value());
}

/// Called once a QSO rec has been filled with information. Saved to adif file.
void wefax::qso_rec_save(void)
{
	if( m_adif_log == false ) {
		return ;
	}

	time_t tmp_time = time(NULL);
	struct tm tmp_tm ;
	localtime_r( &tmp_time, &tmp_tm );

	char buf_timeoff[64] ;
	snprintf( buf_timeoff, sizeof(buf_timeoff),
		"%02d%02d",
		tmp_tm.tm_hour,
		tmp_tm.tm_min );
	m_qso_rec.putField(TIME_OFF, buf_timeoff);

	adifFile.writeLog (logbook_filename.c_str(), &qsodb);
	qsodb.isdirty(0);

	qsodb.qsoNewRec (&m_qso_rec);
	// dxcc_entity_cache_add(&rec);
	LOG_INFO( _("Updating log book %s"), logbook_filename.c_str() );
}

/// Called when changing the carrier in the GUI, and by class modem with 1000Hz, when initializing.
void wefax::set_freq(double freq)
{
	modem::set_freq(freq);
	/// This must recompute the increments of the trigonometric tables.
	m_impl->set_carrier( freq );
}

// String telling the tx and rx internal state.
std::string wefax::state_string(void) const
{
	return m_impl->state_string();
}

/// Called when a file is saved, so XML-RPC calls can get the filename.
void wefax::put_received_file( const std::string &filnam )
{
	m_impl->put_received_file( filnam );
}

/// Returns a received file name, by chronological order.
std::string wefax::get_received_file( double max_seconds )
{
	return m_impl->get_received_file( max_seconds );
}

/// This is not thread-safe, at all.
std::string wefax::send_file( const std::string & filnam, double max_seconds )
{
	return m_impl->send_file( filnam, max_seconds );
}

/// Transmitting files is done in exclusive mode.
bool wefax::transmit_lock_acquire(const std::string & filnam, double max_seconds )
{
	return m_impl->transmit_lock_acquire( filnam, max_seconds );
}

/// Called after a file is sent.
void wefax::transmit_lock_release( const std::string & err_msg )
{
	m_impl->transmit_lock_release( err_msg );
}

