#include "MCosy.h" #include #include #include #include #include #include #include "MGCosy.h" #include "macs.h" #include "timer.h" #include "slalib.h" #include "slamac.h" #include "shaftencoder.h" #include // PRIO_PROCESS typedef struct tm tm_t; #define GEAR_RATIO_ALT 75.55 // 75.25 VERY IMPORTANT! unit=RE/SE #define GEAR_RATIO_AZ 179.8 // VERY IMPORTANT! unit=RE/SE const XY kGearRatio(GEAR_RATIO_ALT, GEAR_RATIO_AZ); const XY kGearRatio2(GEAR_RATIO_ALT*16384.0/360.0, GEAR_RATIO_AZ*16384.0/360.0); double Rad2SE(double rad) { return 16384.0/D2PI*rad; } double Rad2ZdRE(double rad) { return 16384.0/D2PI*rad*kGearRatio.X(); } double Rad2AzRE(double rad) { return 16384.0/D2PI*rad*kGearRatio.Y(); } double Deg2ZdRE(double rad) { return rad*kGearRatio2.X(); } double Deg2AzRE(double rad) { return rad*kGearRatio2.Y(); } //double Rad2Deg(double rad) //{ // return 360.0/D2PI*rad; //} ZdAz MCosy::CorrectTarget(const ZdAz &src, const ZdAz &dst) { // src [se] // dst [rad] // fAltMax = 70 // fAltMin = -105/110 // fAzMin = -355 // fAzMax = 355 ZdAz source = src * 360.0/16384.0; ZdAz dest = dst * 360.0/D2PI; if (dest.Zd()>-1e-6 && dest.Zd()<1e-6) return dst; const float fZdMin = -67; const float fZdMax = 67; const float fAzMin = -29; const float fAzMax = 423; // // This corrects to target for the shortest distance, not for the fastest move! // ZdAz s = source-dest; float min = s.Sqr(); // // Is it enought to search inside one revolution? // ZdAz ret = dest; for (int i=-5; i<5+1; i++) { const ZdAz p(i%2 ? -dest.Zd() : dest.Zd(), dest.Az() - i*180); // // Range Check // if (p.Zd()fZdMax) continue; if (p.Az()fAzMax) continue; // // Calculate distance // s = source-p; const float dist = s.Sqr(); if (dist > min) continue; // // New shortest distance // ret = p; min = dist; } return ret*(16384.0/360.0); } ZdAz MCosy::GetSePos() { const int p0 = fAlt1->GetPos(); const int p1 = fAlt2->GetPos(); const int p2 = fAz->GetPos(); const int a0 = p0; //p0>8192?p0-16384:p0; const int a1 = p1; //p1>8192?p1-16384:p1; const int a2 = p2; //p2>8192?p2-16384:p2; // // interpolate shaft encoder positions // const float a = (float)(a0-a1)/2; // // calculate 'regelabweichung' // return ZdAz(a, a2); } ZdAz MCosy::GetRePos() { return ZdAz(fMac2->GetPos(), fMac1->GetPos()); } int MCosy::SetPosition(const ZdAz &dst) // [rad] { // FIXME: CORRECT BY fOffset !!!!!!!!!! // // Calculate new target position (shortest distance to go) // const ZdAz src = GetSePos(); const ZdAz dest = CorrectTarget(src, dst); cout << "Positioning to Target:" << endl; // cout << "Source Alt: " << src.Alt() << "se Az:" << src.Az() << "se -> Alt: " << srcd.Alt() << kDEG << " Az:" << srcd.Az() << kDEG << endl; // cout << "Shortest Dest Alt: " << dest.Alt() << "se Az:" << dest.Az() << "se -> Alt: " << sed.Alt() << kDEG << " Az:" << sed.Az() << kDEG << endl; for (int i=0; i<10; i++) { // // Get Shaft Encoder Positions // const ZdAz p=GetSePos(); // // calculate control deviation and rounded cd // ZdAz rd = dest-p; // [se] ZdAz cd = rd; // [se] cd.Round(); // // check if we reached the correct position already // if (!(int)cd.Zd() && !(int)cd.Az()) { cout << "Positioning done with " << i << "manuvers." << endl; return TRUE; } // // change units from se to re // rd *= kGearRatio; // [re] // // Set velocities // const int vr = fMac1->GetVelRes(); const float maxvel = (i?0.1:0.9)*vr; // maxvel = 90% const float maxacc = (i?0.1:0.5)*vr; // maxacc = 50%; const float diff = i?1:fabs(rd.Ratio()); cout << "Salt/Saz: " << diff << endl; if (diff <1) { fMac1->SetVelocity(maxvel); fMac1->SetAcceleration(maxacc); fMac1->SetDeceleration(maxacc); fMac2->SetVelocity(maxvel*diff); fMac2->SetAcceleration(maxacc*diff); fMac2->SetDeceleration(maxacc*diff); } else { fMac1->SetVelocity(maxvel/diff); fMac1->SetAcceleration(maxacc/diff); fMac1->SetDeceleration(maxacc/diff); fMac2->SetVelocity(maxvel); fMac2->SetAcceleration(maxacc); fMac2->SetDeceleration(maxacc); } rd.Round(); cout << " + APOS: Zd=" << setw(6) << p.Zd() << "se Az=" << setw(6) << p.Az() << "se" << endl; cout << " + dZd=" << setw(6) << cd.Zd() << "se dAz=" << setw(6) << cd.Az() << "se" << endl; cout << " + dZd=" << setw(6) << rd.Zd() << "re dAz=" << setw(6) << rd.Az() << "re" << endl; // // repositioning (relative) // if ((int)cd.Zd()) fMac2->StartRelPos(rd.Zd()); if ((int)cd.Az()) fMac1->StartRelPos(rd.Az()); cout << "Waiting for positioning..." << flush; WaitForSdos(); cout << "SDO..." << flush; while (fMac1->IsPositioning() || fMac2->IsPositioning()) { if (StopWaitingForSDO()) return FALSE; usleep(1); } cout << "done." << endl; } cout << "Positioning ERROR!" << endl; return FALSE; } void MCosy::TrackPosition(const RaDec &dst) // ra, dec [rad] { // // Position to actual position // Timer t; t.GetTime(); RaDec pm; ZdAz dest = RaDec2ZdAz(t.GetMjd(), dst, pm); if (!SetPosition(dest)) { cout << "ERROR: Cannot start tracking, unable to reach requested position." << endl; return; } if (StopWaitingForSDO()) return; // // calculate offset from present se position // const ZdAz sepos = GetSePos()*kGearRatio; fMac1->ReqPos(); fMac2->ReqPos(); const ZdAz repos=GetRePos(); fOffset = sepos-repos; cout << "Offset: " << sepos.Zd() << "re, " << sepos.Az() << "re" << endl; cout << "Offset: " << repos.Zd() << "re, " << repos.Az() << "re" << endl; cout << "Offset: " << fOffset.Zd() << "re, " << fOffset.Az() << "re" << endl; // // Start revolution mode // fMac2->SetAcceleration(0.90*fMac2->GetVelRes()); fMac2->SetDeceleration(0.90*fMac2->GetVelRes()); fMac1->SetAcceleration(0.90*fMac1->GetVelRes()); fMac1->SetDeceleration(0.90*fMac1->GetVelRes()); fMac2->SetRpmMode(TRUE); fMac1->SetRpmMode(TRUE); /*-*/ int s = t.GetSecs(); cout << "Start tracking: Ra: " << Rad2Deg(dst.Ra()) << kDEG << " Dec: "; cout << Rad2Deg(dst.Dec()) << kDEG << endl; // // Initialize Tracker (slalib or starguider) // fRaDec = dst; fTracking = kTRUE; // // We want to reach the theoretical position exactly in about 0.5s // const float dt = 1; // 1 second while (!StopWaitingForSDO()) { // // Request Real Position from Drive // Timer t; t.GetTime(); // // Request theoretical Position for a time in the future (To+dt) from CPU // dest = CorrectTarget(GetSePos(), RaDec2ZdAz(t.GetMjd()+dt/(60*60*24), dst, pm)); fMac2->RequestSDO(0x6004); fMac1->RequestSDO(0x6004); WaitForSdos(); if (StopWaitingForSDO()) { lout << "Error 6004 happened" << endl; SkipPendingSdos(); break; } // // Copy fOffset to a local variable // ZdAz offset = fOffset; // // distance between (To+dt) and To [re] // position time difference < 5usec // dest *= kGearRatio; dest -= GetRePos() + offset; // // Velocity to go [re/min] to reach the right position at time t+dt // correct for the duration of RaDec2AltAz // const ZdAz v = dest*60.0/(dt-(fMac2->GetTime()-t)); // // calculate real velocity of future [re/min] // ZdAz vt = v/4; vt.Round(); if (v.Zd()>.9*fMac1->GetVelRes() || v.Az()>.9*fMac2->GetVelRes()) { cout << "Error: Tracking speed faster than possible maximum velocity." << endl; break; } // // Set theoretical velocity (as early after calculation as possible) // // // Maybe we should attenuate the changes // fMac2->SendSDO(0x3006, 1, (LWORD_t)vt.Zd()); // SetRpmVelocity [re/min] fMac1->SendSDO(0x3006, 1, (LWORD_t)vt.Az()); // SetRpmVelocity [re/min] WaitForSdos(); if (StopWaitingForSDO()) { lout << "Error 3006 happened" << endl; SkipPendingSdos(); break; } // // Now do 'unnecessary' things // calculate control deviation - for the moment for convinience // if (fMac1->GetTime()-s > 1) { ZdAz dest0=CorrectTarget(GetSePos(), RaDec2ZdAz(fMac2->GetMjd(), dst, pm)); dest0 *= kGearRatio; dest0 -= GetRePos()+offset; dest0.Round(); cout << "Control deviation: "; cout << setw(4) << (int)fOffset.Zd() << " "; cout << setw(4) << (int)fOffset.Az() << " re "; cout << setw(4) << dest0.Zd() << " "; cout << setw(4) << dest0.Az() << " re V: "; cout << setw(4) << vt.Zd() << " "; cout << setw(4) << vt.Az() << " re/min/4" << endl; s = (int)fMac1->GetTime(); } // // Update speed as often as possible. // make sure, that dt is around 10 times larger than the // update time // // usleep(50000); // 0.05s } fTracking = kFALSE; // // Stop revolution mode // fMac2->SetRpmMode(FALSE); fMac1->SetRpmMode(FALSE); cout << "Tracking stopped." << endl; } ZdAz MCosy::RaDec2ZdAz(const double mjd, const RaDec &dst, const RaDec &pm) { int status; // // calculate observers location (goe) // double fPhi, fElong; slaDaf2r(51, 38, 48.0, &fPhi, &status); slaDaf2r( 9, 56, 36.0, &fElong, &status); // cout << "fPhi: 51ø38'48.0\" = " << 360.0/D2PI*fPhi << endl; // cout << "fElong: 9ø56'36.0\" = " << 360.0/D2PI*fElong << endl; // // ----- calculate star independent parameters ---------- // double fAmprms[21]; double fAoprms[14]; slaMappa(2000.0, mjd, fAmprms); slaAoppa(mjd, 0, // mjd, UT1-UTC fElong, fPhi, 148, // g”ttingen long, lat, height 0, 0, // polar motion x, y-coordinate (radians) 273.155, 1013.25, 0.5, // temp, pressure, humidity 0.2, 0.0065, // wavelength, tropo lapse rate fAoprms); // // ---- Mean to apparent ---- // double r=0, d=0; slaMapqkz(dst.Ra(), dst.Dec(), (double*)fAmprms, &r, &d); // // Doesn't work - don't know why // // slaMapqk(dst.Ra(), dst.Dec(), pm.Ra(), pm.Dec(), // ra, dec (rad), r, d (rad) // 0, 0, fAmprms, &r, &d); // // // -- apparent to observed -- // double r1=0; // ra double d1=0; // dec double h0=0; // ha double az, zd; slaAopqk (r, d, fAoprms, &az, // observed azimuth (radians: N=0,E=90) &zd, // observed zenith distance (radians) &h0, // observed hour angle (radians) &d1, // observed declination (radians) &r1); // observed right ascension (radians) return ZdAz(zd, az); } void *MCosy::Proc(int msg, void *mp) { switch (msg) { case WM_STOP: cout << "Stopping positioning." << endl; fMac1->SetDeceleration(0.5*fMac1->GetVelRes()); fMac2->SetDeceleration(0.5*fMac2->GetVelRes()); cout << "Stoping" << endl; fMac1->SetRpmMode(FALSE); fMac2->SetRpmMode(FALSE); cout << "Done." << endl; while (fMac1->IsPositioning() || fMac2->IsPositioning()) usleep(1); cout << "Positioning stopped." << endl; return NULL; case WM_PRESET: cout << "WM_PRESET: START" << endl; fAlt1->SetPreset(); fAlt2->SetPreset(); fAz->SetPreset(); cout << "WM_PRESET: DONE (return 0xaffe)" << endl; return (void*)0xaffe; case WM_POLARIS: { cout << "WM_POLARIS: START" << endl; Timer t; t.GetTime(); RaDec rd(37.94, 89.2644); ZdAz za=MCosy::RaDec2ZdAz(t.GetMjd(), rd*D2PI/360.0)*16384.0/D2PI; cout << "Calc Zd: " << za.Zd() << " Az: " << za.Az() << endl; ZdAz sepos = GetSePos(); cout << "Got Zd: " << sepos.Zd() << " Az: " << sepos.Az() << endl; fAlt1->SetPreset(za.Zd()); fAlt2->SetPreset(-za.Zd()); fAz->SetPreset(za.Az()); cout << "WM_PRESET: DONE (return 0xaffe)" << endl; } return (void*)0xaffe; case WM_POSITION: cout << "WM_POSITION: START" << endl; { ZdAz dest = *((ZdAz*)mp); SetPosition(dest*D2PI/360.0); } cout << "WM_POSITION: DONE (return 0x7777)" << endl; return (void*)0x7777; case WM_TRACK: cout << "WM_TRACK: START" << endl; { RaDec dest = *((RaDec*)mp); TrackPosition(dest*D2PI/360.0); } cout << "WM_TRACK: DONE (return 0x8888)" << endl; return (void*)0x8888; } cout << "Unknown Msg" << endl; return (void*)0xffffffff; } void MCosy::TalkThread() { // // Start the Network // Network::Start(); PostMsg(WM_PRESET, 0, 0); /* cout << "PostMsg(WM_PRESET)" << endl; void *rc = cout << hex << "WM_PRESET: ret=" << rc << endl; RaDec dest = RaDec(45.0, 30.0)*D2PI/360.0; cout << "PostMsg(WM_TRACK)" << endl; cout << sizeof(RaDec) << "==" << sizeof(dest) << endl; rc=PostMsg(WM_TRACK, &dest, sizeof(dest)); cout << "DEST killed." << endl; */ // AltAz dest = AltAz(45.0, 30.0); // double ra, dec; // slaDaf2r( 71, 0, 0, &ra, &status); // 0 WARNING: RANGE // slaDaf2r( 89, 0, 0, &dec, &status); // 49 // cout << "Start tracking: Ra: " << Rad2Deg(ra) << kDEG << " Dec: " << Rad2Deg(dec) << kDEG << endl; // dest = AltAz(-46.0, 210); // SetPosition(dest); setpriority(PRIO_PROCESS, 0, 10); while (1) { // // wait until a tracking session is started // while (!fTracking) usleep(1); RaDec pm; ZdAz sollalt; // [se] ZdAz sollaz; // [se] ZdAz old; ZdAz ist=fOffset/kGearRatio; // [se] // // only update fOffset while tracking // while (fTracking) { usleep(100000/*00*/); // 0.1s // // Make changes (eg wind) smoother - attenuation of control function // ZdAz offset(fOffset.Zd()*9.0/10.0+((ist.Zd()-sollalt.Zd())*kGearRatio.X())/10.0, fOffset.Az()*9.0/10.0+((ist.Az()-sollaz.Az()) *kGearRatio.Y())/10.0); fOffset = offset; // fOffset.Zd(((offset.Zd()>1000)||(offset.Zd()<-1000))?0:offset.Zd()); // fOffset.Az(((offset.Az()>1000)||(offset.Az()<-1000))?0:offset.Az()); // // get position, where we are // ist = GetSePos(); // [se] // // if the position didn't change continue // if ((int)ist.Zd() == (int)old.Zd() && (int)ist.Az() == (int)old.Az()) continue; // // if Alt Shaftencoder changed position // if ((int)ist.Zd() != (int)old.Zd()) { // // Get time from last shaftencoder position change // const double t = (fAlt1->GetMjd()+fAlt2->GetMjd())/2.0; // // calculate were we should be // sollalt = CorrectTarget(ist, RaDec2ZdAz(t, fRaDec, pm)); old.Zd(ist.Zd()); } // // if Alt Shaftencoder changed position // if ((int)ist.Az() != (int)old.Az()) { // // Get time from last shaftencoder position change // const double t = fAz->GetMjd(); // // calculate were we should be // sollaz = CorrectTarget(ist, RaDec2ZdAz(t, fRaDec, pm)); old.Az(ist.Az()); } } } } void *MCosy::MapTalkThread(void *arg) { pthread_detach(pthread_self()); MCosy *cosy = (MCosy*)arg; cosy->TalkThread(); cosy->lout << "- Sending Thread done." << endl; return NULL; } int MCosy::StopWaitingForSDO() { return Break() || fMac1->HasError() || fMac2->HasError(); } void MCosy::Start() { if (fTxThrd) { cout << "Error: tx thread already started." << endl; return; } lout << "- Starting sending Thread." << endl; fTxThrd = new pthread_t; pthread_create(fTxThrd, NULL, MapTalkThread, this); } void MCosy::Stop() { if (!fTxThrd) return; pthread_cancel(*fTxThrd); delete fTxThrd; fTxThrd = NULL; lout << "- Sending Thread stopped." << endl; SkipPendingSdos(); Network::Stop(); } MCosy::MCosy(const char *dev, const int baud, ostream &out) : Network(dev, baud, out), fTxThrd(NULL), fTracking(kFALSE) { // // Create Nodes // fMac1=new Macs(1, lout); fMac2=new Macs(2, lout); fAlt1=new ShaftEncoder(4, lout); fAlt2=new ShaftEncoder(5, lout); fAz =new ShaftEncoder(6, lout); // // Connect the devices to the network // SetNode(fMac1); SetNode(fMac2); SetNode(fAlt1); SetNode(fAlt2); SetNode(fAz); MGCosy *fWin=new MGCosy(this, gClient->GetRoot(), 1, 1); fAz->SetDisplay(fWin->GetLabel1()); fAlt1->SetDisplay(fWin->GetLabel2()); fAlt2->SetDisplay(fWin->GetLabel3()); } void MCosy::TerminateApp() { gSystem->ExitLoop(); } MCosy::~MCosy() { delete fAz; delete fAlt2; delete fAlt1; delete fMac1; delete fMac2; delete fWin; }