Added feature: GPS on/off and checks if at least 4 satellites is in use.
[speedfreak] / Client / carmainwindow.cpp
1 /*
2  * CarMainWindow main class
3  *
4  * @author     Toni Jussila <toni.jussila@fudeco.com>
5  * @author     Janne Änäkkälä <janne.anakkala@fudeco.com>
6  * @author     Tiina Kivilinna-Korhola <tiina.kivilinna-korhola@fudeco.com>
7  * @author     Olavi Pulkkinen <olavi.pulkkinen@fudeco.com>
8  * @author     Rikhard Kuutti <rikhard.kuutti@fudeco.com>
9  * @author     Kai Rasilainen <kai.rasilainen@fudeco.com>
10  * @copyright  (c) 2010 Speed Freak team
11  * @license    http://opensource.org/licenses/gpl-license.php GNU Public License
12  */
13
14 #include "carmainwindow.h"
15 #include "math.h"
16
17 #define kAccelerometerSampleRate    50
18 #define kFilteringFactor            0.2
19 #define kSecondsInHour              3600
20
21 /**
22   *Constructor of this class.
23   *@param QWidget pointer to parent object. By default the value is NULL.
24   */
25 CarMainWindow::CarMainWindow(QWidget *parent):QMainWindow(parent), ui(new Ui::CarMainWindow)
26 {
27     ui->setupUi(this);
28     ui->tabWidget->setCurrentWidget(this->ui->StartTab);
29     result = new ResultDialog();
30     //measure = new MeasureDialog();
31     welcomeDialog = new WelcomeDialog();
32     welcomeDialog->show();
33
34     initComboBoxStartTabUnits();
35     initListViewStartTabAccelerationCategories();
36
37     myLogin = new LoginWindow(this);
38     categorylist = new CategoryList();
39     myHttpClient = new HttpClient(this);
40     myRegistration = new Registration(this);
41     connect(myRegistration,SIGNAL(sendregistration()),this,SLOT(regUserToServer()));
42     connect(myLogin,SIGNAL(userNameChanged()),this,SLOT(userLogin()));
43     myRoute = new RouteDialog( this);
44
45     //GPS
46     location = new Maemo5Location(this);
47     gpsData = new GPSData(location);
48     connect(location,SIGNAL(agnss()),this,SLOT(gpsStatus()));
49
50     time = 0;
51     speed = 0;
52     timer = new QTimer();
53
54     // Accelerometer
55     accelerometer = new Accelerometer();
56     accelerometer->setSampleRate(100);
57
58     reverseAccelerationFlag = false;
59     vehicleStartedMoving = false;
60     isNewRun = true;
61     isSetup = false;
62     stopTime = 0;
63     accelerationStartThreshold = 0.02;
64
65     QTimer *accelerometerTimer = new QTimer(this);
66     connect(accelerometerTimer, SIGNAL(timeout()), this, SLOT(readAccelerometerData()));
67     accelerometerTimer->start(kAccelerometerSampleRate);
68
69     // Calculate
70     calculate = new Calculate();
71     //connect(calculate, SIGNAL(checkPointReached()), this, SLOT(handleCheckPoint()));
72
73     resetAccelerometerMeasurements();
74
75     measures = new Measures();
76     this->initializeMeasures();
77
78     timer->setInterval(300);
79
80     connect(this->timer, SIGNAL(timeout()), this, SLOT(after_timeout()));
81     connect(myLogin, SIGNAL( userNameChanged()), this, SLOT(updateUserName()));
82
83     ui->labelMeasureTabResult->hide();
84
85     this->setWindowTitle("Speed Freak");
86
87 }
88
89 /**
90   *Destructor of this class. Deletes all dynamic objects and sets them to NULL.
91   */
92 CarMainWindow::~CarMainWindow()
93 {
94     delete ui;
95     ui = NULL;
96     //delete result;
97     //delete measure;
98     delete categorylist;
99     categorylist = NULL;
100     delete welcomeDialog;
101     welcomeDialog = NULL;
102     delete myRoute;
103     myRoute = NULL;
104     delete gpsData;
105     gpsData = NULL;
106 }
107
108 /**
109   *This function is used to .
110   *@param
111   */
112 void CarMainWindow::changeEvent(QEvent *e)
113 {
114     QMainWindow::changeEvent(e);
115     switch (e->type()) {
116     case QEvent::LanguageChange:
117         ui->retranslateUi(this);
118         break;
119     default:
120         break;
121     }
122 }
123
124 /**
125   *This slot function is called when ever list view is update. Start-tab view.
126   */
127 void CarMainWindow::on_listViewStartTabAccelerationCategories_clicked(QModelIndex index)
128 {
129     QString str = index.data().toString();
130     QStringList list = str.split("-");
131     QStringList list2 = list[1].split(" ");
132
133     ui->lineEditStartTabMin->setText(list[0]);
134     ui->lineEditStartTabMax->setText(list2[0]);
135     updateComboBoxStartTabUnits(list2[1]);
136 }
137
138 /**
139   *This slot function is called when ever auto start button clicked. Start-tab view.
140   */
141 void CarMainWindow::on_autoStartButton_clicked()
142 {
143     choice = ui->listViewStartTabAccelerationCategories->currentIndex();
144     choiceInt = choice.row();
145     qDebug() << choiceInt;
146     if (choiceInt == 0)
147     {
148         ui->labelMeasureTabHeader->setText("Accelerate to 40 km/h");
149         result->setDiagramGapStem(75);
150     }
151
152     else if (choiceInt == 1)
153     {
154         ui->labelMeasureTabHeader->setText("Accelerate to 100 km/h");
155         result->setDiagramGapStem(30);
156     }
157
158     else
159     {
160         ui->labelMeasureTabHeader->setText("Accelerate to 80 km/h");
161         result->setDiagramGapStem(37.5);
162     }
163     ui->labelMeasureTabResult->setText("");
164     //delete measure;
165     //measure = NULL;
166     //measure = new MeasureDialog();
167    // connect(measure, SIGNAL(speedAchieved()), this, SLOT(openResultView()));
168     accelerometer->start();
169     timer->start();
170     // Show measure dialog.
171     //measure->show();
172
173     // TODO: Move next if else to the function which is called when target speed
174     // has reached.
175     if (choiceInt == 0)
176     {
177         if (floor(this->measures->getTime40kmh()) <= 5)
178         {
179             result->setDiagramGapHorizontal(80);
180         }
181
182         else if (floor(this->measures->getTime40kmh()) <= 10)
183         {
184             result->setDiagramGapHorizontal(40);
185         }
186
187         else
188         {
189             result->setDiagramGapHorizontal(20);
190         }
191     }
192
193     else
194     {
195         if (floor(this->measures->getTime40kmh()) <= 5)
196         {
197             result->setDiagramGapHorizontal(80);
198         }
199
200         else if (floor(this->measures->getTime40kmh()) <= 10)
201         {
202             result->setDiagramGapHorizontal(40);
203         }
204
205         else
206         {
207             result->setDiagramGapHorizontal(20);
208         }
209     }
210
211     ui->tabWidget->setCurrentWidget(this->ui->tabMeasureResult);
212 }
213
214 /**
215   *This slot function is called when ever list view is update. Start-tab view.
216   *@param QString unit.
217   */
218 void CarMainWindow::updateComboBoxStartTabUnits(QString unit)
219 {
220     ui->comboBoxStartTabUnits->setCurrentIndex(ui->comboBoxStartTabUnits->findText(unit, Qt::MatchExactly));
221 }
222
223 /**
224   *This function is used to init unit combobox. Start-tab view.
225   */
226 void CarMainWindow::initComboBoxStartTabUnits()
227 {
228     units << "km/h" << "km" << "h" << "m" << "min" << "Mile" << "Mph" << "in" << "ft" << "yrd";
229     ui->comboBoxStartTabUnits->addItems(units);
230 }
231
232 /**
233   *This function is used to set items to unit combobox. Start-tab view.
234   *@param QStringlist units
235   */
236 void CarMainWindow::setComboBoxStartTabUnits(QStringList units)
237 {
238     ui->comboBoxStartTabUnits->addItems(units);
239 }
240
241 /**
242   *This function is used to init listViewStartTabAccelerationCategories. Start-tab view.
243   */
244 void CarMainWindow::initListViewStartTabAccelerationCategories()
245 {
246     accelerationCategoriesStartTab << "0-40 km/h" << "0-100 km/h"; //<< "0-1/4 Mile" << "0-1/8 Mile" << "0-50 km" << "50-100 Mile" << "0-60 Mph" << "0-100 m" << "0-50 ft" << "0-50 yrd" << "0-500 in";
247     QAbstractItemModel *model = new StringListModel(accelerationCategoriesStartTab);
248     ui->listViewStartTabAccelerationCategories->setModel(model);
249 }
250
251 /**
252   *This function is used to set items to listViewStartTabAccelerationCategories. Start-tab view.
253   *@param QStringlist accelerationCategoriesStartTab
254   */
255 void CarMainWindow::setListViewStartTabAccelerationCategories(QStringList accelerationCategoriesStartTab)
256 {
257     QAbstractItemModel *model = new StringListModel(accelerationCategoriesStartTab);
258     ui->listViewStartTabAccelerationCategories->setModel(model);
259 }
260
261 /**
262   *This function is used to set items to category combobox. Top-tab view.
263   *@param
264   */
265 void CarMainWindow::setCategoryCompoBox()
266 {
267     ui->comboBoxTopCategory->addItems(categorylist->getCategoryList());
268 }
269
270 /**
271   *This function is used to set items to labelTopList. Top-tab view.
272   *@param QString category
273   */
274 void CarMainWindow::setListViewTopList(QString category, int size)
275 {
276     QString topList;
277     topList.append( categorylist->getTopList(category, size));
278     ui->labelTopList->setText(topList);
279 }
280
281 /**
282   *This slot function is called when speed is achieved in measure dialog. Opens result dialog.
283   */
284 void CarMainWindow::openResultView()
285 {
286     //result->saveMeasuresToArray(measure->measures);
287     // Show result dialog.
288     //result->show();
289     ui->pushButtonSendResult->setEnabled(true);
290     QString timeInteger;
291     if (choiceInt == 0)
292     {
293         if (floor(this->measures->getTime40kmh()) <= 5)
294         {
295             result->setDiagramGapHorizontal(80);
296         }
297
298         else if (floor(this->measures->getTime40kmh()) <= 10)
299         {
300             result->setDiagramGapHorizontal(40);
301         }
302
303         else
304         {
305             result->setDiagramGapHorizontal(20);
306         }
307     }
308
309     else
310     {
311         if (floor(this->measures->getTime40kmh()) <= 5)
312         {
313             result->setDiagramGapHorizontal(80);
314         }
315
316         else if (floor(this->measures->getTime40kmh()) <= 10)
317         {
318             result->setDiagramGapHorizontal(40);
319         }
320
321         else
322         {
323             result->setDiagramGapHorizontal(20);
324         }
325     }
326     timeInteger.setNum(this->measures->getTime40kmh());
327     //time = "0 - 40 km/h: ";
328     //time.append(timeInteger);
329     //ui->labelResult40kmh->setText(time);
330     ui->labelMeasureTabResult->show();
331     ui->labelMeasureTabResult->setText(timeInteger);
332     //ui->tabWidget->setCurrentWidget(this->ui->tabMeasureResult);
333 }
334
335 /**
336   *This slot function is called when registrate button is clicked.
337   */
338 void CarMainWindow::on_registratePushButton_clicked()
339 {
340     myRegistration->show();
341 }
342
343 /**
344   *This slot function is called when ever refresh button clicked. Top-tab view.
345   */
346 void CarMainWindow::on_buttonTopRefresh_clicked()
347 {
348     myHttpClient->requestCategories();
349     setCategoryCompoBox();
350 }
351
352 /**
353   *This slot function is called when ever category combobox current index changed. Top-tab view.
354   *@param QString category
355   *@todo Check where limitNr is taken.
356   */
357 void CarMainWindow::on_comboBoxTopCategory_currentIndexChanged(QString category)
358 {
359     int limitNr = 5;                    //replace with real value?
360     QString limit = QString::number(limitNr);
361     category = "acceleration-0-100";    //replace with real value from category list/top window
362     myHttpClient->requestTopList(category, limit);
363     setListViewTopList(category,10);
364 }
365
366 /**
367   *This slot function is called when ever category combobox activated. Top-tab view.
368   *@param QString category
369   */
370 void CarMainWindow::on_comboBoxTopCategory_activated(QString category)
371 {
372     setListViewTopList(category,10);
373 }
374
375 /**
376   *This slot function is called when set/change user button is clicked.
377   */
378 void CarMainWindow::on_setUserPushButton_clicked()
379 {
380     myLogin->show();
381 }
382
383 /**
384   *@brief Just for development, for the real button is not shown until
385   *measurin started and there are results.
386   *@todo Implement with real code and yet leave sendXml in the bottom in use.
387   */
388 void CarMainWindow::on_manualStartButton_clicked()
389 {
390
391 }
392
393 /**
394   * This slot function is called when timer gives timeout signal. Checks current speed
395   * and stores times in measure class.
396   */
397 void CarMainWindow::after_timeout()
398 {
399     QString timeString, speedString;
400     //time++;
401     time = accelerometer->getTotalTime();
402     speed = accelerometer->getCurrentSpeed();
403     //speed = speed +10;
404
405     if (floor(speed) == 10)
406     {
407         measures->setTime10kmh(time);
408     }
409
410     else if (floor(speed) == 20)
411     {
412         measures->setTime20kmh(time);
413     }
414
415     else if (floor(speed) == 30)
416     {
417         measures->setTime30kmh(time);
418     }
419
420     else if (floor(speed) == 40)
421     {
422         measures->setTime40kmh(time);
423     }
424
425     else if (floor(speed) == 50)
426     {
427         measures->setTime50kmh(time);
428     }
429
430     else if (floor(speed) == 60)
431     {
432         measures->setTime60kmh(time);
433     }
434
435     else if (floor(speed) == 70)
436     {
437         measures->setTime70kmh(time);
438     }
439
440     else if (floor(speed) == 80)
441     {
442         measures->setTime80kmh(time);
443     }
444
445     else if (floor(speed) == 90)
446     {
447         measures->setTime90kmh(time);
448     }
449
450     else if (floor(speed) == 100)
451     {
452         measures->setTime100kmh(time);
453     }
454
455     else
456     {
457
458     }
459
460     // If speed is over 40 km/h emits speedAchieved() signal and close this dialog.
461     if (speed >= 40.0)
462     {
463         timer->stop();
464         accelerometer->stop();
465         time = 0;
466         speed = 0;
467         //emit this->speedAchieved();
468         this->openResultView();
469         //this->close();
470
471     }
472
473     // Updates speed and time.
474     else
475     {
476         timeString.setNum(time);
477         speedString.setNum(speed);
478         ui->labelMeasureTabTime->setText(timeString);
479         ui->labelMeasureTabSpeed->setText(speedString);
480
481         timer->start();
482     }
483
484 }
485
486 /**
487   * Initializes measures class's member variables.
488   */
489 void CarMainWindow::initializeMeasures()
490 {
491     measures->setTime10kmh(0);
492     measures->setTime20kmh(0);
493     measures->setTime30kmh(0);
494     measures->setTime40kmh(0);
495     measures->setTime50kmh(0);
496     measures->setTime60kmh(0);
497     measures->setTime70kmh(0);
498     measures->setTime80kmh(0);
499     measures->setTime90kmh(0);
500     measures->setTime100kmh(0);
501 }
502
503 /**
504   * This slot function is called when Abort button is clicked.
505   */
506 void CarMainWindow::on_pushButtonMeasureTabAbort_clicked()
507 {
508     measures->setTime10kmh(0);
509     measures->setTime20kmh(0);
510     measures->setTime30kmh(0);
511     measures->setTime40kmh(0);
512     measures->setTime50kmh(0);
513     measures->setTime60kmh(0);
514     measures->setTime70kmh(0);
515     measures->setTime80kmh(0);
516     measures->setTime90kmh(0);
517     measures->setTime100kmh(0);
518     timer->stop();
519     accelerometer->stop();
520     time = 0;
521     speed = 0;
522     ui->tabWidget->setCurrentWidget(this->ui->StartTab);
523     //this->close();
524 }
525
526 void CarMainWindow::on_pushButtonSendResult_clicked()
527 {
528     myHttpClient->sendResultXml();
529     ui->pushButtonSendResult->setEnabled(false);
530 }
531
532 void CarMainWindow::updateUserName()
533 {
534     QString newUserName;
535
536     newUserName = myLogin->getUserName();
537     ui->userNameLabel->setText( "User: " + newUserName);
538
539     if (newUserName.length())
540     {
541        ui->setUserPushButton->setText( "Change User");
542        this->setWindowTitle("Speed freak - " + newUserName);
543     }
544     else
545     {
546         ui->setUserPushButton->setText( "Set User");
547         this->setWindowTitle("Speed freak");
548     }
549 }
550
551 void CarMainWindow::regUserToServer()
552 {
553     myHttpClient->requestRegistration();
554 }
555
556
557 void CarMainWindow::on_drawRoutePushButton_clicked()
558 {
559     myRoute->show();
560 }
561
562 /**
563   * Opens result dialog when show result button is clicked.
564   * Sends measures as parameter to the resultdialogs saveMeasuresToArray-function.
565   */
566 void CarMainWindow::on_pushButtonShowResultDialog_clicked()
567 {
568     Measures meas;
569     meas.setTime10kmh(1.3);
570     meas.setTime20kmh(2.5);
571     meas.setTime30kmh(3.6);
572     meas.setTime40kmh(6.7);
573     meas.setTime50kmh(7.3);
574     meas.setTime60kmh(7.5);
575     meas.setTime70kmh(8.6);
576     meas.setTime80kmh(8.7);
577     meas.setTime90kmh(9.6);
578     meas.setTime100kmh(9.9);
579     result->setDiagramGapHorizontal(40);
580     result->saveMeasuresToArray(&meas);
581     this->result->show();
582 }
583
584 void CarMainWindow::userLogin()
585 {
586     myHttpClient->checkLogin();
587 }
588
589 /**
590   * Resets Accelerometer measurement variables
591   */
592 void CarMainWindow::resetAccelerometerMeasurements()
593 {
594     currentAcceleration = 0;
595     currentAccelerationString = "";
596     currentSpeed = "";
597     currentTime = 0;
598     distanceTraveled = "";
599     firstAcceleration = 0;
600     //horsepower = null;
601     isNewRun = true;
602     //lastScreenUpdateInSeconds = 0;
603     previousTime = 0;
604     reverseAccelerationFlag = false;
605     stopWatch.setHMS(0, 0, 0, 0);
606     //accelerometer->stop();
607     totalTime = "";
608     vehicleStartedMoving = false;
609     calculate->reset();
610 }
611
612 /**
613   * This function is called to handle checkpoints
614   *@param totalTime total time elapsed since starting measurements
615   *@param currentSpeed current speed of the device
616   */
617 void CarMainWindow::handleCheckPoint(double totalTime, double currentSpeed)
618 {
619     // TODO
620     //totalTime;
621     //currentSpeed;
622     return;
623 }
624
625 /**
626   *This function is called to read (and process) data from the accelerometer
627   */
628 void CarMainWindow::readAccelerometerData()
629 {
630     QString s;
631     double changeInAcceleration = 0;
632     qreal x, y, z;
633
634     accelerometer->getAcceleration(x, y, z);
635     accelerometer->smoothData(x, y, z);
636
637     // Apply calibration
638     x -= accelerometer->getCalibrationX();
639     y -= accelerometer->getCalibrationY();
640     z -= accelerometer->getCalibrationZ();
641
642     QString str = QString("acc x: " + QString::number(x) + "\n" +
643                           "acc y: " + QString::number(y) + "\n" +
644                           "acc z: " + QString::number(z) + "\n");
645
646     if (!vehicleStartedMoving)
647     {
648         if (isNewRun)
649         {
650             firstAcceleration = sqrt(x*x + y*y + z*z);
651             //firstAcceleration = y; // first read
652             isNewRun = false;
653         }
654     }
655
656     currentAcceleration = sqrt(x*x + y*y + z*z);
657     changeInAcceleration = (currentAcceleration - firstAcceleration); // firstAcceleration only gets set once
658
659     if (((abs(changeInAcceleration) <= accelerationStartThreshold)
660                 && !vehicleStartedMoving))
661     {
662         return;
663     }
664
665     if (reverseAccelerationFlag)
666     {
667         // will be false until after 1st calculation
668         if ((changeInAcceleration <= 0))
669         {
670             // actually increasing here...
671             changeInAcceleration = abs(changeInAcceleration);
672         }
673         else
674         {
675             // actually decreasing here...
676             changeInAcceleration = (changeInAcceleration * -1);
677         }
678     }
679     if (!vehicleStartedMoving)
680     {
681         if ((changeInAcceleration < 0))
682         {
683             // we started to move backwards first time through
684             reverseAccelerationFlag = true;
685             changeInAcceleration = abs(changeInAcceleration);
686         }
687         vehicleStartedMoving = true;
688
689         stopWatch.setHMS(0, 0, 0, 0);
690         stopWatch.start();
691     }
692     //  keep the following line as close to the SetKinematicsProperties method as possible
693     currentTime = stopWatch.elapsed();
694     calculate->calculateParameters(changeInAcceleration, (currentTime - previousTime)/1000);
695     previousTime = currentTime;
696
697     s.sprintf("%.2f", changeInAcceleration);
698     currentAccelerationString = s;
699
700     double speed = 0.0;
701     speed = calculate->getCurrentSpeed();
702     speed = ((speed*1000)/kSecondsInHour);
703     s.sprintf("%.2f", speed);
704     currentSpeed = s;
705
706     s.sprintf("%.2f", calculate->getDistanceTraveled());
707     distanceTraveled = s;
708
709     // TODO
710     //distanceTraveled;
711     //horsepower;
712     //totalTime;
713
714     s.sprintf("%.2f", calculate->getTotalTime());
715     totalTime = s;
716
717     str.append("ca: " + currentAccelerationString + " G\n" );
718     str.append("cspeed: " + currentSpeed + " km/h \n" );
719     str.append("dist: " + distanceTraveled + " m \n" );
720     str.append("time: " + totalTime + " s \n" );
721
722     if ((stopTime > 0) && (previousTime >= stopTime))
723     {
724         // we want to end at a stopping point that the user chose
725         // output results
726         resetAccelerometerMeasurements();
727     }
728 }
729
730 /**
731   *This function is used to calibrate accelerometer
732   */
733 void CarMainWindow::calibrateAccelerometer()
734 {
735     resetAccelerometerMeasurements();
736     accelerometer->calibrate();
737 }
738
739 /**
740   *This slot function is called when GPS on checkbox state changed.  Route-tab view.
741   *@param int GPSState
742   */
743 void CarMainWindow::on_gpsOnCheckBox_stateChanged(int GPSState)
744 {
745     if (GPSState == 0)
746     {
747         ui->labelRouteTabGPSStatus->setText("GPS off");//testing
748         location->stopPollingGPS();
749     }
750     else
751     {
752         ui->labelRouteTabGPSStatus->setText("GPS on");//testing
753         location->startPollingGPS();
754     }
755 }
756
757 /**
758   *This slot function is called when GPS status changed.  Route-tab view.
759   */
760 void CarMainWindow::gpsStatus()
761 {
762     if (ui->gpsOnCheckBox->isChecked())
763     {
764         if (location->getSatellitesInUse() >= 4)
765         {
766             ui->labelRouteTabGPSStatus->setText("GPS ready");
767         }
768
769         else
770         {
771             ui->labelRouteTabGPSStatus->setText("Waiting for GPS");
772         }
773     }
774 }