#if 0 ############################# ## ## BAORadio Indi driver ## Franck RICHARD ## Mai 2010 ## ############################# #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "indicom.h" #include "Socket.h" #include "BAO.h" using namespace std; auto_ptr telescope(0); const int POLLMS = 1; // Period of update, 1 ms. const char *mydev = "BAO"; // Name of our device. const char *BASIC_GROUP = "Main Control"; // Main Group const char *OPTIONS_GROUP = "Options"; // Options Group /* Handy Macros */ //#define currentRA EquatorialCoordsRN[0].value //#define currentDEC EquatorialCoordsRN[1].value #define targetRA EquatorialCoordsWN[0].value #define targetDEC EquatorialCoordsWN[1].value static void ISPoll(void *); static void retry_connection(void *); /************************************************************************************** ** ***************************************************************************************/ void *pThreadSocket (void * arg) { try { int pos = SocketsNumber; // Trouver éventuellement un emplacement disponible dans l'intervalle [1..SocketsNumber] /* for (int i=1; iISGetProperties(dev); } /************************************************************************************** ** ***************************************************************************************/ void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) { ISInit(); telescope->ISNewSwitch(dev, name, states, names, n); } /************************************************************************************** ** ***************************************************************************************/ void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n) { ISInit(); telescope->ISNewText(dev, name, texts, names, n); } /************************************************************************************** ** ***************************************************************************************/ void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n) { ISInit(); telescope->ISNewNumber(dev, name, values, names, n); } /************************************************************************************** ** ***************************************************************************************/ void ISPoll (void *p) { INDI_UNUSED(p); telescope->ISPoll(); IEAddTimer (POLLMS, ISPoll, NULL); } /************************************************************************************** ** ***************************************************************************************/ void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) { INDI_UNUSED(dev); INDI_UNUSED(name); INDI_UNUSED(sizes); INDI_UNUSED(blobsizes); INDI_UNUSED(blobs); INDI_UNUSED(formats); INDI_UNUSED(names); INDI_UNUSED(n); } /************************************************************************************** ** ***************************************************************************************/ void ISSnoopDevice (XMLEle *root) { INDI_UNUSED(root); } /************************************************************************************** ** Initialisation de la classe BAO ***************************************************************************************/ BAO::BAO() { init_properties(); ConnectSP.s = IPS_IDLE; lastSet = -1; fd = -1; simulation = false; lastRA = 0; lastDEC = 0; currentSet = 0; JJAnc = 0.0; SocketsNumber = 1; ActualisationTM1 = 15.0*60.0; ActualisationTM2 = 5.0; InitThreadOK=false; LecturePosition=false; Abort=false; Park=false; Goto=false; LastGotoOK=true; TrackingMode=1; for (int i=0; i= -90.0 && Latitude <= 90.0; Latitude*=Pidiv180; } else if (eqp == &GeographicCoordsWN[1]) { Longitude = values[i]; nset += Longitude >= 0.0 && Longitude <= 360.0; Longitude*=-Pidiv180; } } if (nset == 2) { //Vérification //IDLog("Geographic : RA %5.2f - DEC %5.2f\n", Latitude, Longitude); GeographicCoordsWNP.s = IPS_OK; IDSetNumber(&GeographicCoordsWNP, NULL); } else { GeographicCoordsWNP.s = IPS_ALERT; IDSetNumber(&GeographicCoordsWNP, "Latitude or Longitude missing or invalid"); Latitude=0.0; Longitude=0.0; } return; } // =================================== // Equatorial Coords // =================================== if (!strcmp (name, EquatorialCoordsWNP.name)) { int i=0, nset=0, error_code=0; double newRA =0, newDEC =0; for (nset = i = 0; i < n; i++) { INumber *eqp = IUFindNumber (&EquatorialCoordsWNP, names[i]); if (eqp == &EquatorialCoordsWN[0]) { newRA = values[i]; nset += newRA >= 0 && newRA <= 24.0; } else if (eqp == &EquatorialCoordsWN[1]) { newDEC = values[i]; nset += newDEC >= -90.0 && newDEC <= 90.0; } } targetRA = newRA; targetDEC = newDEC; if (nset == 2) { char RAStr[32], DecStr[32]; double targetAZ, targetAlt; fs_sexa(RAStr, newRA, 2, 3600); fs_sexa(DecStr, newDEC, 2, 3600); IDLog("We received JNow RA %s - DEC %s\n", RAStr, DecStr); // on convertit les coordonnées équatoriales de la zone du ciel observée // en unités de codeurs des moteurs ADDEC2Motor(newRA, newDEC); if (process_coords() == false) { EquatorialCoordsWNP.s = IPS_ALERT; IDSetNumber(&EquatorialCoordsWNP, NULL); } } else { EquatorialCoordsWNP.s = IPS_ALERT; IDSetNumber(&EquatorialCoordsWNP, "Error ! RA or Dec missing or invalid"); } return; } // =================================== // Actualisation // =================================== if (!strcmp (name, ActualisationNP1.name)) { int i=0, nset=0, error_code=0; double newAct1 =0; for (nset = i = 0; i < n; i++) { INumber *eqp = IUFindNumber (&ActualisationNP1, names[i]); if (eqp == &ActualisationN1[0]) { newAct1 = values[i]; if (newAct1 >= 0.0 && newAct1 <= 3600.0) { ActualisationTM1=newAct1; ActualisationNP1.s = IPS_OK; IDSetNumber(&ActualisationNP1, NULL); } else { ActualisationNP1.s = IPS_ALERT; IDSetNumber(&ActualisationNP1, "Error ! Delay invalid"); } } } } if (!strcmp (name, ActualisationNP2.name)) { int i=0, nset=0, error_code=0; double newAct2 =0; for (nset = i = 0; i < n; i++) { INumber *eqp = IUFindNumber (&ActualisationNP2, names[i]); if (eqp == &ActualisationN2[0]) { newAct2 = values[i]; if (newAct2 >= 0.0 && newAct2 <= 3600.0) { ActualisationTM2=newAct2; ActualisationNP2.s = IPS_OK; IDSetNumber(&ActualisationNP2, NULL); } else { ActualisationNP2.s = IPS_ALERT; IDSetNumber(&ActualisationNP2, "Error ! Delay invalid"); } } } } /* end EquatorialCoordsWNP */ } /************************************************************************************** ** L'utilisateur clique sur l'un des boutons de la boîte Indi ***************************************************************************************/ void BAO::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) { // ignore if not ours // if (strcmp (mydev, dev)) return; // =================================== // Connect Switch // =================================== if (!strcmp (name, ConnectSP.name)) { if (IUUpdateSwitch(&ConnectSP, states, names, n) < 0) return; connect_telescope(); return; } if (is_connected() == false) { IDMessage(mydev, "Error ! BAORadio is offline. Please connect before issuing any commands."); reset_all_properties(); return; } // =================================== // Coordinate Set // =================================== if (!strcmp(name, OnCoordSetSP.name)) { if (IUUpdateSwitch(&OnCoordSetSP, states, names, n) < 0) return; currentSet = get_switch_index(&OnCoordSetSP); OnCoordSetSP.s = IPS_OK; IDSetSwitch(&OnCoordSetSP, NULL); } // =================================== // Abort slew // =================================== if (!strcmp (name, AbortSlewSP.name)) { Abort=true; IUResetSwitch(&AbortSlewSP); /* if (EquatorialCoordsWNP.s == IPS_BUSY) { AbortSlewSP.s = IPS_OK; EquatorialCoordsWNP.s = IPS_IDLE; EquatorialCoordsRNP.s = IPS_IDLE; HorizontalCoordsWNP.s = IPS_IDLE; IDSetSwitch(&AbortSlewSP, "Slew aborted."); IDSetNumber(&EquatorialCoordsWNP, NULL); IDSetNumber(&EquatorialCoordsRNP, NULL); IDSetNumber(&HorizontalCoordsWNP, NULL); } */ return; } // =================================== // Park // =================================== if (!strcmp (name, ParkSP.name)) { Park=true; IUResetSwitch(&ParkSP); /* if (EquatorialCoordsWNP.s == IPS_BUSY) { AbortSlewSP.s = IPS_OK; EquatorialCoordsWNP.s = IPS_IDLE; EquatorialCoordsRNP.s = IPS_IDLE; HorizontalCoordsWNP.s = IPS_IDLE; IDSetSwitch(&AbortSlewSP, "Slew aborted."); IDSetNumber(&EquatorialCoordsWNP, NULL); IDSetNumber(&EquatorialCoordsRNP, NULL); IDSetNumber(&HorizontalCoordsWNP, NULL); } */ return; } } /************************************************************************************** ** fct peut-être inutile ***************************************************************************************/ void BAO::handle_error(INumberVectorProperty *nvp, int err, const char *msg) { nvp->s = IPS_ALERT; /* If the error is a time out, then the device doesn't support this property */ if (err == -2) { nvp->s = IPS_ALERT; IDSetNumber(nvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg); } else /* Changing property failed, user should retry. */ IDSetNumber( nvp , "%s failed.", msg); fault = true; } /************************************************************************************** ** Initialisation des vecteurs INDI ***************************************************************************************/ void BAO::reset_all_properties() { ConnectSP.s = IPS_IDLE; OnCoordSetSP.s = IPS_IDLE; AbortSlewSP.s = IPS_IDLE; ParkSP.s = IPS_IDLE; ObjectTP.s = IPS_IDLE; EquatorialCoordsWNP.s = IPS_IDLE; //EquatorialCoordsRNP.s = IPS_IDLE; GeographicCoordsWNP.s = IPS_IDLE; //SlewAccuracyNP.s = IPS_IDLE; //TrackAccuracyNP.s = IPS_IDLE; ActualisationNP1.s = IPS_IDLE; ActualisationNP2.s = IPS_IDLE; IUResetSwitch(&OnCoordSetSP); IUResetSwitch(&AbortSlewSP); IUResetSwitch(&ParkSP); OnCoordSetS[0].s = ISS_ON; ConnectS[0].s = ISS_OFF; ConnectS[1].s = ISS_ON; IDSetSwitch(&ConnectSP, NULL); IDSetSwitch(&OnCoordSetSP, NULL); IDSetSwitch(&AbortSlewSP, NULL); IDSetSwitch(&ParkSP, NULL); IDSetText(&ObjectTP, NULL); IDSetNumber(&EquatorialCoordsWNP, NULL); //IDSetNumber(&EquatorialCoordsRNP, NULL); IDSetNumber(&GeographicCoordsWNP, NULL); //IDSetNumber(&SlewAccuracyNP, NULL); //IDSetNumber(&TrackAccuracyNP, NULL); IDSetNumber(&ActualisationNP1, NULL); IDSetNumber(&ActualisationNP2, NULL); } /************************************************************************************** ** ***************************************************************************************/ void BAO::correct_fault() { fault = false; IDMessage(mydev, "Telescope is online."); } /************************************************************************************** ** ***************************************************************************************/ bool BAO::is_connected() { if (simulation) return true; // return (ConnectSP.sp[0].s == ISS_ON); return (ConnectSP.s == IPS_OK); } /************************************************************************************** ** ***************************************************************************************/ static void retry_connection(void * p) { } /************************************************************************************** ** Extraction de la position de l'antenne ** dans le retour de la commande POS ** POS/Valeur az/Valeur alt ***************************************************************************************/ Position BAO::ExtractPosition(std::string str) { Position result; std::string str2; result.x = -1; result.y = -1; int pos = str.find("/"); if (pos != string::npos) { str2 = str.substr(pos+1); pos = str2.find("/"); if (pos != string::npos) { result.x = atoi(str2.substr(0, pos).c_str()); result.y = atoi(str2.substr(pos+1).c_str()); } } return result; } /************************************************************************************ * cette procédure convertit les coordonnées equatoriales de l'objet visé * en unités de codeurs des paraboles (nb de tours des deux axes moteurs depuis la position PARK) ************************************************************************************/ void BAO::ADDEC2Motor(double newRA, double newDEC) { double targetAz; double targetAlt; char AzStr[32]; char AltStr[32]; // Calcule la hauteur et l'azimut de la zone du ciel pointée (en fonction de la date et du lieu) Azimut(tsl, Latitude, newRA * 15.0 * Pidiv180, newDEC * Pidiv180, &targetAz, &targetAlt); // En degrés targetAlt *= N180divPi; targetAz *= N180divPi; // Affichage dans les logs fs_sexa(AzStr, targetAz, 2, 3600); fs_sexa(AltStr, targetAlt, 2, 3600); IDLog("Horizontal coords : az %s - Alt %s\n", AzStr, AltStr); // Le calcul est ici très sommaire et arbitraire : // Je considère qu'il y a 100 positions possibles sur les deux axes // De plus, je considère qu'il n'est pas possible de viser un objet à // moins de 30° de hauteur au-dessus de l'horizon TargetPosition.x=(int)(targetAz*100.0/360.0); targetAlt=((90.0-targetAlt)/60.0); if (targetAlt>=1.0) { targetAlt=1.0; //on ne peut pas viser un objet situé à moins de 30° //au-dessus de l'horizon IDSetSwitch(&OnCoordSetSP, "Erreur ! L objet suivi est situe a moins de 30° au-dessus de l horizon. Goto annule."); Goto=false; LecturePosition=false; InitAntennes(); } TargetPosition.y=(int)(100.0*targetAlt); } /************************************************************************************ * Retourne simplement le nombre d'antennes connectées * et capables de communiquer ************************************************************************************/ int BAO::AntennesConnectees() { int num=0; for (int i=1; imemSocketsNumber) { memSocketsNumber=SocketsNumber; IDSetSwitch(&ConnectSP, "Connexion de l antenne %s. (Antennes connectees : %i)", Sockets[SocketsNumber-1].IP.c_str(), AntennesConnectees()); } // Début des échanges avec les microcontrôleurs // Analyse des réponses des microcontrôleurs for (int i=1; i> reponse; //IDSetSwitch(&OnCoordSetSP, "Réponse ISPOLL : %s\n", reponse.c_str()); // pour vérif //Dans le cas où plusieurs trames seraient arrivées entre deux appels de POLLMS //les traiter successivement pos=reponse.find("\n"); // d'où l'intérêt de mettre un '\n' à la fin des trames //pour différencier une trame de la précédente while ((pos!=string::npos) && (reponse.length()>1)) { memreponse=reponse.substr(pos+1); reponse=reponse.substr(0, pos); // On traite ici les acknowledges if ((reponse.find("ACK")!=string::npos) && (reponse.find("NACK")==string::npos)) { if (reponse.find("POSITION")!=string::npos) { Sockets[i].ack_pos=true; } else if (reponse.find("GOTO")!=string::npos) { Sockets[i].ack_goto=true; } else if (reponse.find("PARK")!=string::npos) { Sockets[i].ack_park=true; } else if (reponse.find("ABORT")!=string::npos) { Sockets[i].ack_abort=true; } } else { //réponse à la requête POSITION if (reponse.find("POSITION")!=string::npos) { if (reponse.find("NACK")!=string::npos) { OnCoordSetSP.s = IPS_ALERT; IDSetSwitch(&OnCoordSetSP, "ALERTE antenne %s : position de l antenne inconnue !\n", Sockets[i].IP.c_str()); Sockets[i].PosValides=false; // Si la position de l'antenne est inconnue, on déconnecte l'antenne Sockets[i].Connected=false; } else if (Sockets[i].ack_pos) { OnCoordSetSP.s = IPS_OK; Sockets[i].Pos = ExtractPosition(reponse); Sockets[i].PosValides = true; IDSetSwitch(&ParkSP, "Antenne %s : POSITION OK (x=%i, y=%i)\n", Sockets[i].IP.c_str(), Sockets[i].Pos.x, Sockets[i].Pos.y); } }//réponse à la requête PARK else if (reponse.find("PARK")!=string::npos) { if (reponse.find("NACK")!=string::npos) { ParkSP.s = IPS_ALERT; IDSetSwitch(&ParkSP, "ALERTE antenne %s : erreur PARK !\n", Sockets[i].IP.c_str()); } else if (reponse.find("OK")!=string::npos) { ParkSP.s = IPS_OK; IDSetSwitch(&ParkSP, "Antenne %s : PARK OK\n", Sockets[i].IP.c_str()); } }//réponse à la requête ABORT else if (reponse.find("ABORT")!=string::npos) { if (reponse.find("NACK")!=string::npos) { AbortSlewSP.s = IPS_ALERT; IDSetSwitch(&AbortSlewSP, "ALERTE antenne %s : erreur ABORT !\n", Sockets[i].IP.c_str()); } if (reponse.find("OK")!=string::npos) { AbortSlewSP.s = IPS_OK; IDSetSwitch(&AbortSlewSP, "Antenne %s : ABORT OK\n", Sockets[i].IP.c_str()); } } //réponse à la requête GOTO else if (reponse.find("GOTO")!=string::npos) { if (reponse.find("NACK")!=string::npos) { OnCoordSetSP.s = IPS_ALERT; IDSetSwitch(&OnCoordSetSP, "ALERTE antenne %s : Erreur GOTO !\n", Sockets[i].IP.c_str()); Sockets[i].Connected=false; } else if (Sockets[i].ack_goto) { if (reponse.find("OK")!=string::npos) { OnCoordSetSP.s = IPS_OK; Sockets[i].GotoOk=true; IDSetSwitch(&ParkSP, "Antenne %s : GOTO OK.\n", Sockets[i].IP.c_str()); lastRA = targetRA; lastDEC = targetDEC; //IDLog("We received JNow RA %s - DEC %s\n", RAStr, DecStr);*/ EquatorialCoordsWNP.s = IPS_OK; IDSetNumber (&EquatorialCoordsWNP, NULL); // Fin du Goto pour toutes les antennes ? int num=0; for (int j=1; j0)) { LecturePosition=false; InitAntennes(); LastGotoOK=true; IDSetSwitch(&OnCoordSetSP, "GOTO OK !"); } } } } } // On passe éventuellement à la trame suivante reponse=memreponse; pos=reponse.find("\n"); } } catch (SocketException& e) //Aïe { Sockets[i].new_sock.shutdown(); Sockets[i].new_sock.create(); Sockets[i].Connected = Sockets[i].new_sock.connect((std::string)Sockets[i].IP); if (Sockets[i].Connected) { Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution=0; } std::string oss; oss="SocketException IsPoll : " + e.description() + "\n"; size_t size = oss.size() + 1; char* buffer = new char[size]; strncpy(buffer, oss.c_str(), size); IDLog(buffer); delete [] buffer; } } if (Abort) { IDSetSwitch(&ConnectSP, "Envoi de la commande Abort\n"); Goto=false; for (int i=1; i delai) { LastGotoOK=false; LecturePosition=true; ADDEC2Motor(targetRA, targetDEC); InitAntennes(); JJAnc=JJ; } //Plus d'antenne ! if (AntennesConnectees() == 0) { // LecturePosition=false; // Goto=false; // InitAntennes(); if ( compt % 1000 == 0) IDSetSwitch(&OnCoordSetSP, "Erreur ! Plus d antennes connectees !"); } } // Exécution de la procédure complète de lecture de la position de l'antenne // puis envoi d'une commande Goto if (LecturePosition) { for (int i=1; iMAXATTENTE) { Sockets[i].etape=0; Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution++; } if (Sockets[i].AnomaliesExecution>MAXANOMALIES) { Sockets[i].etape=3; // if ( compt % 1000 == 0) { IDSetSwitch(&OnCoordSetSP, "Erreur sur l antenne %s : pas d acknowledge recu apres l ordre POSITION. \ Deconnexion de l antenne.", Sockets[i].IP.c_str()); Sockets[i].Connected=false; } } } } break; //Valeurs pos valides ? case 2 : { if (Sockets[i].PosValides) { Sockets[i].AttenteExecution=0; Sockets[i].etape++; } else { // on réitère l'ordre précédent si rien ne se passe Sockets[i].AttenteExecution++; if (Sockets[i].AttenteExecution>MAXATTENTE) { Sockets[i].etape=2; Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution++; } if (Sockets[i].AnomaliesExecution>MAXANOMALIES) { Sockets[i].etape=3; // if ( compt % 1000 == 0) { IDSetSwitch(&OnCoordSetSP, "Erreur sur l antenne %s : la position retournee n est pas valide. \ Deconnexion de l antenne.", Sockets[i].IP.c_str()); Sockets[i].Connected=false; } } } } break; //ack goto ? case 4 : { if (Sockets[i].ack_goto) { Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution=0; Sockets[i].etape++; } else { // on réitère l'ordre précédent si rien ne se passe Sockets[i].AttenteExecution++; if (Sockets[i].AttenteExecution>MAXATTENTE) { Sockets[i].etape=4; Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution++; } if (Sockets[i].AnomaliesExecution>MAXANOMALIES) { Sockets[i].etape=6; // if ( compt % 1000 == 0) { IDSetSwitch(&OnCoordSetSP, "Erreur sur l antenne %s : pas d acknowledge recu apres l ordre GOTO. \ Deconnexion de l antenne.", Sockets[i].IP.c_str()); Sockets[i].Connected=false; } } } } break; //goto ok ? case 5 : { if (Sockets[i].GotoOk) { Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution=0; Sockets[i].etape++; } else { // on réitère l'ordre précédent si rien ne se passe Sockets[i].AttenteExecution++; if (Sockets[i].AttenteExecution>MAXATTENTE) { Sockets[i].etape=5; Sockets[i].AttenteExecution=0; Sockets[i].AnomaliesExecution++; } if (Sockets[i].AnomaliesExecution>MAXANOMALIESGOTO) { Sockets[i].etape=6; // if ( compt % 1000 == 0) { IDSetSwitch(&OnCoordSetSP, "Erreur sur l antenne %s : l antenne n a pas renvoye GOTO/OK. \ Deconnexion de l antenne.", Sockets[i].IP.c_str()); Sockets[i].Connected=false; } } } } break; } } } // Détection d'anomalies concernant l'envoi de trames sur la socket. déconnexion du micro-cont ? for (int i=1; i 0) { Sockets[i].etape=5; // if ( compt % 1000 == 0) { IDSetSwitch(&OnCoordSetSP, "Erreur sur l antenne %s : deconnexion de l antenne.", Sockets[i].IP.c_str()); Sockets[i].Connected=false; } } } //On attend que toutes les antennes soient prêtes pour lancer l'ordre Goto -> meilleure synchronisation int num=0; for (int i=1; i0)) { for (int i=1; insp ; i++) if (sp->sp[i].s == ISS_ON) return i; return -1; } /************************************************************************************** ** Activation de l'interface ***************************************************************************************/ void BAO::connect_telescope() { switch (ConnectSP.sp[0].s) { case ISS_ON: ConnectS[0].s = ISS_ON; ConnectS[1].s = ISS_OFF; IDLog("\nHello BAORadio !\n"); ConnectSP.s = IPS_OK; IDSetSwitch (&ConnectSP, "BAORadio is online. Retrieving basic data..."); break; case ISS_OFF: ConnectS[0].s = ISS_OFF; ConnectS[1].s = ISS_ON; ConnectSP.s = IPS_IDLE; SocketsNumber=1; InitThreadOK=false; for (int i=0; i