Rig frequency calibration

The   settings   are  a   linear   calibration   correction  for   the
rig. Calibration constants are supplied as an intercept error in Hertz
and a slope error in parts per million.

The correction is  applied only to the final frequencies  going to and
from  the radio.   This means  that  any transverter  offsets are  not
corrected. This is  appropriate as each transverter will  have its own
error factors.  Transvertor and  other individual band specific errors
can only be corrected by adjusting the offset for the band.

Thanks  to Mike  W9MDB for  an  initial implementation  of Joe  K1JT's
suggestion. See also the WSPR documentation for details of calculating
the required calibration constants.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@5396 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2015-05-19 17:52:47 +00:00
parent 2d3bc88078
commit b0d8f5ee1f
3 changed files with 148 additions and 23 deletions

View File

@ -132,6 +132,7 @@
#include <algorithm>
#include <functional>
#include <limits>
#include <cmath>
#include <QApplication>
#include <QMetaType>
@ -384,6 +385,8 @@ private:
bool validate ();
void message_box (QString const& reason, QString const& detail = QString ());
void fill_port_combo_box (QComboBox *);
Frequency apply_calibration (Frequency) const;
Frequency remove_calibration (Frequency) const;
Q_SLOT void on_font_push_button_clicked ();
Q_SLOT void on_decoded_text_font_push_button_clicked ();
@ -488,6 +491,8 @@ private:
bool have_rig_;
bool rig_changed_;
TransceiverState cached_rig_state_;
double frequency_calibration_intercept_;
double frequency_calibration_slope_ppm_;
// the following members are required to get the rig into split the
// first time monitor or tune or Tx occur
@ -503,7 +508,6 @@ private:
// no split.
bool enforce_mode_and_split_;
FrequencyDelta transceiver_offset_;
// configuration fields that we publish
QString my_callsign_;
@ -716,7 +720,6 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
, setup_split_ {false}
, required_tx_frequency_ {0}
, enforce_mode_and_split_ {false}
, transceiver_offset_ {0}
, default_audio_input_device_selected_ {false}
, default_audio_output_device_selected_ {false}
{
@ -1019,6 +1022,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_);
if (rig_params_.ptt_port.isEmpty ())
{
@ -1202,6 +1207,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 ();
}
void Configuration::impl::write_settings ()
@ -1286,6 +1293,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_);
}
void Configuration::impl::set_rig_invariants ()
@ -1636,6 +1645,8 @@ void Configuration::impl::accept ()
save_directory_ = ui_->save_path_display_label->text ();
enable_VHF_features_ = ui_->enable_VHF_features_check_box->isChecked ();
decode_at_52s_ = ui_->decode_at_52s_check_box->isChecked ();
frequency_calibration_intercept_ = ui_->calibration_intercept_spin_box->value ();
frequency_calibration_slope_ppm_ = ui_->calibration_slope_ppm_spin_box->value ();
auto new_server = ui_->udp_server_line_edit->text ();
if (new_server != udp_server_name_)
@ -2091,9 +2102,8 @@ void Configuration::impl::transceiver_frequency (Frequency f)
cached_rig_state_.frequency (f);
cached_rig_state_.mode (mode);
// lookup offset
transceiver_offset_ = stations_.offset (f);
Q_EMIT frequency (f + transceiver_offset_, mode);
// apply any offset & calibration
Q_EMIT frequency (apply_calibration (f + stations_.offset (f)), mode);
}
}
@ -2101,16 +2111,17 @@ void Configuration::impl::transceiver_tx_frequency (Frequency f)
{
if (/* set_mode () || */ cached_rig_state_.tx_frequency () != f || cached_rig_state_.split () != !!f)
{
cached_rig_state_.tx_frequency (f);
cached_rig_state_.split (f);
cached_rig_state_.tx_frequency (f);
// lookup offset if we are in split mode
if (f)
// lookup offset and apply calibration if we are in split mode
if (cached_rig_state_.split ())
{
transceiver_offset_ = stations_.offset (f);
f += transceiver_offset_;
// apply and offset and calibration
f = apply_calibration (f + stations_.offset (f));
}
// Rationalise TX VFO mode if we ask for split and are
// responsible for mode.
Q_EMIT tx_frequency (f, cached_rig_state_.split ()
@ -2217,15 +2228,17 @@ void Configuration::impl::handle_transceiver_update (TransceiverState state)
close_rig ();
}
cached_rig_state_ = state;
// take off calibration & offset
state.frequency (remove_calibration (state.frequency ()) - stations_.offset (state.frequency ()));
// take off offset
cached_rig_state_.frequency (cached_rig_state_.frequency () - transceiver_offset_);
if (cached_rig_state_.tx_frequency ())
if (state.tx_frequency ())
{
cached_rig_state_.tx_frequency (cached_rig_state_.tx_frequency () - transceiver_offset_);
// take off calibration & offset
state.tx_frequency (remove_calibration (state.tx_frequency ()) - stations_.offset (state.tx_frequency ()));
}
cached_rig_state_ = state;
// pass on to clients
Q_EMIT self_->transceiver_update (cached_rig_state_);
}
@ -2408,6 +2421,17 @@ void Configuration::impl::fill_port_combo_box (QComboBox * cb)
cb->setEditText (current_text);
}
auto Configuration::impl::apply_calibration (Frequency f) const -> Frequency
{
return std::llround (frequency_calibration_intercept_
+ (1. + frequency_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 !defined (QT_NO_DEBUG_STREAM)
ENUM_QDEBUG_OPS_IMPL (Configuration, DataMode);

View File

@ -1807,8 +1807,8 @@ for assessing propagation and system performance.</string>
<property name="title">
<string>Working Frequencies</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_15">
<item row="0" column="0" rowspan="2">
<widget class="QTableView" name="frequencies_table_view">
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
@ -1836,7 +1836,89 @@ for assessing propagation and system performance.</string>
</attribute>
</widget>
</item>
<item>
<item row="0" column="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="toolTip">
<string>See WSPR documentattion Appendix C for details of how to determine these factors for your radio.</string>
</property>
<property name="title">
<string>Frequency Calibration</string>
</property>
<layout class="QFormLayout" name="formLayout_7">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Intercept:</string>
</property>
<property name="buddy">
<cstring>calibration_intercept_spin_box</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="calibration_intercept_spin_box">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> Hz</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>-9999.989999999999782</double>
</property>
<property name="maximum">
<double>9999.989999999999782</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Slope:</string>
</property>
<property name="buddy">
<cstring>calibration_slope_ppm_spin_box</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="calibration_slope_ppm_spin_box">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> ppm</string>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-9.999900000000000</double>
</property>
<property name="maximum">
<double>9.999900000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -1849,6 +1931,19 @@ for assessing propagation and system performance.</string>
</property>
</spacer>
</item>
<item row="1" column="2">
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
@ -2122,7 +2217,9 @@ soundcard changes</string>
<tabstop>quick_call_check_box</tabstop>
<tabstop>tx_QSY_check_box</tabstop>
<tabstop>disable_TX_on_73_check_box</tabstop>
<tabstop>enable_VHF_features_check_box</tabstop>
<tabstop>watchdog_check_box</tabstop>
<tabstop>decode_at_52s_check_box</tabstop>
<tabstop>CW_id_after_73_check_box</tabstop>
<tabstop>CW_id_interval_spin_box</tabstop>
<tabstop>rig_combo_box</tabstop>
@ -2136,11 +2233,12 @@ soundcard changes</string>
<tabstop>CAT_handshake_none_radio_button</tabstop>
<tabstop>CAT_handshake_xon_radio_button</tabstop>
<tabstop>CAT_handshake_hardware_radio_button</tabstop>
<tabstop>CAT_control_lines_group_box</tabstop>
<tabstop>CAT_DTR_check_box</tabstop>
<tabstop>CAT_RTS_check_box</tabstop>
<tabstop>PTT_VOX_radio_button</tabstop>
<tabstop>PTT_DTR_radio_button</tabstop>
<tabstop>PTT_CAT_radio_button</tabstop>
<tabstop>PTT_DTR_radio_button</tabstop>
<tabstop>PTT_RTS_radio_button</tabstop>
<tabstop>PTT_port_combo_box</tabstop>
<tabstop>TX_source_data_radio_button</tabstop>
@ -2176,6 +2274,8 @@ soundcard changes</string>
<tabstop>udpWindowToFront</tabstop>
<tabstop>udpWindowRestore</tabstop>
<tabstop>frequencies_table_view</tabstop>
<tabstop>calibration_intercept_spin_box</tabstop>
<tabstop>calibration_slope_ppm_spin_box</tabstop>
<tabstop>stations_table_view</tabstop>
<tabstop>pbCQmsg</tabstop>
<tabstop>pbMyCall</tabstop>
@ -2251,12 +2351,12 @@ soundcard changes</string>
</connection>
</connections>
<buttongroups>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_mode_button_group"/>
</buttongroups>
</ui>

View File

@ -72,6 +72,7 @@ public:
//
// Aggregation of all of the rig and PTT state accessible via this
// interface.
//
class TransceiverState
{
public: