Improved frequency calibration

Measure check  box added to FreqCal  mode, check to record  to fmt.all
with  current  calibration correction  disabled,  uncheck  to see  the
impact of the current calibration parameters.

The  fmt.all  file  is  now  optionally  renamed  to  fmt.bak  when  a
calibration solution  is accepted.  This allows  users to  preserve an
fmt.all file that they might have edited for best fit.

A calibration procedure might proceed thus:-

1) select FreqCal mode,

2) step through suggested  calibration test frequencies deleting those
that have no usable signal,

3) enable "Menu->Tools->Execute frequency calibration cycle" and check
that suitable signals are present,

4) select a suitable FTol and T/R period,

5) check  "Measure" and let the  cycle complete a few  times to gather
data,

6) uncheck "Measure" to complete the data capture, optionally tidy the
fmt.all file with your favourite editor,

7) push "Menu->Tools->Solve for  calibration parameters" and accept if
you like what you see,

8) sit back and admire your accurately frequency calibrated station.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@8167 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2017-10-13 22:34:21 +00:00
parent a9a1ac1d7a
commit 6424b4986f
6 changed files with 107 additions and 63 deletions

View File

@ -517,8 +517,8 @@ private:
bool rig_changed_;
TransceiverState cached_rig_state_;
int rig_resolution_; // see Transceiver::resolution signal
double frequency_calibration_intercept_;
double frequency_calibration_slope_ppm_;
CalibrationParams calibration_;
bool frequency_calibration_disabled_; // not persistent
unsigned transceiver_command_number_;
// configuration fields that we publish
@ -672,15 +672,17 @@ QDir Configuration::azel_directory () const {return m_->azel_directory_;}
QString Configuration::rig_name () const {return m_->rig_params_.rig_name;}
bool Configuration::pwrBandTxMemory () const {return m_->pwrBandTxMemory_;}
bool Configuration::pwrBandTuneMemory () const {return m_->pwrBandTuneMemory_;}
auto Configuration::calibration_params () const -> CalibrationParams
void Configuration::set_calibration (CalibrationParams params)
{
return {m_->frequency_calibration_intercept_, m_->frequency_calibration_slope_ppm_};
m_->calibration_ = params;
}
void Configuration::adjust_calibration_parameters (double intercept, double slope_ppm)
void Configuration::enable_calibration (bool on)
{
m_->frequency_calibration_intercept_ += intercept;
m_->frequency_calibration_slope_ppm_ += slope_ppm;
auto target_frequency = m_->remove_calibration (m_->cached_rig_state_.frequency ()) - m_->current_offset_;
m_->frequency_calibration_disabled_ = !on;
transceiver_frequency (target_frequency);
}
bool Configuration::is_transceiver_online () const
@ -802,12 +804,15 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
QSettings * settings, QWidget * parent)
: QDialog {parent}
, self_ {self}
, transceiver_thread_ {nullptr}
, ui_ {new Ui::configuration_dialog}
, settings_ {settings}
, doc_dir_ {doc_path ()}
, data_dir_ {data_path ()}
, temp_dir_ {temp_directory}
, writeable_data_dir_ {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}
, restart_sound_input_device_ {false}
, restart_sound_output_device_ {false}
, frequencies_ {&bands_}
, next_frequencies_ {&bands_}
, stations_ {&bands_}
@ -822,6 +827,7 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
, have_rig_ {false}
, rig_changed_ {false}
, rig_resolution_ {0}
, frequency_calibration_disabled_ {false}
, transceiver_command_number_ {0}
, degrade_ {0.} // initialize to zero each run, not
// saved in settings
@ -1033,9 +1039,6 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_);
ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_);
restart_sound_input_device_ = false;
restart_sound_output_device_ = false;
enumerate_rigs ();
initialize_models ();
@ -1135,8 +1138,8 @@ void Configuration::impl::initialize_models ()
ui_->accept_udp_requests_check_box->setChecked (accept_udp_requests_);
ui_->udpWindowToFront->setChecked(udpWindowToFront_);
ui_->udpWindowRestore->setChecked(udpWindowRestore_);
ui_->calibration_intercept_spin_box->setValue (frequency_calibration_intercept_);
ui_->calibration_slope_ppm_spin_box->setValue (frequency_calibration_slope_ppm_);
ui_->calibration_intercept_spin_box->setValue (calibration_.intercept);
ui_->calibration_slope_ppm_spin_box->setValue (calibration_.slope_ppm);
if (rig_params_.ptt_port.isEmpty ())
{
@ -1338,8 +1341,8 @@ void Configuration::impl::read_settings ()
accept_udp_requests_ = settings_->value ("AcceptUDPRequests", false).toBool ();
udpWindowToFront_ = settings_->value ("udpWindowToFront",false).toBool ();
udpWindowRestore_ = settings_->value ("udpWindowRestore",false).toBool ();
frequency_calibration_intercept_ = settings_->value ("CalibrationIntercept", 0.).toDouble ();
frequency_calibration_slope_ppm_ = settings_->value ("CalibrationSlopePPM", 0.).toDouble ();
calibration_.intercept = settings_->value ("CalibrationIntercept", 0.).toDouble ();
calibration_.slope_ppm = settings_->value ("CalibrationSlopePPM", 0.).toDouble ();
pwrBandTxMemory_ = settings_->value("pwrBandTxMemory",false).toBool ();
pwrBandTuneMemory_ = settings_->value("pwrBandTuneMemory",false).toBool ();
}
@ -1434,8 +1437,8 @@ void Configuration::impl::write_settings ()
settings_->setValue ("AcceptUDPRequests", accept_udp_requests_);
settings_->setValue ("udpWindowToFront", udpWindowToFront_);
settings_->setValue ("udpWindowRestore", udpWindowRestore_);
settings_->setValue ("CalibrationIntercept", frequency_calibration_intercept_);
settings_->setValue ("CalibrationSlopePPM", frequency_calibration_slope_ppm_);
settings_->setValue ("CalibrationIntercept", calibration_.intercept);
settings_->setValue ("CalibrationSlopePPM", calibration_.slope_ppm);
settings_->setValue ("pwrBandTxMemory", pwrBandTxMemory_);
settings_->setValue ("pwrBandTuneMemory", pwrBandTuneMemory_);
settings_->setValue ("Region", QVariant::fromValue (region_));
@ -1824,8 +1827,8 @@ void Configuration::impl::accept ()
twoPass_ = ui_->cbTwoPass->isChecked ();
x2ToneSpacing_ = ui_->cbx2ToneSpacing->isChecked ();
realTimeDecode_ = ui_->cbRealTime->isChecked ();
frequency_calibration_intercept_ = ui_->calibration_intercept_spin_box->value ();
frequency_calibration_slope_ppm_ = ui_->calibration_slope_ppm_spin_box->value ();
calibration_.intercept = ui_->calibration_intercept_spin_box->value ();
calibration_.slope_ppm = ui_->calibration_slope_ppm_spin_box->value ();
pwrBandTxMemory_ = ui_->checkBoxPwrBandTxMemory->isChecked ();
pwrBandTuneMemory_ = ui_->checkBoxPwrBandTuneMemory->isChecked ();
auto new_server = ui_->udp_server_line_edit->text ();
@ -2677,14 +2680,16 @@ void Configuration::impl::fill_port_combo_box (QComboBox * cb)
auto Configuration::impl::apply_calibration (Frequency f) const -> Frequency
{
return std::llround (frequency_calibration_intercept_
+ (1. + frequency_calibration_slope_ppm_ / 1.e6) * f);
if (frequency_calibration_disabled_) return f;
return std::llround (calibration_.intercept
+ (1. + calibration_.slope_ppm / 1.e6) * f);
}
auto Configuration::impl::remove_calibration (Frequency f) const -> Frequency
{
return std::llround ((f - frequency_calibration_intercept_)
/ (1. + frequency_calibration_slope_ppm_ / 1.e6));
if (frequency_calibration_disabled_) return f;
return std::llround ((f - calibration_.intercept)
/ (1. + calibration_.slope_ppm / 1.e6));
}
#if !defined (QT_NO_DEBUG_STREAM)

