פרויקט קורונה: סגר-מטר

כשהממשלה אמרה לנו לא להתרחק יותר ממאה, חמש-מאות או אלף מטרים מהבית, רבים שאלו "איך אני אדע איפה זה", ורבים ענו להם "יש אפליקציה". אבל אנחנו מייקרים, ויש לנו דרכים יותר מגניבות לפתור את הבעיה, נכון? הנה פרויקט לדוגמה, שמבוסס על לוח BBC Micro:bit, מודול GPS קטנטן ותוכנה שנכתבה ב-MicroPython… לצערי!

הסגר-מטר בפעולה (המספר 11 חולף בתצוגה)
הסגר-מטר בפעולה (המספר 11 חולף בתצוגה)

המטרה, בגדול, היא ליצור התקן שיעזור לנו להסתובב ברחובות בלי להתרחק בטעות יותר מדי ממקום המגורים. כשמתחילים להיכנס לפרטים מגלים הרבה מאוד אי-בהירויות בקטע הזה. למשל, איך מגדירים את נקודת המוצא שנקראת "מקום המגורים"? האם זה שער הכניסה לחצר, מרכז המאסה של הבניין, חדר השינה או מה? האם חישוב המרחק צריך לקחת בחשבון גם את ממד הגובה, ואת כּדוּריוּת הפלנטה? כדי לא להתחרפן, נלך על הכי פשוט שאפשר ונניח שהכוונה למרחק בקו ישר מנקודה שאנחנו מחליטים, בהנחה שכדור הארץ שטוח (בכל זאת, מדובר על טווח של עד קילומטר בלבד, אז זה נכון בקירוב) ובהתעלם מממד הגובה.

בכל רגע נתון יהיו לנו קואורדינטות GPS של נקודת המוצא שהגדרנו מראש, ואם יש קליטה מהלוויינים, גם של המיקום הנוכחי. תחת הנחת העולם השטוח, אפשר לחשב מהנתונים האלה מרחק לפי משפט פיתגורס, אם רק נדע מה פקטור ההמרה מיחידות GPS למטרים. לשם כך, צריך קודם כל להמיר את פורמט המעלות-דקות ("DMM") המעצבן שמודול ה-GPS שלי נותן (מספרים שיכולים להיראות, למשל, ככה: "3211.30488") לפורמט מעלות עשרוניות ("DD"). הנה פונקציית פייתון שעושה את זה, תוך התעלמות מכל הנושא של צפון/דרום או מזרח/מערב, שלא ממש קריטי לנו בתוך גבולות ישראל:

def GPS_DMM_to_DD(GPS_DMM):
    c_deg = GPS_DMM // 100
    c_min = GPS_DMM - (c_deg * 100)
    return c_deg + (c_min / 60)

יש מלכודת נוספת כאן, והיא שהיחס בין ערכי ה-GPS למטרים אינו קבוע על פני כל הפלנטה. לדוגמה, הבדל של מעלה אחת בקווי אורך, באזור קו המשווה, מייצג מרחק הרבה יותר גדול פיזית מאשר אותו הבדל באזור קרוב לקטבים. ישראל די רחוקה מהקטבים אמנם, אך בכל זאת אי אפשר להמיר למטרים, עם אותה נוסחה בדיוק, את הפרשי המעלות בקווי רוחב ובקווי אורך. הטעות תהיה משמעותית.

אחרי קצת משחקים עם Google maps ומחשבוני המרה אונליין, הגעתי לפקטורי ההמרה המתאימים לאזור הכללי שבו אני גר. כדי לחשב את המרחק הנכון במטרים, מכפילים את ההפרש בין הקואורדינטות בקווי רוחב ב-110905.73, את ההפרש בקווי אורך ב-93926.11, ועם שתי התוצאות האלה ממשיכים לפיתגורס. בדקתי כמה נקודות ציון כדי לוודא שהנוסחאות שלי מפיקות אותה תוצאה כמו גוגל. בסדר, עכשיו לקוד שמקבל נתונים מה-GPS ומעבד אותם.

אז כאמור, בחרתי לממש את הרעיון על לוח BBC Micro:bit, ובשפת התכנות מיקרו-פייתון, כי חשבתי שזה יהיה קל וזריז. הרי העבודה השחורה היא בעיקר בחילוץ הקואורדינטות מתוך המחרוזות שמודול ה-GPS שולח, מה שאפשר לעשות בפייתון בשתיים-שלוש שורות קוד, ול-Micro:bit כבר יש תצוגה מינימלית מובנית שזה בכלל עושה את החיים קלים. לא ככה?

