הבלוג של ארתיום
בלוג על לינוקס, תוכנה חופשית, מוזיקה, סלסה, ומה לא!
תמיכה בחצובה או סיפורי הסבת indi ו-indigo לאנדרואיד
מה שטוב בסטנדרטים זה שיש הרבה כאלה...
אחרי תקופה ארוכה של פיתוח תוכנה/אפליקציית OpenLiveStacker המיועדת לצילום אסטרונימי בזמן אמת, הגעתי לנקודה שמעבר לתמיכה במצלמה נדרשת תמיכה בחצובה ובמצלמה גנרית. אז איך מתחברים במשהו גנרי? משתמשים בממשק סטנדרטי! בעולם "חלונות" יש ASCOM. בעולם הלינוקס המצב נותן מגוון רחב של סטנדרטים
במבט הראשון Alpaca זה הדבר האולטימטיבי - המשך של ASCOM רק חוצה פלטפורמה! נהדר.
רק יש בעיה קטנטנה - אין באמת דרייברים ל-Alpaca כל זה תיאורתי לחלוטין.
השני זה פרוייקט indi - שרוב הייצרנים של ציוד צילום תומכים בו. יש דרייברים מובנים בקוד פתוח ויש כלאה שעובדים עם SDK של צד ג'. בפועל זו הדרך לעבוד בלינוקס. ואפילו מוצרים כמו ASIAir משתמשים בו.
הוא בא עם כלים EKOS ו-KStars לשליטה בציוד שחווית המשתמש זה לא הצד החזק שלהם בלשון המעטה
בעיה נוספת זה ש-indi בנוי כבד על ארכיטקטורת שרת-לקוח. וכל דרייבר למעשה רץ בתהליך משלו. זה מצוין לעבודה בלינוקס קלאסי אבל אנדרואיד מאוד עויין לתהליכים שרצים זמן רב וזו בעיה גדולה.
מעבר לזה הוא משוחרר תחת GPL/LGPL. אבל מבחינתי זו בעיה פחותה בגלל שגם ככה OpenLiveStacker הוא GPL
הפרוייקט הנוסף הוא indigo שנוצר בעקבות חלוקי דיעות עם indi (קצת כמו kde וגנום). הפרויקט הזה פופולרי על מאק.
אז מה ההבדלים?
- הוא כתוב ב-C בניגוד ל++C - שאומנם זה חסרון אבל לא גדול במיוחד.
- יש מעט מאוד תלויות
- הוא מאפשר לטעון דרייברים ישירות - כ-Shared object בניגוד לתהליכים והתקשורת היא פנימית
- הרישיון דמוי BSD (בהתחלה לא היה תואם GPL אבל בעקבות פנייתי אליהם שונה לאחד תואם)
- כלי שליטה הרבה יותר נוחים: כלומר AIN שלהם נותן חוויה הרבה יותר שפויה בהשוואה ל-EKOS.
- החסרון היחיד שראיתי שהוא לא עוטף תקשורת בפונקציות משלו אז תמיכה ב-serial port שמאוד פופולרי בחצובות תהיה יותר מסובכת (כי ב-Android לא ניתן לגשת ישירות ל-dev/tty/)
אם כך הבחירה הייתה פשוטה indigo.
תמיכה ב-indigo
אחת הבעיות הראשונות שראיתי עם indigo זה הרישיון שלא היה מאושר OSS ובפירוש לא היה תואם GPL מה שגרם לי מהיסוסים קשים. אז פניתי למפתחים והפלא הפלא הם הסירו את הסעיף הבעייתי והרישיון למעשה הפך כמעט זהה ל-BSD2. נהדר!
התחלתי דווקא ממשהו מוכר - לתמוך במצלמות נוספות דרך indigo כדי להבין את הממשק ולהתנסות. הייתי צריך להתרגל לצורת חשיבה. בניקוד לממשקים של מצלמות שעבדתי עד עכשיו - שיכולתי לקרוא לפונקציות, לשנות הגדרות פה היה מדובר בפרוטוקול - לשלוח properties ולקבל עדכונים בצורה אסינכרונית. שברתי ראש זמן מה, קיבלתי ייעוץ מהמפתחים שחייב להגיד נתנו תשובות בצורה מהירה ומקיפה - ותוך זמן לא רב יכולתי להתחבר למצלמות דרך ממשק indigo. בדקתי ASI ZWO, בדקתי Meade/ToupTek וגם בדקתי את ה-sv105 וסה"כ הייתי מרוצה.
זה פתח דלת לעבודה בלינוקס עם כל מצלמה אפשרית.
התחלתי לשחק עם התוכנה שלהם ולהפעיל חצובה. שמתי לב להתנהגות מוזרה. כשלמשל שלחתי את החצובה לכוכב הצפון במקום שהיא תרים את הראש בכ-30 מעלות כמו שזה מצופה - היא הסתובבה הצידה. היה ברור שהחצובה חושבת משווני ולא AltAz.
התחלתי לחפש וגיליתי שהחצובה שיש לי AZ GTi לא נתמכת צורת ברירת המחדל שלה AltAZ. למעשה פרוייקט indigo בכלל עובד רק בצורה משוונית. כי זה "מה שהצלמים עושים". ולמטרה שלי שזה EAA דווקא שימוש ב-AltAz הרבה יותר פשוט והגיוני.
זה היה מאכזב מאוד. ביקשתי לשפר את התיעוד - כי לא הייתי צריך לגלות זאת בשלב כל-כך מאוחר. אומנם קיבלתי הצעה להוסיף בעצמי תמיכה כזו (כי אין מניעה מובנית) אבל החלטתי שזה לא השלב להיכנס לדבר כזה.
אז indi אני בא
אז מה מצב עם indi
מימשתי מהר גישה למצלמה כדי ללמוד ממשקים וזה היה מהיר ופשוט (אחרי הכרות עם indigo) ואז עברתי למימוש.
הבעיה העיקרית עם indi שיש לו ארכיטקטורה מבוססת תהליכים נפרדים ואנדרואיד לא אוהב תהליכים.
כל דרייבר מדבר עם indi-server ע"י שליחה וקבלה של הודעות XML. גם הלקוח מדבר עם indi-server ע"י שליחת הודעות XML. דבר ראשון בדקתי אולי הלקוח יכול לדבר עם דרייבר ישירות במקום לדבר עם שרת שהוא סה"כ מתווך ביניהם. בדקתי פקודה פשוטה <getProperies />
שגם השרת וגם הלקוח אומרים לתמוך בה. אבל מסתבר שזאת אומנם אותה פקודה אבל לא בדיוק - יש לה פרמטרים שונים בהתאם אם מדברים עם השרת או עם הדרייבר.
אפשרות נוספת הייתה לדבר ישירות עם האובייקט של הדרייבר - אבל מהר מאוד הבנתי שזו לא תהיה משימה פשוט בכלל. הם לא בנויים לזה.
משמעה צריך להסב גם את ה-indi-server ולהפוך אותו לספריה.
- הפכתי main של דרייבר לפונקצייה מקבלת pipe file dscriptors במקום stdin/stdout כמו שעושה בתהליך
- שיניתי את main של indiserver כדי שיריץ את השרת וידבר מעל pipes עם דרייבר אבל יטען shared object במקום להעלות תהליך נפרד
- שיניתי תקשורת של דרייבר שיכתוב ל-pipe במקום ל-stdout - מזל שכבר יש אובייקט שמטפל בזה נדרשו רק שינויים קלים.
המזל הגדול שה-event loop של דרייבר מחבינת הקוד שונה מ-event loop של indi-server. ה-indi-server משתמש ב-libev ודרייבר במשהו שנכתב ע"י מפתחי indi - אז המזל שהם יכולים לרוץ ולא להפריע אחד לשני
בדקתי על לינוקס - עובד מתחבר לחצובה שולח פקודת goto
פה התחילו הצרות
מי הזיז את ה-ifdef שלי?
אני בא להריץ על אנדרואיד - בדיוק אותו הקוד ובדיוק אותו תהליך בנייה - לא מתחבר. התחלתי לבדוק לעומק. חשדתי שאנדרויד חוסם תקשורת UDP. אפילו כתבתי פקודה פשוטה מחוץ ל-indi שמאפשרת לשלוח הודעת לחצובה וראיתי שהיא עוברת ואני מקבל תשובה. אז מה הבעיה?
שרפת על זה מלא זמן. הוספתי הדפסות debug עד שהגעתי לפונקציה שאמורה לקבל תשובה והיא מחזיקה שגיאה. מפה לשם אני מחפש ואני מוצא את קטע הקוד הבא:
int tty_timeout_microseconds(int fd, long timeout_seconds, long timeout_microseconds)
{
#if defined(_WIN32) || defined(ANDROID)
...
return TTY_ERRNO;
אההההה... זה היה מתסכל - אבל אחרי שמצאתי זה התחיל לעבוד. זו הייתה התקדמות מרשימה!
צרה שלא מחכה לי
המשכתי לפתח הכל עבד יחסית חלק, יש שליטה, יש plate solve יש sync, סה"כ עובד.
ואז אני מתחיל לשים לב ש-plate solve לא עובד מידי פעם. ואז אני שם לב שברגע שאני מעלה את הדרייבר של חצובה זה מפסיק לעבוד! לפני זה בדקתי עם דרייבר חיצוני והכל עובד, אבל עם דרייבר פנימי נופל.
התחלתי לחפור. אני מבצע plate solving בעזרת כלי שנקרא ASTAP (שאגב כתוב ב-Pascal!). ואני מריץ אותו בתהליך חיצוני ומקבל את הסטטוס לפי מה שהוא מחזיר לתהליך אבא - האם הצליח, נכשל ומאיזו סיבה. לכן חשוב לי לדעת שהתהליך סיים ובאיזו צורה. בשביל זה אני מחכה לו (waitpid). מבדיקת strace אני מגלה שמישהו אחר תופס את הסיום התהליך וקורא את הסטטוס. ומה שנשאר לי זו רק השגיאה.
החשוד המידי זה כמובן indi-server כי הוא מריץ לתהליכים. אבל על פניו אני לא מבקש ממנו לחכות לתהליך. אז מה מסתבר שספריית libev מיוזמתה משתלטת ועוקבת על תהליכים ואם מישהו מסיים היא תופסת את הסטטוס שלה.
התחלתי לחפש בכל מקום, כולל בקוד איך לבקש ממנה - "אל תתערבי בניהול תהליכים!" ניסית כמה פרמטרים ללא הועיל. היא פשוט מגדירה signal handler עבור SIGCHLD ותופסת את כל האירועים. בדקתי דגלים שונים ללא הועיל. פניתי לרשימת התפוצה של libev - ללא מענה... עד שנפל האסיפון. אחרי ששרת indi/libev עולה אני פשוט יכול לדרוס את ה-signal handler שהוא הגדיר עם אחד ברירת המחדל של המערכת וחוסם את האפשרות לצורר הזה לתפוס לי את הסטטוס.
זה לא היה פשוט.
מתמטיקה זו שפה קשה.
המשכתי. Indi בפנים משתמש במה שנקרא Observed Equatorial Coordinates - מערכת הצירים שמתייחסת לציר סיבוב כדור הארץ הנוכחי (כן הוא משתנה עם הזמן - בגלל הנקיפה ). רוב בסיסי הנתונים של גרמי שמיים עובדים עם מערכת צירים המתייסת לJ2000. לכן צריך להמיר (לא הבדל גדול אבל משמעותי לדיוק החצובה).
למזלי indi נותנת פונקציות המרה - הכל פשוט. בדקתי עובד. התקנתי על הטלפון ופתאום אני מתחיל לשים לב שהערכים של RA/DEC ממממממש מוזרים ולא נכונים.
מתחיל לחפור - בלינוקס הכל נכון. בסימולטור של אנדרואיד הכל נכון, במכשיר פיזי - שגיאה.
ממשיך לחפור מכניס הדפסות לכל נקודה בפונקציות החישוב. מגיע ש-libnova ש-indi משתמשת בו מחזירה ערכים לא תקינים. אולי בניתי לא נכון? ממשיך בסוף מגיע לפונקצית חישוב של נקיפת כדור הארץ מדפיס כל חישובי הביניים ומזהה את הנקודה בה הכל משתנה. איפה? בחישוב קוסינוס!
לצורך חישוב הנקיפה libnova עובדת עם long double ובפרט קוראת לפונקציות cosl אם הן זמינות. בודק כניסה ויציאה והחישוב לא נכון. פונקציית cosl (שזה cos של long double) שבורה! מחליף ל-cos רגיל והכל תקין.
לזה פשוט לא ציפיתי. הכנסתי patch שלחתי למפתחים של libnova נראה מה יעשו
כשהכפתורים נלחצים
משחרר גרסה מקבל משוב ראשון - משהו לא מתנהג נכון עם כפתורים שמזיזים את החצובה. תסתכל בלוגים, נכון התהגות מוזרה. מה כבר יכול להיות יותר פשוט:
- כשכפתור נלחץ - מפעילים הזזה בכיוון הנכון
- כשכפתור משתחרר עוצרים את ההזזה
מסתבר שזה לא כל-כך פשוט. מפה ושם שרפתי על זה הרבה זמן. זו היית משימה מורכבת. איזה צרות למשל? שחררתי כפתור קיבלתי אירוע שהוא השתחרר לחצתי על אחד אחר ועוד פעם קיבלתי אירוע שהכפתור הקודם לא לחוץ.. אז הם הוא לא לחוץ אפשר לעצור? בקיצור. אולי יש מישהו פה אוהב לכתוב JavaScript/Web UI יקח ממני את הצרות האלה ואני אתעסק רק בדברים פשוטים כמו הסבת קוד C++/C מורכב, לדבר עם מערכת הפעלה מוזרה ולפתור בעיות אלגוריתמיות של חישובים?
שורה תחתונה
עברתי דרך ארוכה אבל יש בסופה סיפוק. עשיתי דבר גדול - לא רק עבור OpenLiveStacker אלא גם זה שכל אחד יוכל לקחת indi ולהריץ על אנדרואיד ולפתח עוד אפליקציות מגניבות ושימושיות (כמובן בקוד פתוח כי indi הוא תחת GPL)
הוסף תגובה:
חובה לאפשר JavaScript כדי להגיב.