View File

@ -159,16 +159,30 @@ public:
QColor color_NewCall () const;
bool pwrBandTxMemory () const;
bool pwrBandTuneMemory () const;
struct CalibrationParams
{
double intercept;
double slope_ppm;
};
CalibrationParams calibration_params () const;
CalibrationParams ()
: intercept {0.}
, slope_ppm {0.}
{
}
// Adjust the current calibration parameters, both arguments are in
// Hertz. They will be added to the current values.
void adjust_calibration_parameters (double intercept, double slope_ppm);
CalibrationParams (double the_intercept, double the_slope_ppm)
: intercept {the_intercept}
, slope_ppm {the_slope_ppm}
{
}
double intercept; // Hertz
double slope_ppm; // Hertz
};
// Temporarily enable or disable calibration adjustments.
void enable_calibration (bool = true);
// Set the calibration parameters and enable calibration corrections.
void set_calibration (CalibrationParams);
// This method queries if a CAT and PTT connection is operational.
bool is_transceiver_online () const;

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>552</width>
<width>521</width>
<height>507</height>
</rect>
</property>
@ -2622,12 +2622,12 @@ soundcard changes</string>
</connection>
</connections>
<buttongroups>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
</buttongroups>
</ui>

View File

@ -1225,18 +1225,20 @@ void MainWindow::dataSink(qint64 frames)
QString t=QString::fromLatin1(line);
DecodedText decodedtext {t, false, m_config.my_grid ()};
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(),
m_logBook,m_config.color_CQ(),m_config.color_MyCall(),m_config.color_DXCC(),
m_config.color_NewCall());
// Append results text to file "fmt.all".
QFile f {m_config.writeable_data_dir ().absoluteFilePath ("fmt.all")};
if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
QTextStream out(&f);
out << t << endl;
f.close();
} else {
MessageBox::warning_message (this, tr ("File Open Error")
, tr ("Cannot open \"%1\" for append: %2")
.arg (f.fileName ()).arg (f.errorString ()));
m_logBook,m_config.color_CQ(),m_config.color_MyCall(),m_config.color_DXCC(),
m_config.color_NewCall());
if (ui->measure_check_box->isChecked ()) {
// Append results text to file "fmt.all".
QFile f {m_config.writeable_data_dir ().absoluteFilePath ("fmt.all")};
if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
QTextStream out(&f);
out << t << endl;
f.close();
} else {
MessageBox::warning_message (this, tr ("File Open Error")
, tr ("Cannot open \"%1\" for append: %2")
.arg (f.fileName ()).arg (f.errorString ()));
}
}
if(m_ihsym==m_hsymStop && ui->actionFrequency_calibration->isChecked()) {
freqCalStep();
@ -2133,12 +2135,16 @@ void MainWindow::on_actionSolve_FreqCal_triggered()
.arg ("StdDev: ", 12).arg (rms, 0, 'f', 2)
, QString {}
, MessageBox::Cancel | MessageBox::Apply)) {
m_config.adjust_calibration_parameters (a, b);
// rename fmt.all as we have consumed the resulting calibration
// solution
auto const& backup_file_name = m_config.writeable_data_dir ().absoluteFilePath ("fmt.bak");
QFile::remove (backup_file_name);
QFile::rename (m_config.writeable_data_dir ().absoluteFilePath ("fmt.all"), backup_file_name);
m_config.set_calibration (Configuration::CalibrationParams {a, b});
if (MessageBox::Yes == MessageBox::query_message (this
, tr ("Delete Calibration Measurements")
, tr ("The \"fmt.all\" file will be renamed as \"fmt.bak\""))) {
// rename fmt.all as we have consumed the resulting calibration
// solution
auto const& backup_file_name = m_config.writeable_data_dir ().absoluteFilePath ("fmt.bak");
QFile::remove (backup_file_name);
QFile::rename (m_config.writeable_data_dir ().absoluteFilePath ("fmt.all"), backup_file_name);
}
}
}
@ -4682,6 +4688,7 @@ void MainWindow::displayWidgets(int n)
ui->cbFirst->setVisible ("FT8" == m_mode);
ui->actionEnable_AP->setVisible ("FT8" == m_mode);
ui->cbVHFcontest->setVisible(m_mode=="FT8" or m_mode=="MSK144");
ui->measure_check_box->setVisible ("FreqCal" == m_mode);
m_lastCallsign.clear (); // ensures Tx5 is updated for new modes
genStdMsgs (m_rpt, true);
}
@ -5121,6 +5128,7 @@ void MainWindow::on_actionFreqCal_triggered()
setup_status_bar (true);
// 18:15:47 0 1 1500 1550.349 0.100 3.5 10.2
ui->decodedTextLabel->setText(" UTC Freq CAL Offset fMeas DF Level S/N");
ui->measure_check_box->setChecked (false);
displayWidgets(nWidgets("001101000000000000000000"));
statusChanged();
}
@ -5214,9 +5222,8 @@ void MainWindow::on_TxFreqSpinBox_valueChanged(int n)
void MainWindow::on_RxFreqSpinBox_valueChanged(int n)
{
m_wideGraph->setRxFreq(n);
if (m_mode == "FreqCal"
&& m_frequency_list_fcal_iter != m_config.frequencies ()->end ()) {
setRig (m_frequency_list_fcal_iter->frequency_ - n);
if (m_mode == "FreqCal") {
setRig ();
}
statusUpdate ();
}
@ -5349,15 +5356,11 @@ void MainWindow::band_changed (Frequency f)
if ("FreqCal" == m_mode)
{
m_frequency_list_fcal_iter = m_config.frequencies ()->find (f);
setRig (f - ui->RxFreqSpinBox->value ());
}
else
{
float r=m_freqNominal/(f+0.0001);
if(r<0.9 or r>1.1) m_bVHFwarned=false;
setRig (f);
setXIT (ui->TxFreqSpinBox->value ());
}
float r=m_freqNominal/(f+0.0001);
if(r<0.9 or r>1.1) m_bVHFwarned=false;
setRig (f);
setXIT (ui->TxFreqSpinBox->value ());
if(monitor_off) monitor(false);
}
}
@ -6656,6 +6659,10 @@ void MainWindow::setRig (Frequency f)
m_freqTxNominal = m_freqNominal;
if (m_astroWidget) m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal);
}
if (m_mode == "FreqCal"
&& m_frequency_list_fcal_iter != m_config.frequencies ()->end ()) {
m_freqNominal = m_frequency_list_fcal_iter->frequency_ - ui->RxFreqSpinBox->value ();
}
if(m_transmitting && !m_config.tx_QSY_allowed ()) return;
if ((m_monitoring || m_transmitting) && m_config.transceiver_online ())
{
@ -6855,6 +6862,13 @@ void MainWindow::on_cbAutoSeq_toggled(bool b)
ui->cbFirst->setVisible((m_mode=="FT8") and b);
}
void MainWindow::on_measure_check_box_stateChanged (int state)
{
if ("FreqCal" == m_mode) {
m_config.enable_calibration (Qt::Checked != state);
}
}
void MainWindow::write_transmit_entry (QString const& file_name)
{
QFile f {m_config.writeable_data_dir ().absoluteFilePath (file_name)};

View File

@ -281,6 +281,7 @@ private slots:
void on_actionQRA64_triggered();
void on_actionFreqCal_triggered();
void splash_done ();
void on_measure_check_box_stateChanged (int);
private:
Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo,

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>815</width>
<height>548</height>
<height>555</height>
</rect>
</property>
<property name="windowTitle">
@ -21,7 +21,7 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0">
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0">
<layout class="QGridLayout" name="gridLayout" columnstretch="2,1">
<property name="horizontalSpacing">
<number>3</number>
</property>
@ -974,6 +974,16 @@ QLabel[oob=&quot;true&quot;] {
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="measure_check_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Check this to start recording calibration data.&lt;br/&gt;While measuring calibration correction is disabled.&lt;br/&gt;When not checked you can view the calibration results.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Measure</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0" colspan="2">