Files
banban/ql-application/threadx/interface/custom_app/app_time_calibrate.c
T

227 lines
9.1 KiB
C

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include "ql_rtos.h"
#include "ql_func.h"
#include "ql_nw.h"
#include "ql_rtc.h"
#include "app_time_calibrate.h"
/* SDK wrapper uses the same PMIC timezone as time(); no libc TZ assumptions. */
extern struct tm *ql_localtime_r(const time_t *, struct tm *);
#define MIN_EPOCH 1704067200LL
#define MAX_EPOCH 2147483647LL
#define ATTEMPTS 3
static ql_task_t calibration_task;
static int calibration_started;
typedef struct {
time_t utc;
unsigned int ticks;
int zone_quarters;
} nitz_sample;
static int leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
/* Explicit Gregorian conversion: no mktime() or APP clock dependency. */
static int calendar_epoch(int year, int month, int day, int hour, int minute,
int second, long long *result)
{
static const int days[] = {31,28,31,30,31,30,31,31,30,31,30,31};
long long total = 0;
int y, m;
if (year < 1970 || year > 2038 || month < 1 || month > 12 ||
day < 1 || day > days[month - 1] + (month == 2 && leap(year)) ||
hour < 0 || hour > 23 || minute < 0 || minute > 59 ||
second < 0 || second > 59) return -1;
for (y = 1970; y < year; ++y) total += 365 + leap(y);
for (m = 1; m < month; ++m) total += days[m - 1] + (m == 2 && leap(year));
*result = ((total + day - 1) * 24 + hour) * 3600 + minute * 60 + second;
return 0;
}
static int two_digits(const char *s)
{
if (s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9') return -1;
return (s[0] - '0') * 10 + s[1] - '0';
}
/* Accept actual "YY/MM/DD HH:MM:SS 32 0", documented signed format, and compact
* "YY/MM/DD,HH:MM:SS+32". NITZ UT is UTC; zone is metadata, not subtracted.
* abs_time/leap_sec have insufficient documented semantics to use as epoch. */
static int parse_nitz(const char *s, time_t *epoch, int *zone)
{
size_t n = strlen(s), pos = 17;
int yy, mo, dd, hh, mm, ss, q, dst, negative = 0;
size_t start;
long long value;
if (n < 20 || s[2] != '/' || s[5] != '/' ||
(s[8] != ' ' && s[8] != ',') || s[11] != ':' || s[14] != ':') return -1;
yy = two_digits(s); mo = two_digits(s + 3); dd = two_digits(s + 6);
hh = two_digits(s + 9); mm = two_digits(s + 12); ss = two_digits(s + 15);
if (yy < 0 || calendar_epoch(2000 + yy, mo, dd, hh, mm, ss, &value) != 0 ||
value < MIN_EPOCH || value > MAX_EPOCH) return -2;
/* Actual SDK output is also "26/10/26 08:00:10 32 0". */
if (s[pos] == ' ') ++pos;
else if (s[pos] != '+' && s[pos] != '-') return -3;
if (s[pos] == '+' || s[pos] == '-') negative = s[pos++] == '-';
start = pos;
q = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9' && pos - start < 2)
q = q * 10 + s[pos++] - '0';
if (pos == start || q > 56 || (negative && q > 48)) return -3;
*zone = negative ? -q : q;
if (pos < n) {
if (s[pos++] != ' ') return -4;
start = pos;
dst = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9' && pos - start < 2)
dst = dst * 10 + s[pos++] - '0';
if (pos == start || pos != n || dst > 2) return -4;
}
*epoch = (time_t)value;
return 0;
}
static int get_sample(int attempt, int index, nitz_sample *sample)
{
QL_NW_NITZ_TIME_INFO_T info;
unsigned int began = ql_rtos_get_systicks();
int rc, parsed;
memset(&info, 0, sizeof(info));
rc = ql_nw_get_nitz_time_info(&info);
sample->ticks = ql_rtos_get_systicks();
printf("[APP-TIME] sample attempt=%d index=%d rc=%d raw=\"%.*s\" "
"abs_time=%lu leap_sec=%u query_ms=%lu\r\n", attempt, index, rc,
(int)sizeof(info.nitz_time), info.nitz_time, info.abs_time,
(unsigned int)info.leap_sec,
(unsigned long)(sample->ticks - began) * 5UL);
if (rc != QL_NW_SUCCESS ||
memchr(info.nitz_time, '\0', sizeof(info.nitz_time)) == NULL) return -1;
parsed = parse_nitz(info.nitz_time, &sample->utc, &sample->zone_quarters);
if (parsed != 0) {
printf("[APP-TIME] rejected stage=parse reason=%d\r\n", parsed);
return -1;
}
/* Bound query delay; this does not establish the age of a cached NITZ. */
if (sample->ticks - began > 400U) {
printf("[APP-TIME] rejected stage=slow_query rtc_not_written=1\r\n");
return -1;
}
printf("[APP-TIME] parsed utc=%ld zone_quarters=%d zone_minutes=%d\r\n",
(long)sample->utc, sample->zone_quarters, sample->zone_quarters * 15);
return 0;
}
static int write_and_verify(const nitz_sample *reference)
{
struct tm local;
ql_rtc_time_t target, actual;
time_t epoch, before = time(NULL), after;
long long expected, local_epoch, delta, actual_local = 0;
int rc, read_rc, calendar_ok;
expected = (long long)reference->utc +
(ql_rtos_get_systicks() - reference->ticks) / 200U;
if (expected > MAX_EPOCH) {
printf("[APP-TIME] FAIL stage=epoch_range\r\n");
return -1;
}
epoch = (time_t)expected;
memset(&local, 0, sizeof(local));
if (ql_localtime_r(&epoch, &local) == NULL ||
calendar_epoch(local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
local.tm_hour, local.tm_min, local.tm_sec, &local_epoch) != 0 ||
local_epoch < MIN_EPOCH || local_epoch > MAX_EPOCH ||
local_epoch - expected < -43200 || local_epoch - expected > 50400) {
printf("[APP-TIME] FAIL stage=local_calendar\r\n");
return -1;
}
memset(&target, 0, sizeof(target));
target.tm_year = local.tm_year + 1900;
target.tm_mon = local.tm_mon + 1;
target.tm_mday = local.tm_mday;
target.tm_hour = local.tm_hour;
target.tm_min = local.tm_min;
target.tm_sec = local.tm_sec;
target.tm_wday = local.tm_wday;
printf("[APP-TIME] set_begin app_before=%ld expected_utc=%ld "
"pmic_zone_minutes=%ld app_calendar=%04d-%02d-%02dT%02d:%02d:%02d\r\n",
(long)before, (long)epoch, (long)((local_epoch - expected) / 60),
target.tm_year, target.tm_mon, target.tm_mday,
target.tm_hour, target.tm_min, target.tm_sec);
rc = ql_rtc_set_time(&target);
memset(&actual, 0, sizeof(actual));
read_rc = ql_rtc_get_time(&actual);
after = time(NULL);
expected = (long long)reference->utc +
(ql_rtos_get_systicks() - reference->ticks) / 200U;
delta = (long long)after - expected;
calendar_ok = calendar_epoch(actual.tm_year, actual.tm_mon, actual.tm_mday,
actual.tm_hour, actual.tm_min, actual.tm_sec,
&actual_local) == 0;
actual_local -= local_epoch + (expected - (long long)epoch);
printf("[APP-TIME] readback set_rc=%d rtc_rc=%d app_after=%ld "
"expected_utc=%lld delta_s=%lld calendar=%04d-%02d-%02dT%02d:%02d:%02d\r\n",
rc, read_rc, (long)after, expected, delta, actual.tm_year, actual.tm_mon,
actual.tm_mday, actual.tm_hour, actual.tm_min, actual.tm_sec);
if (rc != 0 || read_rc != 0 || after < MIN_EPOCH ||
delta < -3 || delta > 3 || !calendar_ok ||
actual_local < -3 || actual_local > 3) {
printf("[APP-TIME] FAIL stage=app_clock_verification\r\n");
return -1;
}
printf("[APP-TIME] PASS source=nitz app_utc=verified "
"wss_requires_separate_handshake_check=1\r\n");
return 0;
}
static void time_calibrate_worker(void *arg)
{
nitz_sample sample;
int attempt;
(void)arg;
printf("[APP-TIME] begin version=1 source=operator_nitz ntp=off "
"policy=first_valid_after_pdp sys_probe=off\r\n");
if (!m_get_api_ptr || !m_get_api_ptr("ql_nw_get_nitz_time_info") ||
!m_get_api_ptr("ql_localtime_r") || !m_get_api_ptr("ql_rtc_set_time") ||
!m_get_api_ptr("ql_rtc_get_time")) {
printf("[APP-TIME] FAIL stage=sdk_api_unavailable\r\n");
goto done;
}
/* Use the first valid operator snapshot promptly after
* PDP activation. A snapshot need not advance between API reads. Its actual
* network reception time is unknown; readback alone cannot establish age. */
for (attempt = 1; attempt <= ATTEMPTS; ++attempt) {
if (get_sample(attempt, 1, &sample) == 0) {
printf("[APP-TIME] snapshot_selected attempt=%d utc=%ld "
"age=unknown first_valid_after_pdp=1 max_rtc_writes=1\r\n",
attempt, (long)sample.utc);
write_and_verify(&sample);
goto done; /* No repeated write, even on setter/readback failure. */
}
if (attempt < ATTEMPTS) ql_rtos_task_sleep_ms(5000);
}
printf("[APP-TIME] FAIL stage=no_valid_nitz rtc_not_written=1\r\n");
done:
printf("[APP-TIME] finished once_per_boot=1\r\n");
ql_rtos_task_delete(NULL);
}
void app_time_calibrate_start(void)
{
int rc;
if (calibration_started) return; /* PDP management is the sole caller. */
calibration_started = 1;
rc = ql_rtos_task_create(&calibration_task, 6 * 1024, 110, "app_time_calibrate",
time_calibrate_worker, NULL);
if (rc != 0) {
calibration_started = 0;
printf("[APP-TIME] FAIL stage=create_task rc=%d\r\n", rc);
}
}