Timestamp چیست و چه کاربردهایی دارد بههمراه سورس و مثال
فرضکنید شما قراره زمان را توسط یک ماژول GPS و ارتباط سریال با پروتکل NMEA دریافتکنید. همچنین لازمه در فاز بعدی اون را ذخیرهکنید و توسط مثلا ارتباط GPRS اون را ارسالکنید و حتی برخی محاسبات روی اون هم انجام بدید، مثلا اون را محلیکنید(همانطورکه میدونید زمان ارسالشده GPS براساس UTC هست) و شایدم برخی محاسبات روی اون انجامبدید. زمان را میتوان یکیاز مهمترین دادهها در سازوکار نرمافزارها و سختافزارها نامید، بههمینسبب نحوه ذخیرهسازی و اعمال برخیاز تغییرات و محاسبات نیز پراهمیت هست. وقتی مهندسین الکترونیک قراره با زمان کار کنند چون منابع سختافزاری بسیارمحدود میشه لازمه بهترین روش برای مواردبالا درنظرگرفتهبشه. اکثر پروژههای الکترونیکی نیازبه محاسبات زمان دارند پروژههایی که دغدغههای زیر را هم دارند:
- ذخیره زمان
- تغییر در زمان،مثلا محلیکردن زمان در زمان
- مقایسه و یا برخی اعمالمحاسبات ریاضی برروی زمان
- انتقالزمان از بسترهای مختلف مانند GPRS
خب برمیگردیم به مثال اولمون… قسمتیاز پکت NMEA را که شامل اطلاعات زمان هست در زیر میبنید.
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
بعداز اولین کاما ۱۲۳۵۱۹ساعت GPS را شما مشاهده میکنید و بعداز ۷امین کاما شما تاریخ ۲۳۰۳۱۷ را مشاهده میکنید. بهعبارتی ساعت ۱۲:۳۵:۱۹ و تاریخ ۲۳/۰۳/۲۱۷ میباشد. حال فرضکنید قرار هست ما این زمان که براساس UTC هست را به ساعت محلی ایران تبدیلکنیم، ساعت محلی ایران در ۶ماه اول سال +۳:۳۰ میباشد و در۶ماه آخر سال +۴:۳۰ میباشد. ما ۱۲بایت کاراکتر اسکی داریم که اگر بخواهیم تبدیلکنیم به حالتعددی میشه ۶بایت اطلاعات که البته هرکدام از بایتها محدودیت هم دارند(مثلا ماه میتونه بین ۱ تا ۱۲ باشه) و صدالبته هرنوع اضافهوکمکردن زمان به این مقادیر (مثل محلیکردن اطلاعات) میتونه توابع خاص خودش را داشتهباشه. الان چطور میتونیم زمان را چهار و ساعت و نیم افزایش بدیم و آنرا به زمان محلی ایران تبدیلکنیم؟ شاید راهاولی که به ذهن خیلیهای میرسه اینهکه یک تابع بنویسیم و در اون بیاییم ابتدا ساعت و دقیقهها را افزایشبدیم! خب باید پس یکسری شرط بنویسیم که اگر بعداز افزایش ساعت و دقیقه روز را هم اضافهکنیم و اگر روز را هم اضافهکردیم باید حواسمونباشه شاید مجبوربشیم ماه را اضافهکنیم! و اگر ماه را اضافهکردیم بایدحواسمون به این باشه که برخیاز ماهها تعداد روزهاش متفاوته و امکانداره حتی به افزایش سال هم برسیم… با کمی نگاه دقیقتر بهنظر بهتره که زمان را به یک عدد تبدیلکنیم و در یک تابع دیگه بتونیم اون عدد را مجددا به تاریخ تبدیلکنیم که البته اون هم تابع خیلی سادهای نداره. صدالبته این پروژه قبلا انجامشده و تحتعنوان Unix Timestamp شناختهمیشه. ساختاری که جهت ذخیرهکردن زمان و تاریخ در سیستمهای یونیکس استفادهشده و میشه. با تبدیل این اعداد به Timestamp شما علنا ۴بایت را اشغال میکنید که هم حافظه کمتری اشغال میکنه و هم محاسبات برروی آن راحتتر هست. البته تنها مشکل این نوع نگهداری زمان بحث ناخوانابودن ساعت در یک نگاه میباشد و لازماست برای تبدیلبه زمان قابلدرک برای ما یک محاسبه ریاضی انجامشود. نوع تبدیل هم بسیارساده هست این عدد از تاریخ ۱/۱/۱۹۷۰ شروعشده و بهازای هرثانیه، عدد مربوطه بعلاوه یک میشود. در وب سایت www.unixtimestamp.com میتوانید بصورت آنلاین تبدیل را انجامدهید.
نحوه استفادهاز توابع Timestamp در میکروکنترلر
بهنظر هویت Timestampe مشخصشده ما برای این سیستم الان به دو تا تابع نیاز داریم: تبدیل ساعت، دقیقه، ثانیه، ماه، روز، سال به TimeStamp و بالعکس. در زیر سورس این دو تابع را میتونید ببنید که برایاینکه مشخصبشه توابع چطور کار میکنن ما یک پروژه کوچک با کدویژن Codevision انجامدادیم و با شبیهساز پروتئوس نیز اون را شبیهسازی کردیم که در زیر میتونید هم سورس کدویژن و هم Proteus را دانلود کنید و در پروژههای خودتون استفادهکنید.
typedef struct _tm_struct { unsigned char tm_sec; /* seconds after the minute - [0,59] */ unsigned char tm_min; /* minutes after the hour - [0,59] */ unsigned char tm_hour; /* hours since midnight - [0,23] */ unsigned char tm_mday; /* day of the month - [1,31] */ unsigned char tm_mon; /* months since January - [0,11] */ unsigned int tm_year; /* years since 1900 */ unsigned char tm_wday; /* days since Sunday - [0,6] */ unsigned char tm_yday; /* days since January 1 - [0,365] */ unsigned char tm_isdst; /* daylight savings time flag */ // NOT USED }tm_struct; // this array represents the number of days in one non-leap year at // the beginning of each month unsigned long DaysToMonth[13] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 }; unsigned long DateToUnixTimestamp(tm_struct *datetime) { unsigned long iday; unsigned long val; iday = (unsigned long)365 * (datetime->tm_year - 70) + DaysToMonth[datetime->tm_mon-1] + (datetime->tm_mday - 1); iday = iday + (datetime->tm_year - 69) / 4; if ((datetime->tm_mon > 2) && ((datetime->tm_year % 4) == 0)) { iday++; } val = (unsigned long) datetime->tm_sec + 60 *(unsigned long) datetime->tm_min + 3600 * (datetime->tm_hour + 24 * iday); return val; } void UnixTimestampToDate(unsigned long binary,tm_struct *datetime) { unsigned long hour; unsigned long day; unsigned long minute; unsigned long second; unsigned long month; unsigned long year; unsigned long whole_minutes; unsigned long whole_hours; unsigned long whole_days; unsigned long whole_days_since_1968; unsigned long leap_year_periods; unsigned long days_since_current_lyear; unsigned long whole_years; unsigned long days_since_first_of_year; unsigned long days_to_month; unsigned long day_of_week; whole_minutes = binary / 60; second = binary - (60 * whole_minutes); // leftover seconds whole_hours = whole_minutes / 60; minute = whole_minutes - (60 * whole_hours); // leftover minutes whole_days = whole_hours / 24; hour = whole_hours - (24 * whole_days); // leftover hours whole_days_since_1968 = whole_days + 365 + 366; leap_year_periods = whole_days_since_1968 / ((4 * 365) + 1); days_since_current_lyear = whole_days_since_1968 % ((4 * 365) + 1); // if days are after a current leap year then add a leap year period if ((days_since_current_lyear >= (31 + 29))) { leap_year_periods++; } whole_years = (whole_days_since_1968 - leap_year_periods) / 365; days_since_first_of_year = whole_days_since_1968 - (whole_years * 365) - leap_year_periods; if ((days_since_current_lyear <= 365) && (days_since_current_lyear >= 60)) { days_since_first_of_year++; } year = whole_years + 68; month = 13; days_to_month = 366; while (days_since_first_of_year < days_to_month) { month--; days_to_month = DaysToMonth[month-1]; if ((month > 2) && ((year % 4) == 0)) { days_to_month++; } } day = days_since_first_of_year - days_to_month + 1; day_of_week = (whole_days + 4) % 7; datetime->tm_yday = days_since_first_of_year; /* days since January 1 - [0,365] */ datetime->tm_sec = second; /* seconds after the minute - [0,59] */ datetime->tm_min = minute; /* minutes after the hour - [0,59] */ datetime->tm_hour = hour; /* hours since midnight - [0,23] */ datetime->tm_mday = day; /* day of the month - [1,31] */ datetime->tm_wday = day_of_week; /* days since Sunday - [0,6] */ datetime->tm_mon = month; /* months since January - [0,11] */ datetime->tm_year = year; /* years since 1900 */ }
در زیر هم میتونید تابع Main برنامه را ببنید که در این تابع ما یک تاریخ را تبدیلبه Timestamp میکنیم سپس اون عدد را یکبار دیگه تبدیلبه تاریخ میکنیم و نمایشمیدیم و در فازبعدی سعی میکنیم اون زمان را +۴.۳۰ به جلو بکشیم(اصطلاحا زمان را محلیکنیم) و دوباره زمان جدید را مشاهدهکنیم.
tm_struct timeStruct; unsigned long UnixTimestamp; char Buf[50]; Port_Init(); putsf("Sisoog.com \n\r"); printf("------------\n\r"); putsf("Time =21:32:53 21/6/2018 \n\r"); timeStruct.tm_hour = 21; timeStruct.tm_min = 32; timeStruct.tm_sec = 53; timeStruct.tm_year = 118; timeStruct.tm_mon = 6; timeStruct.tm_mday = 21; UnixTimestamp = DateToUnixTimestamp(&timeStruct); ltoa(UnixTimestamp,Buf); printf("TimeStamp=");puts(Buf); printf("\n\r"); UnixTimestampToDate(UnixTimestamp,&timeStruct); sprintf(Buf,"TimeStruct -> %d:%d:%d, %d/%d/%d \n\r",timeStruct.tm_hour,timeStruct.tm_min,timeStruct.tm_sec, timeStruct.tm_mday,timeStruct.tm_mon,timeStruct.tm_year+1900); puts(Buf); printf("------------\n\r"); printf("Local Time +4.5 \n\r"); UnixTimestamp+=(4*60+30)*60; //Local Time +4.5 UnixTimestampToDate(UnixTimestamp,&timeStruct); sprintf(Buf,"TimeStruct -> %d:%d:%d, %d/%d/%d +4.5 \n\r",timeStruct.tm_hour,timeStruct.tm_min,timeStruct.tm_sec, timeStruct.tm_mday,timeStruct.tm_mon,timeStruct.tm_year+1900); puts(Buf);
در زیر نیز خروجی تابع را میتونید مشاهدهکنید:
فایلهای دانلودی موردنیاز
دانلود سورسکد کدویژن و پروتئوس: دانلود سورسکدویژن – تبدیل و مثال Timestamp
منبع: سیسوگ