כעבור שלושה ימים…

אז לא, מסתבר שלא ככה. לא אלאה אתכם בכל הפרטים הקטנים, הנה המסקנות העיקריות מהשעות הרבות שבזבזתי על הנושא:

  1. הפונקציות שמציגות דברים על המסך, כגון display.show ו-display.scroll, הן כברירת מחדל פונקציות חוסמות (blocking) שמעכבות את כל שאר הקוד עד שהן מחליטות שמספיק. כדי למנוע את זה, צריך לציין במפורש, בקריאה להן, את הפרמטר wait=False.
  2. הזיכרון והמהירות של המיקרו:ביט צנועים מאוד (במיוחד, כמובן, כשכותבים ב-MicroPython), ובלי תכנות זהיר, כשיש שטף של מידע נכנס – סתם לדוגמה, קלט ממודול GPS – הם עלולים לא לעמוד בקצב.
  3. כשיש רק ערוץ UART אחד והוא שמור לקלט, מאוד קשה לדבג קוד.
  4. משהו בהמרה של פלט הפונקציה uart.readline למחרוזת רגילה, באמצעות str, לא עובד כמו שצריך (כן, ניסיתי קידודים שונים). עד עכשיו אני לא בטוח מה בדיוק הבעיה, אולי קשור לגודל ה-buffer הנכנס או משהו, אבל כבר די נמאס לי לבדוק.

ומה שהכי גרוע, מדי פעם, באקראי, הלוח "נפל" על קלט מסוים בדיוק ברגע הנכון אחרי צריבת הקוד או החיבור לחשמל, ואז הקוד כן עבד כמצופה – ובהמשך, או בהפעלה הבאה, כשל.

הבעיות האלה התערבבו עם הטעויות הקטנות והטפשיות שלי לאורך הדרך (למשל, שנתתי למידע ממחרוזות לא רלוונטיות לדרוס את המידע הטוב עוד לפני שהספקתי להציג אותו…), והכול ביחד גרם, איך נאמר, לקצת תסכול. אבל ההתמדה השתלמה ובסופו של דבר הפרויקט הושלם.

הסגר-מטר בעיצומה של הודעת שגיאה - לפני שה-GPS התחיל לשלוח מידע שימושי
הסגר-מטר בעיצומה של הודעת שגיאה – לפני שה-GPS התחיל לשלוח מידע שימושי (לחצו להגדלה)

איך זה עובד

החומרה מינימליסטית ככל האפשר: לוח BBC Mirco:bit, בית סוללות 2xAAA שמתחבר אליו (היה בקיט של המיקרו:ביט), מודול GPS סיני קטן שמסתדר ב-3V והגיע עם כבלים נוחים (עכשיו בגלל הקורונה אפשרויות המשלוח מצומצמות), ומתאם למיקרו:ביט עם headers (כמו זה, אם כי יש עוד הרבה דגמים בשוק). למעשה, אם יש לכם חוטי גישור וחוטים עם "תנינים", אפשר לאלתר חיבור ממודול ה-GPS למיקרו:ביט בלי מתאם, אם כי זה פחות אמין ונוח.

הקוד קורא כל הזמן את המידע שמגיע ב-UART מה-GPS (בפורמט NMEA הסטנדרטי) ומחפש את סימן ההיכר GNGLL, שמציין מחרוזת שמכילה את קואורדינטות המיקום. למה לא GPGGA המוכרת-יותר? כי GLL קצרה יותר – פחות עבודה למיקרו-פייתון – והנתונים המלאים של איכות קליטה, שעה וכדומה פשוט לא מעניינים אותי ביישום הספציפי הזה.

כשנמצאה המחרוזת המתאימה, הקוד מחלץ ממנה את הקואורדינטות, ממיר אותן לעשרוניות כפי שתואר למעלה ומחשב את המרחק. אחת לשמונה שניות, המרחק העדכני, מעוגל למטרים שלמים, מוצג ב-scroll רגיל על פני התצוגה. אם המחרוזת לא נמצאת כלל – למשל, אם מודול ה-GPS התנתק בטעות – מוצג הטקסט "…", ואם המחרוזת נמצאה אבל הנתונים בה לא תקינים מאיזושהי סיבה (כגון אות לוויין לא מספק) מוצג "Prs" לסימון כללי של בעיית Parsing.

רוצים קוד MicroPython שעושה את זה? הנה יש לכם מה לעשות בסגר 😉

להרשמה
הודע לי על
0 תגובות
Inline Feedbacks
הראה את כל התגובות