// -*- C++ -*- generated by wxGlade 0.6.3 on Tue Mar 10 11:02:18 2009 /* Copyright 2009 Ronald S. Burkey This file is part of yaAGC. yaAGC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. yaAGC 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. You should have received a copy of the GNU General Public License along with yaAGC; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Filename: yaTelemetry.cpp Purpose: A program to display telemetry-downlink info from AGC. Compiler: GNU gcc. Reference: http://www.ibibio.org/apollo Mods: 2009-03-09 RSB. Began. 2009-03-13 RSB Added --simple. 2009-03-14 RSB Use smaller default font sizes when the --simple switch is used. Work around a funky socket error which appears in Windows. 2009-04-07 RSB Began adding stuff related to MSK formtting. The program does nothing more than connect to yaAGC on a socket, and then display any telemetry messages it receives. There is a single active widget, which is a text control. A periodically-executing event handler fetches data from the socket, formats it properly, and sticks it into the text-control. The text control is read-only, meaning that the user can't type anything into it. The text control is sized to have 80 columns and 43 rows, since this is the same as the size of the xterm which was previously used to display the downlink data when yaDSKY processed it before yaTelemetry was created. Anyone who knows C++ will notice, I'm sure, that I duplicate a lot of functions between MainFrameClass and SimpleFrameClass that I should have just handled through inheritance. If anyone wants to fix it up for me, that would be swell, though it might goof up maintenance of the classes via wxGlade. */ #include "yaTelemetry.h" #include "../yaAGC/yaAGC.h" #include "../yaAGC/agc_engine.h" MainFrameClass *MainFrame = NULL; SimpleFrameClass *SimpleFrame = NULL; #include #include #include #include using namespace std; ProcessDownlinkList_t PrintMsk683, PrintMsk966, PrintMsk1123, PrintMsk1137; #define PULSE_INTERVAL 100 static char DefaultHostname[] = "localhost"; char *Hostname = DefaultHostname; static char NonDefaultHostname[129]; #ifdef WIN32 static int StartupDelay = 500; #else static int StartupDelay = 0; #endif #if defined (WIN32) #define DEFAULT_FONTSIZE_RESIZABLE 8 #define DEFAULT_FONTSIZE_RETRO 9 #elif defined (__APPLE__) #define DEFAULT_FONTSIZE_RESIZABLE 10 #define DEFAULT_FONTSIZE_RETRO 14 #else #define DEFAULT_FONTSIZE_RESIZABLE 8 #define DEFAULT_FONTSIZE_RETRO 11 #endif static int Points = DEFAULT_FONTSIZE_RETRO; static bool Simple = false; // Here are some templates for various MSK screens. static char Msk683[] = " CSM GNC PRIMARY TAB 0683 \n" "GMT XX:XX:XX:XX SITE XXXXXXX \n" "GETA XXXX:XX:XX QUADA QUADB QUADC QUADD \n" "CTE XXXX:XX:XX WPU CAL C LBS XXXX.X XXXX.X XXXX.X XXXX.X \n" "CMC XXXX:XX:XX HE P/T R PCT XXX.X XXX.X XXX.X XXX.X \n" "CMCB XXXX:XX:XX ______________________________________________________ \n" "GETC XXXX:XX:XX | PKG TEMP degF +XXX.X +XXX.X +XXX.X +XXX.X |\n" "CMC dT XXX.XXX | HE TK P PSIA XXXX XXXX XXXX XXXX |\n" "ISS XXXXXXX | M | HE TK T degF +XXX.X +XXX.X +XXX.X +XXX.X |\n" "OPT XXXX | D | HE MN P PSIA XX.X XX.X XX.X XX.X |\n" "CMC XXXX | E | FU MN P PSIA XX.X XX.X XX.X XX.X |\n" "ID XXXXXXX | OX MN P PSIA XX.X XX.X XX.X XX.X |\n" " | CM-RCS SYS 1 SYS 2 SPS |\n" "VERB NOUN PRGM | HE TK P PSIA XX.X XX.X OX TK PSI XXXX |\n" " XX XX XX | HE MN P PSIA XX.X XX.X OX LN PSI XXXX |\n" "REG 1 +XXXXX | HE TK T degF +XXX.X +XXX.X EG CH PSI XXXX |\n" "REG 2 +XXXXX |___________________________________ FU TK PSI XXXX |\n" "REG 3 +XXXXX PITCH YAW ROLL | FU LN PSI XXXX |\n" " ISS ATT +XXX.X +XXX.X +XXX.X | HE TK PSI XXXX |\n" "_______DAP_______ ACDU +XXX.X +XXX.X +XXX.X |__________________|\n" "RATE DB FCDU +XXX.X +XXX.X +XXX.X \n" " X.X X.X ERR CMC +XX.X +XX.X +XX.X HE TK T degF +XXX.X \n" "VEH ACC +XXX.X ERR SCS +XX.X +XX.X +XX.X OX FD LN degF +XXX.X \n" "VG X +XXXX | ENT +XX.X +XX.X +XX.X FU FD LN degF +XXX.X \n" "VG Y +XXXX RATE | G/N +XX.X +XX.X +XX.X OX LN I degF +XXX.X \n" "VG Z +XXXX | SCS +XX.X +XX.X +XX.X FU LN I degF +XXX.X \n" "PIPX +XXXXX EG VLV degF +XXX.X \n" "PIPY +XXXXX GMB CMD +XX.XX +XX.XX +XX.XX N2 TA PSI XXXX \n" "PIPZ +XXXXX OCDU DC +XX.XX +XX.XX N2 TB PSI XXXX \n" "PIP T +XXX/X SPS GMB +XX.XX +XX.XX OX T1 PCT XXX.X \n" "TEV XXXX:XX:XX AT TVC +X.XX +X.XX OX T2 PCT XXX.X \n" "TIG XXXX:XX:XX MN TVC +X.XX +X.XX +X.XX FU T1 PCT XXX.X \n" " DIF CUR +XXX.X +XXX.X FU T2 PCT XXX.X "; static char Msk966[] = " CMC MONITOR D H/S 966 \n" " _____________________________________________________________________________ \n" "| ID XXXXX GMT XX:XX:XX.XX SITE XXXXXXX X/ROLL Y/PITCH Z/YAW |\n" "| CTE XXXX:XX:XX PIPA +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 0 XXXXX GETA XXXX:XX:XX DELV +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 1 XXXXX GETC XXXX:XX:XX VGIMU +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 2 XXXXX CMCB XXXX:XX:XX |\n" "| FGWD 3 XXXXX CMC XXX:XX:XX.XX FCDU +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 4 XXXXX TCMSV XXX:XX:XX.XX DCDU +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 5 XXXXX TLMSV XXX:XX:XX.XX ACDU +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 6 XXXXX TGO XXX:XX:XX |\n" "| FGWD 7 XXXXX TIG XXX:XX:XX.XX ERROR +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 8 XXXXX TEVNT XXX:XX:XX.XX AK +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 9 XXXXX _______________________ ADOT +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 10 XXXXX | PG XX FL X WARN X | OMGAC +XXX.XX +XXX.XX +XXX.XX |\n" "| FGWD 11 XXXXX | VB XX NN XX PRG X | P ERR +XXX.XX +XXX.XX +XXX.XX |\n" "| | KKK X | I BIAS +XXX.XX +XXX.XX +XXX.XX |\n" "| CHNL 11 XXXXX | R1 +XXXXX UPFST X | P OCTL +XXX.XX +XXX.XX +XXX.XX |\n" "| CHNL 12 XXXXX | R2 +XXXXX UPSW X | LM XXXXX ACTOFF +XX.XXX +XX.XXX |\n" "| CHNL 13 XXXXX | R3 +XXXXX CMD X | CSM XXXXX GMBDCMD +XX.XXX +XX.XXX |\n" "| CHNL 14 XXXXX |_______________________| |\n" "| CHNL 30 XXXXX REDO DSTB11 XXXXX HAPO +XXXX.X TCSI XXX:XX:XX.XX |\n" "| CHNL 31 XXXXX XXXXX FAILRG HPER +XXXX.X TCDH XXX:XX:XX.XX |\n" "| CHNL 32 XXXXX RSBBQ XXXXX LAT +XX.XXX TTPI XXX:XX:XX.XX |\n" "| CHNL 33 XXXXX XXXXX XXXXX LONG +XXX.XX TTPF XXX:XX:XX.XX |\n" "| XXXXX XXXXX VMAGI XXXXX TMRK XXX:XX:XX.XX |\n" "| IMDE 30 XXXXX HLDFG INTEG TIME VGTLI XXXXX TVHF XXX:XX:XX.XX |\n" "| IMDE 31 XXXXX XXXXX XXX:XX:XX.XX dT XXX:XX.XXX VHFRNG NM +XXX.XX |\n" "| OPTMDE XXXXX RCSFG STARID1 XXX CDH dH +XX.XX ELEVN ANG +XXX.XX |\n" "| DPDTR1 XXXXX XXXXX STARID2 XXX VHF XX OPT XX CNTRL ANG +XXX.XX |\n" "| DPDTR2 XXXX CDU SHFT +XXX.XX dVLX +XXXX.X Y +XXXX.X Z +XXXX.X |\n" "| CDU TRUN +XX.XXX dVR/PX +XXXX.X Y +XXXX.X Z +XXXX.X |\n" "|_____________________________________________________________________________|"; static char Msk1123[] = " LM GUID, CONTROL, and PROP RT | | | | 1123 \n" " | AEA | LGC | PCM | \n" "GET XXX:XX:XX MET XXX:XX:XX |_____|_____|_____| \n" "AGS XXX:XX:XX LGC XXX:XX:XX LGC FMT XXXXXX SITE XXXXXXX \n" "T/E XXX:XX:XX TGO XX:XX TTF/8 XX:XX \n" "PGNS RATE XX.X XX.X XX.X ____________________________\n" "RGA RATE XX.X XX.X XX.X | \n" "ASA RATE XX.X XX.X XX.X | ____VEH WT_____\n" "______________ROLL/Z_____PITCH/Y______YAW/X_______| | \n" "ATT CMDS XX.X XX.X XX.X | | XXXXX LGC \n" "RSVR GMBL XXX.X XXX.X XXX.X | | XXXXX CAL \n" "_________________FDAI ATTITUDES___________________| |_______________\n" "ICDUD ATT XXX.X XXX.X XXX.X | \n" "IMU ATT XXX.X XXX.X XXX.X | \n" "CDUA ATT XXX.X XXX.X XXX.X | _TORQUE_ \n" "AGS ATT XXX.X XXX.X XXX.X | | | \n" "_________________ATTITUDE ERRORS__________________| +U | XX.X | \n" "PGNS ERR XX.X XX.X XX.X | -U | XX.X | \n" "AGS ERR XXX.X XXX.X XXX.X | +V | XX.X | \n" "___________________VELOCITIES_____________________| -V | XX.X | \n" "LGC DEL V XX.X XX.X XX.X | |________| \n" "AGS DEL V XX.X XX.X XX.X | \n" "AGS ULL XX.XX ACT VEL XX.X | \n" " AUTO XX F0 XXXXX | \n" "R XXX.X ATT H XX F1 XXXXX | \n" "P XXX.X ______LR______ F2 XXXXX | \n" "Y XXX.X | AP X |____DEDA________DSKY___| \n" "RR XXXXX | ALT XX V XX | AD XXX | P XX | \n" "TRK XXXXX | | RO +XXXXX | V XX | _______________\n" "R XXX.XX | SR XXXXX | CLR X | N XX | | XXX GTC \n" "RDT XXXX | VX +XXXXX | M +XXXXX | 1 +XXXXX | | XXX CMD \n" "SH +XXX | VY +XXXXX | +XXXXX | 2 +XXXXX | | XXX POS \n" "TR +XXX | VZ +XXXXX | REDO XXXXX | 3 +XXXXX | | XXX TH "; static char Msk1137[] = "GET XXX:XX:XX LGC XXX:XX:XX GMT XXX:XX:XX AEA LGC PCM 1137 \n" "______THRTL_______ ROLL-R PITCH-Q YAW-P _____________ SITE XXXXX\n" "SELECT XXXX | LGC XXXXX +XX.X +XX.X +XX.X WT XXXXX D/L XXXXX\n" "DECA +XXX.XX | AGS ERR +XXX.X +XXX.X +XXX.X CSM XXXXX XXX C5 MSK\n" "MAN THR XXX% | OMEGA-D +X.XX +X.XX +X.XX BUG 000XX XXX C6 MSK\n" "AUTO THR XXX% | OFFSET +XX.XXX +XX.XXX +XX.XXX DAP XXXXX XXXXX C 30 \n" "CMD THR XXX% | GMBL DR XX XX XX C 11 XXXXX XXXXX C 31 \n" "VAR ACT XXX% | SC TORQ-U TORQ-V TORQ-P C 12 XXXXX XXXXX C 32 \n" "GUID CMD XXX% | AUTO XX XX.XX XX.XX XX.XX C 13 XXXXX XXXXX C 33 \n" "TCP XXX% | A/H XX XX.XX XX.XX XX.XX C 14 XXXXX XXXXX RAD \n" "_____________ATTITUDES___________________STATUS__________________LR____________\n" " ROLL-Z PITCH-Y YAW-X | _____DAP_____ | LR RNG XXXX VEL XXXX \n" "B +XXX.XXX +XXX.XXX +XXX.XXX | TIG XXX:XX:XX | LR XXX PGNSd AGSd \n" "OCTAL XXXXX XXXXX XXXXX | TGO XXXXX | VXS XXXXX XXXXX XXXXX \n" "RSVRS +XXX.XX +XXX.XX +XXX.XX | T/E XXX:XX:XX | VYS XXXXX XXXXX XXXXX \n" "ACDU +XXX.XX +XXX.XX +XXX.XX | T/P XXXXX | VZS XXXXX XXXXX XXXXX \n" "RSVR +XXX.XX +XXX.XX +XXX.XX | LGC X | RNG XXXXX XXXXX XXXXX \n" "ACT +XXX.XX +XXX.XX +XXX.XX | ISS X | ___________RR____________\n" "IDES +XXX.XX +XXX.XX +XXX.XX | PCNS X | MODE XXXXX XXXXX XXXXX \n" "FDES +XXX.XX +XXX.XX +XXX.XX | CH 77 XXXXX | DATA XXXXX XXXXX XXXXX \n" "AGS +XXX.XX +XXX.XX +XXX.XX | FREG0 XXXXX | \n" "dERR +XX.XX +XX.XX +XX.XX | FREG1 XXXXX | _______SHFT_____TRUN_____\n" " | FREG2 XXXXX | LGC +XXX.X +XXX.X \n" "______________RATES_________________| REDO X | RR +XXX.X +XXX.X \n" "PGNS-B +XX.XX +XX.XX +XX.XX | PROG XX | ERR +XX.XX +XX.XX \n" "RGA-B +XX.X +XX.X +XX.X | VERB XX | RANGE XXX.XX XXX.XX \n" " | NOUN XX | RNGRT +XXXX +XXXX \n" "________DELTA VELOCITIES____________|__ FL +XXXXX | \n" "AGS-B +XX.X +XX.X +XX.X | +XX.X | R1 +XXXXX | _______POWER____TEMPS____\n" "LGC-B +XX.X +XX.X +XX.X | +XX.X | R2 +XXXXX | 800~ XX.X XX.X \n" "ACD dV XX.X XX.X XX.X | +XX.X | R3 +XXXXX | 3200~ XX.X XX.X \n" "PIP-S XXXXX XXXXX XXXXX | VEL | | 120v XXX XXX \n" "SM XX.X XX.X XX.X | GAIN | | BIAS X.XX X.XX "; // Clears the output text control. The reason we use this instead of the // text-control's native Clear function is that we want the text control // to always contain exactly TELEMETRY_ROWS x TELEMETRY_COLUMNS worth of // stuff, even if that stuff is blank spaces. This makes it easier for us // to read and write to random locations, since the writing can always be // replacement of existing text. We expect this function to be called only // at startup, or when the downlink mode changes, so we don't bother to // optimize it for speed. void MainFrameClass::ClearScreen (void) { wxString EmptyScreen; wxTextAttr Style; wxColor TextColor; int i, j; for (i = 0; i < TELEMETRY_ROWS; i++) { if (i > 0) EmptyScreen += wxT ("\n"); for (j = 0; j < TELEMETRY_COLUMNS; j++) EmptyScreen += wxT (" "); } Style = TextCtrl->GetDefaultStyle (); #ifdef __APPLE__ // On the Mac, the background color of the text control does not come out // right --- it's always white, no matter what I do. It also has a // (disabled) scrollbar no matter what I do. I've expended more time on // it than it's worth to me, so on the Mac I simply eliminate the decorative // border into which the text control (which is supposed to be black) is // supposed to blend, and choose a text color that shows up okay on the // (wrong) white background. TextColor.Set (0x000000UL); #else TextColor.Set (0xc0c0c0UL); #endif Style.SetTextColour (TextColor); TextCtrl->SetDefaultStyle (Style); TextCtrl->Clear (); TextCtrl->FitInside (); TextCtrl->ChangeValue (EmptyScreen); } // Print to screen at coordinates x,y. Clipping is performed, but // not wrapping. In other words, any call to Print() will go on // a single line. Don't try to defeat it by putting in newlines, // as this will just mess up the rationale by which the screen is // being managed. void MainFrameClass::Print (int x, int y, wxString &Value) { int From, To, Length, xend; // Sanity checking. Abort if the string will be completely // off the screen. The usual case is that everthing will be // fine and on-screen, so we do a quick check for that case. if (y < 0 || y >= TELEMETRY_ROWS) return; Length = Value.Length (); xend = x + Length; if (x < 0 || xend > TELEMETRY_COLUMNS) { // Perhaps it's completely off-screen! if (xend <= 0 || x >= TELEMETRY_COLUMNS) return; // No, at least partially on-screen. if (xend > TELEMETRY_COLUMNS) { Length -= (xend - TELEMETRY_COLUMNS); Value = Value.Left (Length); } if (x < 0) { Length += x; Value = Value.Right (Length); } } // All checked and/or clipped. Output it. From = TextCtrl->XYToPosition (x, y); To = From + Length; TextCtrl->Replace (From, To, Value); } // Set font-size in the text control. void MainFrameClass::FontSize (int Points) { TextCtrl->SetFont(wxFont(Points, wxMODERN, wxNORMAL, wxNORMAL, 0, wxT(""))); } // Remove frame decorations. void MainFrameClass::Undecorate (void) { delete bitmap_1; delete bitmap_5; delete bitmap_3; delete bitmap_2; bitmap_1 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("tTelemetryLeft.jpg"), wxBITMAP_TYPE_ANY)); bitmap_5 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("tTelemetryTop.jpg"), wxBITMAP_TYPE_ANY)); bitmap_3 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("tTelemetryBottom.jpg"), wxBITMAP_TYPE_ANY)); bitmap_2 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("tTelemetryRight.jpg"), wxBITMAP_TYPE_ANY)); do_layout(); } // Clears the output text control. The reason we use this instead of the // text-control's native Clear function is that we want the text control // to always contain exactly RowsToUse x TELEMETRY_COLUMNS worth of // stuff, even if that stuff is blank spaces. This makes it easier for us // to read and write to random locations, since the writing can always be // replacement of existing text. We expect this function to be called only // at startup, or when the downlink mode changes, so we don't bother to // optimize it for speed. void SimpleFrameClass::ClearScreen (void) { wxString EmptyScreen; //wxTextAttr Style; wxColor TextColor; int i, j; switch (MskType) { case 683: EmptyScreen = wxString::FromAscii (Msk683); break; case 966: EmptyScreen = wxString::FromAscii (Msk966); break; case 1123: EmptyScreen = wxString::FromAscii (Msk1123); break; case 1137: EmptyScreen = wxString::FromAscii (Msk1137); break; default: for (i = 0; i < RowsToUse; i++) { if (i > 0) EmptyScreen += wxT ("\n"); for (j = 0; j < TELEMETRY_COLUMNS; j++) EmptyScreen += wxT (" "); } break; } //Style = TextCtrl->GetDefaultStyle (); //TextColor.Set (0x000000UL); //Style.SetTextColour (TextColor); //TextCtrl->SetDefaultStyle (Style); //TextCtrl->Clear (); //TextCtrl->FitInside (); TextCtrl->SetLabel (EmptyScreen); //TextCtrl->Fit (); Fit (); } #if 0 // Print to screen at coordinates x,y. Clipping is performed, but // not wrapping. In other words, any call to Print() will go on // a single line. Don't try to defeat it by putting in newlines, // as this will just mess up the rationale by which the screen is // being managed. void SimpleFrameClass::Print (int x, int y, wxString &Value) { int From, To, Length, xend; // Sanity checking. Abort if the string will be completely // off the screen. The usual case is that everthing will be // fine and on-screen, so we do a quick check for that case. if (y < 0 || y >= RowsToUse) return; Length = Value.Length (); xend = x + Length; if (x < 0 || xend > TELEMETRY_COLUMNS) { // Perhaps it's completely off-screen! if (xend <= 0 || x >= TELEMETRY_COLUMNS) return; // No, at least partially on-screen. if (xend > TELEMETRY_COLUMNS) { Length -= (xend - TELEMETRY_COLUMNS); Value = Value.Left (Length); } if (x < 0) { Length += x; Value = Value.Right (Length); } } // All checked and/or clipped. Output it. From = TextCtrl->XYToPosition (x, y); To = From + Length; TextCtrl->Replace (From, To, Value); } #endif // Set font-size in the text control. void SimpleFrameClass::FontSize (int Points) { TextCtrl->SetFont(wxFont(Points, wxMODERN, wxNORMAL, wxNORMAL, 0, wxT(""))); } void SwriteTelemetry (void) { int i; wxString Dummy; for (i = 0; i < Sheight; i++) { Dummy = wxString::FromAscii (Sbuffer[i]); MainFrame->Print (0, i, Dummy); } } void SwriteTelemetrySimple (void) { int i; wxString Dummy; for (i = 0; i < Sheight; i++) { if (i > 0) Dummy += wxT ("\n"); Dummy += wxString::FromAscii (Sbuffer[i]); } SimpleFrame->TextCtrl->SetLabel (Dummy); } // This is the event handler for the background timer. The background timer // executes about every 100 ms. and services the socket interface. Upon // receiving downlink data, it updates the output screen. void TimerClass::Notify () { #ifdef DEBUG static int Count = 0; int j; wxString Dummy; j = Count % TELEMETRY_ROWS; Dummy = wxT ("Hello #"); Dummy << Count++; MainFrame->Print (2 * j, j, Dummy); #endif static int ServerSocket = -1; static unsigned char Packet[4]; static int PacketSize = 0; int i; unsigned char c; if (StartupDelay > 0) { StartupDelay -= PULSE_INTERVAL; return; } // Try to connect to the server (yaAGC) if not already connected. if (ServerSocket == -1) { ServerSocket = CallSocket (Hostname, Portnum); if (ServerSocket != -1) printf ("yaTelemetry is connected.\n"); } if (ServerSocket != -1) { for (;;) { i = recv (ServerSocket, (char *) &c, 1, MSG_NOSIGNAL); if (i == -1) { // The conditions i==-1,errno==0 or 9 occur only on Win32, // and I'm not sure exactly what they corresponds to---but // empirically I find that ignoring them makes no difference // to the operation of the program. if (errno == EAGAIN || errno == 0 || errno == 9) i = 0; else { //wxString Dummy; //Dummy << wxT ("yaTelemetry reports server error ") << errno; //wxMessageBox (Dummy); printf ("yaTerminal reports server error %d\n", errno); close (ServerSocket); ServerSocket = -1; break; } } if (i == 0) break; if (0 == (0xc0 & c)) PacketSize = 0; if (PacketSize != 0 || (0xc0 & c) == 0) { Packet[PacketSize++] = c; if (PacketSize >= 4) { ActOnIncomingIO (Packet); PacketSize = 0; } } } } } void TimerClass::ActOnIncomingIO (unsigned char *Packet) { int Channel, Value, uBit; // Check to see if the message has a yaAGC signature. If not, // ignore it. The yaAGC signature is 00 01 10 11 in the // 2 most-significant bits of the packet's bytes. We are // guaranteed that the first byte is signed 00, so we don't // need to check it. if (0x40 != (Packet[1] & 0xc0) || 0x80 != (Packet[2] & 0xc0) || 0xc0 != (Packet[3] & 0xc0)) return; if (ParseIoPacket (Packet, &Channel, &Value, &uBit)) goto Error; // Decode the digital downlink data. The way this works is that // DecodeDigitalDownlink() buffers the incoming downlink list, // and when it is fully buffered calls a callback function to // process it. The default library callback function is called // when MskType==0 ("raw" data). For other MskTypes, the // default processor is overridden and one of the ProcessMskXXXX() // functions from later in this file becomes the callback function // instead. if (Channel == 013 || Channel == 034 || Channel == 035) DecodeDigitalDownlink (Channel, Value, CmOrLm); return; Error: IoErrorCount++; } /////////////////////////////////////////////////////////////////////////////// // The stuff below this line was created by wxGlade, and could conceivably be // modified by wxGlade in the future. So try to confine useful code to the // area above this line, to the extent feasible. // begin wxGlade: ::extracode // end wxGlade MainFrameClass::MainFrameClass(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style): wxFrame(parent, id, title, pos, size, wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU) { // begin wxGlade: MainFrameClass::MainFrameClass bitmap_1 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("TelemetryLeft.jpg"), wxBITMAP_TYPE_ANY)); bitmap_5 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("TelemetryTop.jpg"), wxBITMAP_TYPE_ANY)); TextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxTE_RICH|wxNO_BORDER); bitmap_3 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("TelemetryBottom.jpg"), wxBITMAP_TYPE_ANY)); bitmap_2 = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxT("TelemetryRight.jpg"), wxBITMAP_TYPE_ANY)); set_properties(); do_layout(); // end wxGlade } BEGIN_EVENT_TABLE(MainFrameClass, wxFrame) // content of this block not found: did you rename this class? EVT_CLOSE(MainFrameClass::TimerStop) END_EVENT_TABLE(); void MainFrameClass::set_properties() { // begin wxGlade: MainFrameClass::set_properties SetTitle(wxT("yaTelemetry by Ron Burkey")); wxIcon _icon; _icon.CopyFromBitmap(wxBitmap(wxT("ApolloPatch2.png"), wxBITMAP_TYPE_ANY)); SetIcon(_icon); SetBackgroundColour(wxColour(0, 0, 0)); SetForegroundColour(wxColour(192, 192, 192)); SetFocus(); bitmap_1->SetBackgroundColour(wxColour(0, 0, 0)); bitmap_5->SetBackgroundColour(wxColour(0, 0, 0)); TextCtrl->SetBackgroundColour(wxColour(0, 0, 0)); TextCtrl->SetForegroundColour(wxColour(192, 192, 192)); TextCtrl->SetFont(wxFont(11, wxMODERN, wxNORMAL, wxNORMAL, 0, wxT(""))); bitmap_3->SetBackgroundColour(wxColour(0, 0, 0)); bitmap_2->SetBackgroundColour(wxColour(0, 0, 0)); // end wxGlade } void MainFrameClass::do_layout() { // begin wxGlade: MainFrameClass::do_layout wxBoxSizer* sizer_1 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* sizer_2 = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* sizer_3 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* sizer_4 = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* sizer_5 = new wxBoxSizer(wxVERTICAL); sizer_2->Add(bitmap_1, 0, 0, 0); sizer_3->Add(bitmap_5, 0, 0, 0); sizer_4->Add(15, 20, 0, 0, 0); sizer_5->Add(20, 30, 0, 0, 0); sizer_5->Add(TextCtrl, 1, wxEXPAND, 0); sizer_5->Add(20, 30, 0, 0, 0); sizer_4->Add(sizer_5, 1, wxEXPAND, 0); sizer_4->Add(15, 20, 0, 0, 0); sizer_3->Add(sizer_4, 1, wxEXPAND, 0); sizer_3->Add(bitmap_3, 0, 0, 0); sizer_2->Add(sizer_3, 1, wxEXPAND, 0); sizer_2->Add(bitmap_2, 0, 0, 0); sizer_1->Add(sizer_2, 1, wxEXPAND, 0); SetSizer(sizer_1); sizer_1->Fit(this); Layout(); // end wxGlade } class yaTelemetryApp: public wxApp { public: bool OnInit(); }; IMPLEMENT_APP(yaTelemetryApp) static wxApp *App; bool yaTelemetryApp::OnInit() { App = this; wxInitAllImageHandlers(); MainFrame = new MainFrameClass(NULL, wxID_ANY, wxEmptyString); SimpleFrame = new SimpleFrameClass (NULL, wxID_ANY, wxEmptyString); int i, Undecorated = 0, FontSizeSwitch = 0; printf ("\n"); printf ("yaTelemetry build " __DATE__ " " __TIME__ ", Copyright 2009 Ronald Burkey\n"); printf ("For more information, consult http://www.ibiblio.org/apollo.\n"); // Read the command-line arguments. Portnum = 19800; CmOrLm = 0; for (i = 1; i < argc; i++) { long IntValue; wxString Arg, Command, Value; Arg = argv[i]; Command = Arg.BeforeFirst ('='); Value = Arg.AfterFirst ('='); if (!Value.ToLong (&IntValue)) IntValue = 0; if (Arg.IsSameAs (wxT ("--help"))) { Help: printf ("\n"); printf ("This program provides a terminal on which to view downlinked telemetry\n"); printf ("from the AGC, which is to say from a running yaAGC server.\n"); printf ("\n"); printf ("USAGE:\n"); printf (" yaTelemetry [OPTIONS]\n"); printf ("\n"); printf ("The available options are:\n"); printf ("\n"); printf (" --help Displays this message.\n"); printf (" --delay=MS Provides a delay after startup before\n"); printf (" attempting to connect to the yaAGC server.\n"); printf (" This allows elimination of certain race\n"); printf (" conditions which can occur when starting up\n"); printf (" a bunch of Virtual AGC servers and clients\n"); printf (" using scripts. MS is the delay time, in \n"); printf (" milliseconds. It defaults to 500 in Windows\n"); printf (" and 0 otherwise.\n"); printf (" --port=P The port number on which to try and set up a\n"); printf (" socket to the listening yaAGC server. By\n"); printf (" convention, the virtual CM uses ports 19697\n"); printf (" through 19706, whilst the virutal LM uses\n"); printf (" ports 19797-19806. By default, yaTelemetry\n"); printf (" uses 19800.\n"); printf (" --ip=H The hostname or IP address of the yaAGC server\n"); printf (" to which yaTelemetry should connect. The\n"); printf (" default is \"localhost\".\n"); printf (" --spacecraft=S The type of spacecraft, either LM or CM. This\n"); printf (" defaults to LM.\n"); printf (" --font-size=P Font size, in points. Defaults to %d on this\n", DEFAULT_FONTSIZE_RETRO); printf (" platform for the default or --undecorated style,\n"); printf (" and to %d for the --simple style (see below).\n", DEFAULT_FONTSIZE_RESIZABLE); printf (" Generally, for the default or --undecorated style,\n"); printf (" the size should be chosen as the maximum for which\n"); printf (" scrollbars do not appear. For the --simple style,\n"); printf (" the minimum size for comfortable reading should be\n"); printf (" selected.\n"); printf (" --undecorated This option is not recommended; you should use\n"); printf (" --simple instead. What this option does is to\n"); printf (" remove decorations (the fake console around the\n"); printf (" displayed data), in order to reduce amount of screen\n"); printf (" space occupied. If the screen height is too\n"); printf (" small to contain the decorations, then this \n"); printf (" switch is automatically selected. It is also\n"); printf (" the default on Mac OS X.\n"); printf (" --simple Produces a simple text display, allowing dynamic\n"); printf (" resizing of the text. This is by far the best choice\n"); printf (" when there is limited display-screen real estate.\n"); printf ("\n"); fflush (stdout); return (false); } else if (Command.IsSameAs (wxT ("--delay"))) StartupDelay = IntValue; else if (Command.IsSameAs (wxT ("--port"))) Portnum = IntValue; else if (Command.IsSameAs (wxT ("--ip"))) { strcpy (NonDefaultHostname, Value.char_str ()); Hostname = NonDefaultHostname; } else if (Command.IsSameAs (wxT ("--spacecraft"))) { Value = Value.Lower (); if (Value.IsSameAs (wxT ("lm"))) CmOrLm = 0; else if (Value.IsSameAs (wxT ("cm"))) CmOrLm = 1; else { char s[129]; strcpy (s, Arg.char_str ()); printf ("Illegal command-line option: %s\n", s); goto Help; } } else if (Command.IsSameAs (wxT ("--font-size"))) { Points = IntValue; FontSizeSwitch = 1; } else if (Command.IsSameAs (wxT ("--undecorated"))) Undecorated = 1; else if (Command.IsSameAs (wxT ("--simple"))) { Simple = true; if (!FontSizeSwitch) Points = DEFAULT_FONTSIZE_RESIZABLE; } else // Illegal option. { char s[129]; strcpy (s, Arg.char_str ()); printf ("Unrecognized command-line option: %s\n", s); goto Help; } } printf (" --delay=%d\n", StartupDelay); printf (" --port=%d\n", Portnum); printf (" --ip=%s\n", Hostname); if (CmOrLm) printf (" --spacecraft=CM\n"); else printf (" --spacecraft=LM\n"); printf (" --font-size=%d\n", Points); #ifdef __APPLE__ Undecorated = 1; #endif if (Undecorated) printf (" --undecorated\n"); if (Simple) printf (" --simple\n"); int x, y, height, width; wxClientDisplayRect (&x, &y, &width, &height); if (height < 1040) Undecorated = 1; printf (" height=%d\n", height); fflush (stdout); if (Simple) { SimpleFrame->FontSize (Points); SetTopWindow(SimpleFrame); extern Swrite_t *SwritePtr; SwritePtr = SwriteTelemetrySimple; SimpleFrame->ClearScreen (); SimpleFrame->Timer = new TimerClass (); SimpleFrame->Timer->Start (PULSE_INTERVAL); SimpleFrame->Show(); } else { MainFrame->FontSize (Points); if (Undecorated) MainFrame->Undecorate (); SetTopWindow(MainFrame); extern Swrite_t *SwritePtr; SwritePtr = SwriteTelemetry; MainFrame->ClearScreen (); MainFrame->Timer = new TimerClass (); MainFrame->Timer->Start (PULSE_INTERVAL); MainFrame->Show(); } return true; } SimpleFrameClass::SimpleFrameClass(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style): wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE) { // begin wxGlade: SimpleFrameClass::SimpleFrameClass panel_1 = new wxPanel(this, wxID_ANY); sizer_10_staticbox = new wxStaticBox(panel_1, -1, wxT("Change text size")); Bigger = new wxButton(panel_1, ID_BIGGER, wxT("Bigger")); Smaller = new wxButton(panel_1, ID_SMALLER, wxT("Smaller")); const wxString DecodingBox_choices[] = { wxT("Raw downlinked data"), wxT("MSK-683 (CM) CRT display"), wxT("MSK-966 (CM) CRT display"), wxT("MSK-1123 (LM) CRT display"), wxT("MSK-1137 (LM) CRT display") }; DecodingBox = new wxRadioBox(panel_1, ID_DECODINGBOX, wxT("Downlink formatting"), wxDefaultPosition, wxDefaultSize, 5, DecodingBox_choices, 0, wxRA_SPECIFY_ROWS); TextCtrl = new wxStaticText(this, wxID_ANY, wxEmptyString); set_properties(); do_layout(); // end wxGlade MskType = 0; RowsToUse = TELEMETRY_ROWS; } BEGIN_EVENT_TABLE(SimpleFrameClass, wxFrame) // begin wxGlade: SimpleFrameClass::event_table EVT_BUTTON(ID_BIGGER, SimpleFrameClass::BiggerPressed) EVT_BUTTON(ID_SMALLER, SimpleFrameClass::SmallerPressed) EVT_RADIOBOX(ID_DECODINGBOX, SimpleFrameClass::FormattingBoxEvent) // end wxGlade EVT_CLOSE(SimpleFrameClass::TimerStop) END_EVENT_TABLE(); // wxGlade: add SimpleFrameClass event handlers void SimpleFrameClass::BiggerPressed(wxCommandEvent &event) { FontSize (++Points); Fit (); } void SimpleFrameClass::SmallerPressed(wxCommandEvent &event) { if (Points > 6) { FontSize (--Points); Fit (); } } void SimpleFrameClass::FormattingBoxEvent(wxCommandEvent &event) { switch (DecodingBox->GetSelection ()) { case 1: MskType = 683 ; ProcessDownlinkList = PrintMsk683; break; case 2: MskType = 966 ; ProcessDownlinkList = PrintMsk966; break; case 3: MskType = 1123 ; ProcessDownlinkList = PrintMsk1123; break; case 4: MskType = 1137 ; ProcessDownlinkList = PrintMsk1137; break; default: MskType = 0; ProcessDownlinkList = PrintDownlinkList; break; } if (MskType) { // The MSK-type displays --- or at least, my verions of them --- // each have 33 rows. RowsToUse = 33; } else { // TELEMETRY_ROWS is the maximum possible number of // screen rows, and was tailored for raw (lightly-formatted) // downlinked data. RowsToUse = TELEMETRY_ROWS; } ClearScreen (); Hide (); InvalidateBestSize (); Layout (); Fit (); Refresh (); Update (); Show (); } // wxGlade: add MainFramClass event handlers> void MainFrameClass::TimerStop(wxCloseEvent &event) { Timer->Stop (); delete Timer; event.Skip (); App->ExitMainLoop (); } void SimpleFrameClass::TimerStop(wxCloseEvent &event) { Timer->Stop (); delete Timer; event.Skip (); App->ExitMainLoop (); } void SimpleFrameClass::set_properties() { // begin wxGlade: SimpleFrameClass::set_properties SetTitle(wxT("yaTelemetry by Ron Burkey")); wxIcon _icon; _icon.CopyFromBitmap(wxBitmap(wxT("ApolloPatch2.png"), wxBITMAP_TYPE_ANY)); SetIcon(_icon); Bigger->SetToolTip(wxT("Make text bigger by clicking this button.")); Smaller->SetToolTip(wxT("Make text smaller by clicking this button.")); DecodingBox->SetToolTip(wxT("Use this box to select how the on-screen downlinked telemetry data is formatted. The MSK settings provide our approximations to the actual CRT displays used by Apollo Program ground-control CRTs. The \"raw\" setting has little formatting but insures that all of the downlinked data is displayed.")); DecodingBox->Enable(false); DecodingBox->SetSelection(0); panel_1->SetBackgroundColour(wxColour(255, 255, 255)); TextCtrl->SetBackgroundColour(wxColour(255, 255, 255)); TextCtrl->SetForegroundColour(wxColour(0, 0, 0)); // end wxGlade } void SimpleFrameClass::do_layout() { // begin wxGlade: SimpleFrameClass::do_layout wxBoxSizer* sizer_6 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* sizer_7 = new wxBoxSizer(wxVERTICAL); wxBoxSizer* sizer_7_copy = new wxBoxSizer(wxHORIZONTAL); wxStaticBoxSizer* sizer_10 = new wxStaticBoxSizer(sizer_10_staticbox, wxHORIZONTAL); wxBoxSizer* sizer_11 = new wxBoxSizer(wxVERTICAL); sizer_7->Add(20, 10, 0, 0, 0); sizer_7_copy->Add(20, 20, 3, 0, 0); sizer_10->Add(20, 20, 0, wxADJUST_MINSIZE, 0); sizer_11->Add(20, 5, 1, wxADJUST_MINSIZE, 0); sizer_11->Add(Bigger, 0, 0, 0); sizer_11->Add(20, 5, 1, wxADJUST_MINSIZE, 0); sizer_11->Add(Smaller, 0, 0, 0); sizer_11->Add(20, 5, 1, wxADJUST_MINSIZE, 0); sizer_10->Add(sizer_11, 1, wxEXPAND, 0); sizer_10->Add(20, 20, 0, wxADJUST_MINSIZE, 0); sizer_7_copy->Add(sizer_10, 0, wxEXPAND, 0); sizer_7_copy->Add(10, 20, 1, 0, 0); sizer_7_copy->Add(DecodingBox, 0, wxADJUST_MINSIZE, 0); sizer_7_copy->Add(20, 20, 3, 0, 0); sizer_7->Add(sizer_7_copy, 0, wxEXPAND, 0); sizer_7->Add(20, 10, 0, 0, 0); panel_1->SetSizer(sizer_7); sizer_6->Add(panel_1, 0, wxEXPAND, 0); sizer_6->Add(TextCtrl, 1, wxEXPAND, 0); SetSizer(sizer_6); sizer_6->Fit(this); Layout(); // end wxGlade } // Function and data structures to parse a buffered downlink list into // meaningful data fields. #if 0 typedef struct { int adot[3]; int agsbuff[12]; int agsk; int aig; int ak[3]; int alfa180; int alphaq, alphar; int alt; int amg; int aog; int aotcode; int at; int besti, bestj; int beta180; int c32flwrd; int cadrflsh[3]; int cdus; int cdux, cduy, cduz, cdut; int cduxd, cduyd, cduzd; int centang; int chan77; int chn11, chn12, chn13, chn14, chn30, chn31, chn32, chn33; int cmdapmod; int cm_epoch; int compnumb; int csmmass; int csteer; int dapbools; int dapdatr1, dapdatr2; int dellt4; int deltah; int deltar; int delv[3]; int delveet[3], delveet1[3], delveet2[3], delveet3[3]; int delvslv[3]; int delvtpf; int diffalt; int dnlralt; int dnlrvelx, dnlrvely, dnlrvelz; int dnrange; int dnrrdot; int dsptb[6]; int ecsteer; int eight_nn; int elev; int errorx, errory, errorz; int failreg[3]; int fc; int gammaei; int gsav[3]; int hapox; int hmeas; int holdflag; int hperx; int id; int igc; int imodes30, imodes33; int land[3]; int landmark; int lastxcmd, lastycmd; int lat; int latang; int lat_spl; int launchaz; int lemmass; int lm_epoch; int lng_spl; int long_; int lrxcdudl, lrycdudl, lrzcdudl; int lrvtimdl; int l_d1; int mark2dwn[6]; int markdown[6]; int mgc; int mktime; int negtorku, negtorkv, negtorkp; int nn; int offset; int ogc; int omegap, omegaq, omegar; int omegapd, omegaqd, omegard; int option1, option2; int optmodes; int pactoff; int paxerr1; int pcmd; int pipax, pipay, pipaz; int piptime, piptime1; int postorku, postorkv, postorkp; int prel, qrel, rrel; int pseudo55; int radmodes; int range; int rangrdot; int rcsflags; int rdot; int redoctr; int refsmmat[6]; int rgu[3]; int rls[3]; int rm; int rn[3]; int rollc; int rolltm; int rrate; int rsbbq[2]; int rtarg[3]; int rtargx, rtargy, rtargz; int rtheta; int r_other[3]; int starsav1[3], starsav2[3]; int state[6]; int svmrkdat[31]; int sync; int talign; int tangnb[2]; int tcdh; int tcsi; int tet; int tevent; int tgo; int thetad[3]; int thetadx, thetady, thetadz; int thetah; int thetax, thetay, thetaz; int tig; int time; int tland; int tpass4; int trkmkcnt; int tte; int ttf8; int ttogo; int ttpf; int ttpi; int t_other; int unfc2[3]; int upbuf[10]; int upcount; int upoldmod; int upverb; int vgtig[3]; int vgvect[2]; int vgu[3]; int vhfcount; int vhftime; int vtio; int vmeas; int vn[3]; int vpred; int vselect; int vtig[3]; int v_other[3]; int wbody[3]; int x789[2]; int yactoff; int ycmd; int ynbsav[3]; int zdotd; int znbsav[3]; } ParsedDownlinkList_t; static ParsedDownlinkList_t ParsedDownlinkList; #endif // 0 void ParseDownlinkList (const DownlinkListSpec_t *Spec) { } // MSK-based replacements for the low-level function that prints a downlink list // that has been buffered in memory. void PrintMsk683 (const DownlinkListSpec_t *Spec) { ParseDownlinkList (Spec); int i; //sprintf (&Sbuffer[0][0], "%s", Spec->Title); for (i = 0; i < MAX_DOWNLINK_LIST; i++) { if (i && !Spec->FieldSpecs[i].IndexIntoList) break; // End of field-list. //PrintField (&Spec->FieldSpecs[i]); } } void PrintMsk966 (const DownlinkListSpec_t *Spec) { ParseDownlinkList (Spec); int i; //sprintf (&Sbuffer[0][0], "%s", Spec->Title); for (i = 0; i < MAX_DOWNLINK_LIST; i++) { if (i && !Spec->FieldSpecs[i].IndexIntoList) break; // End of field-list. //PrintField (&Spec->FieldSpecs[i]); } } void PrintMsk1123 (const DownlinkListSpec_t *Spec) { ParseDownlinkList (Spec); int i; //sprintf (&Sbuffer[0][0], "%s", Spec->Title); for (i = 0; i < MAX_DOWNLINK_LIST; i++) { if (i && !Spec->FieldSpecs[i].IndexIntoList) break; // End of field-list. //PrintField (&Spec->FieldSpecs[i]); } } void PrintMsk1137 (const DownlinkListSpec_t *Spec) { ParseDownlinkList (Spec); int i; //sprintf (&Sbuffer[0][0], "%s", Spec->Title); for (i = 0; i < MAX_DOWNLINK_LIST; i++) { if (i && !Spec->FieldSpecs[i].IndexIntoList) break; // End of field-list. //PrintField (&Spec->FieldSpecs[i]); } }