feat: Support all current timezones for GPS (#3406)

* Support all current timezones.

* Fix function name and calculation for timezone offset.

* Allow setting any time offset value from -12 to +14 in 15 minutes increments.

* Alternate timezone editor in Companion (from elecpower).

* Fix compiler warning.

* chore: Add the T20
This commit is contained in:
philmoz
2023-07-16 14:02:27 +10:00
committed by GitHub
parent 286ae00678
commit bd92f70056
27 changed files with 223 additions and 42 deletions
@@ -182,6 +182,7 @@ Node convert<GeneralSettings>::encode(const GeneralSettings& rhs)
node["hapticMode"] = rhs.hapticMode;
node["stickMode"] = rhs.stickMode;
node["timezone"] = rhs.timezone;
node["timezoneMinutes"] = rhs.timezoneMinutes;
node["adjustRTC"] = (int)rhs.adjustRTC;
node["inactivityTimer"] = rhs.inactivityTimer;
@@ -390,6 +391,7 @@ bool convert<GeneralSettings>::decode(const Node& node, GeneralSettings& rhs)
node["hapticMode"] >> rhs.hapticMode;
node["stickMode"] >> rhs.stickMode;
node["timezone"] >> rhs.timezone;
node["timezoneMinutes"] >> rhs.timezoneMinutes;
node["adjustRTC"] >> rhs.adjustRTC;
node["inactivityTimer"] >> rhs.inactivityTimer;
@@ -212,6 +212,7 @@ class GeneralSettings {
BeeperMode hapticMode;
unsigned int stickMode; // TODO enum
int timezone;
int timezoneMinutes;
bool adjustRTC;
bool optrexDisplay;
unsigned int inactivityTimer;
+3 -1
View File
@@ -1,4 +1,4 @@
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(generaledit_NAMES
generaledit
@@ -10,6 +10,7 @@ set(generaledit_SRCS
hardware.cpp
trainer.cpp
generaloptions.cpp
timezoneedit.cpp
)
set(generaledit_HDRS
@@ -17,6 +18,7 @@ set(generaledit_HDRS
hardware.h
trainer.h
generaloptions.h
timezoneedit.h
)
set(generaledit_UIS
+12 -7
View File
@@ -150,7 +150,8 @@ ui(new Ui::GeneralSetup)
}
ui->gpsFormatCB->setCurrentIndex(generalSettings.gpsFormat);
ui->timezoneSB->setValue(generalSettings.timezone);
ui->timezoneLE->setTime((generalSettings.timezone * 3600) + (generalSettings.timezoneMinutes/*quarter hours*/ * 15 * 60));
if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
ui->adjustRTC->setChecked(generalSettings.adjustRTC);
@@ -328,6 +329,16 @@ GeneralSetupPanel::~GeneralSetupPanel()
delete ui;
}
void GeneralSetupPanel::on_timezoneLE_textEdited(const QString &text)
{
if (!lock) {
int secs = ui->timezoneLE->timeInSeconds();
generalSettings.timezone = secs / 3600;
generalSettings.timezoneMinutes = (secs % 3600) / (15 * 60); // timezoneMinutes in quarter hours
emit modified();
}
}
void GeneralSetupPanel::populateBacklightCB()
{
QComboBox * b = ui->backlightswCB;
@@ -693,12 +704,6 @@ void GeneralSetupPanel::on_switchesDelay_valueChanged(int)
emit modified();
}
void GeneralSetupPanel::on_timezoneSB_editingFinished()
{
generalSettings.timezone = ui->timezoneSB->value();
emit modified();
}
void GeneralSetupPanel::on_adjustRTC_stateChanged(int)
{
generalSettings.adjustRTC = ui->adjustRTC->isChecked();
+1 -1
View File
@@ -50,7 +50,7 @@ class GeneralSetupPanel : public GeneralPanel
void on_faimode_CB_stateChanged(int );
void on_rotEncMode_CB_currentIndexChanged(int index);
void on_speakerPitchSB_editingFinished();
void on_timezoneSB_editingFinished();
void on_timezoneLE_textEdited(const QString &text);
void on_adjustRTC_stateChanged(int);
void on_hapticStrength_valueChanged();
void on_soundModeCB_currentIndexChanged(int index);
+9 -12
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>810</width>
<width>858</width>
<height>880</height>
</rect>
</property>
@@ -927,17 +927,7 @@
<item row="18" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="timezoneSB">
<property name="minimum">
<number>-14</number>
</property>
<property name="maximum">
<number>12</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
<widget class="TimezoneEdit" name="timezoneLE"/>
</item>
<item>
<widget class="QCheckBox" name="adjustRTC">
@@ -2501,6 +2491,13 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>TimezoneEdit</class>
<extends>QLineEdit</extends>
<header>timezoneedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,41 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "timezoneedit.h"
TimezoneEdit::TimezoneEdit(QWidget * parent) :
TimerEdit(parent)
{
setShowSeconds(false);
setMinimumTime(-12 * 60 * 60);
setMaximumTime(14 * 60 * 60);
setSingleStep(15 * 60);
setPageStep(60 * 60);
}
void TimezoneEdit::setupFormat()
{
QString inputRe = "^(?<pol>-|\\+|\\s)?(?<hrs>[0-9]*[0-9]):(?<mins>00|15|30|45)";
m_validator->setRegularExpression(QRegularExpression(inputRe));
setInputMask("#99:99");
setValidator(m_validator);
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include <TimerEdit>
class TimezoneEdit : public TimerEdit
{
Q_OBJECT
public:
explicit TimezoneEdit(QWidget * parent = Q_NULLPTR);
virtual ~TimezoneEdit() {}
protected:
void setupFormat() override;
};
+1 -1
View File
@@ -86,7 +86,7 @@ class TimerEdit : public QLineEdit
protected:
void textEditedHandler();
void setupFormat();
virtual void setupFormat();
void emitValueChanged();
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
+2 -1
View File
@@ -855,7 +855,8 @@ PACK(struct RadioData {
// Real attributes
NOBACKUP(uint8_t manuallyEdited:1);
NOBACKUP(int8_t spare0:7 SKIP);
int8_t timezoneMinutes:3; // -3 to +3 ==> (-45 to 45 minutes in 15 minute increments)
NOBACKUP(int8_t spare0:4 SKIP);
CUST_ATTR(semver,nullptr,w_semver);
CUST_ATTR(board,nullptr,w_board);
CalibData calib[MAX_CALIB_ANALOG_INPUTS] NO_IDX;
+12 -3
View File
@@ -599,9 +599,18 @@ void menuRadioSetup(event_t event)
#if defined(GPS)
case ITEM_RADIO_SETUP_TIMEZONE:
lcdDrawTextAlignedLeft(y, STR_TIMEZONE);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.timezone, attr|LEFT);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.timezone, -12, 12);
{
lcdDrawTextAlignedLeft(y, STR_TIMEZONE);
int tzIndex = timezoneIndex(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes);
lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, timezoneDisplay(tzIndex).c_str(), attr);
if (attr) {
tzIndex = checkIncDec(event, tzIndex, minTimezone(), maxTimezone(), EE_GENERAL);
if (checkIncDec_Ret) {
g_eeGeneral.timezone = timezoneHour(tzIndex);
g_eeGeneral.timezoneMinutes = timezoneMinute(tzIndex);
}
}
}
break;
case ITEM_RADIO_SETUP_ADJUST_RTC:
+12 -3
View File
@@ -547,9 +547,18 @@ void menuRadioSetup(event_t event)
break;
case ITEM_RADIO_SETUP_TIMEZONE:
lcdDrawText(INDENT_WIDTH, y, STR_TIMEZONE);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.timezone, attr|LEFT);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.timezone, -12, 12);
{
lcdDrawText(INDENT_WIDTH, y, STR_TIMEZONE);
int tzIndex = timezoneIndex(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes);
lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, timezoneDisplay(tzIndex).c_str(), attr);
if (attr) {
tzIndex = checkIncDec(event, tzIndex, minTimezone(), maxTimezone(), EE_GENERAL);
if (checkIncDec_Ret) {
g_eeGeneral.timezone = timezoneHour(tzIndex);
g_eeGeneral.timezoneMinutes = timezoneMinute(tzIndex);
}
}
}
break;
case ITEM_RADIO_SETUP_ADJUST_RTC:
+16 -1
View File
@@ -556,10 +556,22 @@ class GpsPage : public SubPage {
{
FlexGridLayout grid(col_two_dsc, row_dsc, 4);
tzIndex = timezoneIndex(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes);
auto line = body.newLine(&grid);
// Timezone
new StaticText(line, rect_t{}, STR_TIMEZONE, 0, COLOR_THEME_PRIMARY1);
new NumberEdit(line, rect_t{}, -12, 12, GET_SET_DEFAULT(g_eeGeneral.timezone));
auto tz = new NumberEdit(line, rect_t{}, minTimezone(), maxTimezone(),
GET_DEFAULT(tzIndex),
[=](int newTz) {
tzIndex = newTz;
g_eeGeneral.timezone = timezoneHour(newTz);
g_eeGeneral.timezoneMinutes = timezoneMinute(newTz);
SET_DIRTY();
});
tz->setDisplayHandler([](int32_t tz) {
return timezoneDisplay(tz);
});
line = body.newLine(&grid);
// Adjust RTC (from telemetry)
@@ -572,6 +584,9 @@ class GpsPage : public SubPage {
new Choice(line, rect_t{}, STR_GPSFORMAT, 0, 1, GET_SET_DEFAULT(g_eeGeneral.gpsFormat));
line = body.newLine(&grid);
}
protected:
int tzIndex;
};
class ViewOptionsPage : public Page
+1 -1
View File
@@ -471,7 +471,7 @@ uint8_t rtcAdjust(uint16_t year, uint8_t mon, uint8_t day, uint8_t hour, uint8_t
t.tm_hour = hour;
t.tm_min = min;
t.tm_sec = sec;
gtime_t newTime = gmktime(&t) + g_eeGeneral.timezone * 3600;
gtime_t newTime = gmktime(&t) + timezoneOffsetSeconds(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes);
gtime_t diff = (g_rtcTime > newTime) ? (g_rtcTime - newTime) : (newTime - g_rtcTime);
#if defined(DEBUG) && defined (PCBTARANIS)
@@ -263,7 +263,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
@@ -293,7 +293,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 22, struct_CalibData, NULL),
@@ -263,7 +263,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
@@ -263,7 +263,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
@@ -301,7 +301,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 22, struct_CalibData, NULL),
@@ -301,7 +301,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 22, struct_CalibData, NULL),
@@ -263,7 +263,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
@@ -263,7 +263,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
@@ -263,7 +263,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
@@ -265,7 +265,8 @@ static const struct YamlNode struct_CustomFunctionData[] = {
};
static const struct YamlNode struct_RadioData[] = {
YAML_UNSIGNED( "manuallyEdited", 1 ),
YAML_PADDING( 7 ),
YAML_SIGNED( "timezoneMinutes", 3 ),
YAML_PADDING( 4 ),
YAML_CUSTOM("semver",nullptr,w_semver),
YAML_CUSTOM("board",nullptr,w_board),
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
+44
View File
@@ -1045,3 +1045,47 @@ char *strAppendDate(char *str, bool time)
#if !defined(BOOT)
#endif
// Manage timezones
// For backward compatibility timezone is stored as two separate values:
// timezone = hour value
// timezoneMinutes - minute value / 15
int8_t minTimezone()
{
return -12 * 4;
}
int8_t maxTimezone()
{
return 14 * 4;
}
std::string timezoneDisplay(int tz)
{
char s[16];
int h = abs(tz / 4);
int m = abs(tz % 4) * 15;
sprintf(s,"%s%d:%02d", (tz < 0) ? "-" : "", h, m);
return std::string(s);
}
int timezoneIndex(int8_t tzHour, int8_t tzMinute)
{
return (tzHour * 4) + tzMinute;
}
int8_t timezoneHour(int tz)
{
return tz / 4;
}
int8_t timezoneMinute(int tz)
{
return tz % 4;
}
int timezoneOffsetSeconds(int8_t tzHour, int8_t tzMinute)
{
return (tzHour * 3600) + (tzMinute * 15 * 60);
}
+9
View File
@@ -174,4 +174,13 @@ int strncasecmp(char (&s1)[L1], const char *const s2)
std::string getValueWithUnit(int val, uint8_t unit, LcdFlags flags);
std::string getGVarValue(uint8_t gvar, gvar_t value, LcdFlags flags);
// Timezone handling
extern int8_t minTimezone();
extern int8_t maxTimezone();
extern std::string timezoneDisplay(int tz);
extern int timezoneIndex(int8_t tzHour, int8_t tzMinute);
extern int8_t timezoneHour(int tz);
extern int8_t timezoneMinute(int tz);
extern int timezoneOffsetSeconds(int8_t tzHour, int8_t tzMinute);
#endif // _STRHELPERS_H_
+1 -1
View File
@@ -934,7 +934,7 @@ static void adjustTimeFromUTC(uint8_t hour, uint8_t min, uint8_t sec,
struct gtm *tp)
{
// Get current UTC date/time
__offtime(&g_rtcTime, -g_eeGeneral.timezone * 3600, tp);
__offtime(&g_rtcTime, -timezoneOffsetSeconds(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes), tp);
tp->tm_hour = hour;
tp->tm_min = min;