diff --git a/README.md b/README.md index fa3c97775..07b25d011 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,4 @@ We welcome contributions. For smaller changes just open a pull request straight ## License -NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). NumWorks is a registered trade mark. +NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). NumWorks is a registered trademark. diff --git a/apps/Makefile b/apps/Makefile index 9f6d9959c..48354048b 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -22,7 +22,6 @@ app_objs += $(addprefix apps/,\ exam_pop_up_controller.o\ global_preferences.o\ i18n.o\ - led_timer.o\ lock_view.o\ main.o\ math_toolbox.o\ diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index cbb0ee259..91d0ec224 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -17,7 +17,6 @@ AppsContainer::AppsContainer() : m_variableBoxController(&m_globalContext), m_examPopUpController(this), m_updateController(), - m_ledTimer(LedTimer()), m_batteryTimer(BatteryTimer(this)), m_suspendTimer(SuspendTimer(this)), m_backlightDimmingTimer(), @@ -241,11 +240,11 @@ Window * AppsContainer::window() { } int AppsContainer::numberOfContainerTimers() { - return 3+(GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate); + return 3; } Timer * AppsContainer::containerTimerAtIndex(int i) { - Timer * timers[4] = {&m_batteryTimer, &m_suspendTimer, &m_backlightDimmingTimer, &m_ledTimer}; + Timer * timers[3] = {&m_batteryTimer, &m_suspendTimer, &m_backlightDimmingTimer}; return timers[i]; } diff --git a/apps/apps_container.h b/apps/apps_container.h index a1cc9d8a3..76fdf3612 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -12,7 +12,6 @@ #include "variable_box_controller.h" #include "exam_pop_up_controller.h" #include "exam_pop_up_controller_delegate.h" -#include "led_timer.h" #include "battery_timer.h" #include "suspend_timer.h" #include "backlight_dimming_timer.h" @@ -70,7 +69,6 @@ private: VariableBoxController m_variableBoxController; ExamPopUpController m_examPopUpController; OnBoarding::UpdateController m_updateController; - LedTimer m_ledTimer; BatteryTimer m_batteryTimer; SuspendTimer m_suspendTimer; BacklightDimmingTimer m_backlightDimmingTimer; diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 9fc012d3d..eda6f520f 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -75,9 +75,8 @@ bool App::layoutFieldDidReceiveEvent(::LayoutField * layoutField, Ion::Events::E return true; } - int bufferLength = Calculation::k_printedExpressionSize; - char bufferForParsing[bufferLength]; - layoutField->serialize(bufferForParsing, bufferLength); + char bufferForParsing[Calculation::k_printedExpressionSize]; + layoutField->serialize(bufferForParsing, Calculation::k_printedExpressionSize); if (!textInputIsCorrect(bufferForParsing)) { displayWarning(I18n::Message::SyntaxError); diff --git a/apps/code/python_text_area.cpp b/apps/code/python_text_area.cpp index cb14bdf78..8f7f3b6d7 100644 --- a/apps/code/python_text_area.cpp +++ b/apps/code/python_text_area.cpp @@ -17,6 +17,8 @@ constexpr KDColor OperatorColor = KDColor::RGB24(0xd73a49); constexpr KDColor StringColor = KDColor::RGB24(0x032f62); constexpr KDColor BackgroundColor = KDColorWhite; +static inline int min(int x, int y) { return (xtok_kind == MP_TOKEN_STRING) { return lex->vstr.len + 2; } @@ -122,7 +124,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * basis. This can work, however the MicroPython lexer won't accept a line * starting with a whitespace. So we're discarding leading whitespaces * beforehand. */ - int whitespaceOffset = 0; + size_t whitespaceOffset = 0; while (text[whitespaceOffset] == ' ' && whitespaceOffset < length) { whitespaceOffset++; } @@ -130,8 +132,8 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char mp_lexer_t * lex = mp_lexer_new_from_str_len(0, text + whitespaceOffset, length - whitespaceOffset, 0); LOG_DRAW("Pop token %d\n", lex->tok_kind); - int tokenFrom = 0; - int tokenLength = 0; + size_t tokenFrom = 0; + size_t tokenLength = 0; KDColor tokenColor = KDColorBlack; while (lex->tok_kind != MP_TOKEN_NEWLINE && lex->tok_kind != MP_TOKEN_END) { diff --git a/apps/exam_pop_up_controller.cpp b/apps/exam_pop_up_controller.cpp index c4821007f..c0c935bf9 100644 --- a/apps/exam_pop_up_controller.cpp +++ b/apps/exam_pop_up_controller.cpp @@ -58,6 +58,8 @@ ExamPopUpController::ContentView::ContentView(Responder * parentResponder) : AppsContainer * container = (AppsContainer *)controller->app()->container(); if (controller->isActivatingExamMode()) { container->reset(); + Ion::LED::setColor(KDColorRed); + Ion::LED::setBlinking(1000, 0.1f); } else { Ion::LED::setColor(KDColorBlack); } diff --git a/apps/exam_pop_up_controller.h b/apps/exam_pop_up_controller.h index 5cf9e2b44..fd04f50dc 100644 --- a/apps/exam_pop_up_controller.h +++ b/apps/exam_pop_up_controller.h @@ -4,6 +4,12 @@ #include #include "exam_pop_up_controller_delegate.h" +class HighContrastButton : public Button { +public: + using Button::Button; + virtual KDColor highlightedBackgroundColor() const override { return Palette::YellowDark; } +}; + class ExamPopUpController : public ViewController { public: ExamPopUpController(ExamPopUpControllerDelegate * delegate); @@ -31,8 +37,8 @@ private: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; - Button m_cancelButton; - Button m_okButton; + HighContrastButton m_cancelButton; + HighContrastButton m_okButton; MessageTextView m_warningTextView; MessageTextView m_messageTextView1; MessageTextView m_messageTextView2; diff --git a/apps/led_timer.cpp b/apps/led_timer.cpp deleted file mode 100644 index d462ca6c7..000000000 --- a/apps/led_timer.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "led_timer.h" - -LedTimer::LedTimer() : - Timer(1), - m_on(false) -{ -} - -bool LedTimer::fire() { - m_on = !m_on; - KDColor ledColor = m_on ? KDColorRed : KDColorBlack; - Ion::LED::setColor(ledColor); - return false; -} diff --git a/apps/led_timer.h b/apps/led_timer.h deleted file mode 100644 index 17f221e48..000000000 --- a/apps/led_timer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef APPS_LED_TIMER_H -#define APPS_LED_TIMER_H - -#include - -class LedTimer : public Timer { -public: - LedTimer(); -private: - bool fire() override; - bool m_on; -}; - -#endif - diff --git a/apps/on_boarding/update_controller.cpp b/apps/on_boarding/update_controller.cpp index f9243f418..d674e4b3a 100644 --- a/apps/on_boarding/update_controller.cpp +++ b/apps/on_boarding/update_controller.cpp @@ -65,7 +65,7 @@ UpdateController::UpdateController() : } bool UpdateController::handleEvent(Ion::Events::Event event) { - if (event != Ion::Events::Back && event != Ion::Events::OnOff) { + if (event != Ion::Events::Back && event != Ion::Events::OnOff && event != Ion::Events::USBPlug && event != Ion::Events::USBEnumeration) { app()->dismissModalViewController(); AppsContainer * appsContainer = (AppsContainer *)app()->container(); if (appsContainer->activeApp()->snapshot() == appsContainer->onBoardingAppSnapshot()) { diff --git a/apps/regression/banner_view.cpp b/apps/regression/banner_view.cpp index 24852c39e..259cff4ea 100644 --- a/apps/regression/banner_view.cpp +++ b/apps/regression/banner_view.cpp @@ -2,16 +2,19 @@ namespace Regression { +constexpr KDColor BannerView::k_textColor; +constexpr KDColor BannerView::k_backgroundColor; + BannerView::BannerView() : - m_dotNameView(KDText::FontSize::Small, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_xView(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_yView(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_regressionTypeView(KDText::FontSize::Small, (I18n::Message)0, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_subText1(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_subText2(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_subText3(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_subText4(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle), - m_subText5(KDText::FontSize::Small, 0.5f, 0.5f, KDColorBlack, Palette::GreyMiddle) + m_dotNameView(k_fontSize, 0.0f, 0.5f, k_textColor, k_backgroundColor), + m_xView(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor), + m_yView(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor), + m_regressionTypeView(k_fontSize, (I18n::Message)0, 0.0f, 0.5f, k_textColor,k_backgroundColor), + m_subText1(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor), + m_subText2(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor), + m_subText3(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor), + m_subText4(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor), + m_subText5(k_fontSize, 0.5f, 0.5f, k_textColor, k_backgroundColor) { } diff --git a/apps/regression/banner_view.h b/apps/regression/banner_view.h index 2483b8af1..49427a699 100644 --- a/apps/regression/banner_view.h +++ b/apps/regression/banner_view.h @@ -10,7 +10,11 @@ class BannerView : public Shared::BannerView { public: BannerView(); int numberOfTextviews() const { return k_numberOfTextViews; } + KDText::FontSize fontSize() const { return k_fontSize; } private: + static constexpr KDText::FontSize k_fontSize = KDText::FontSize::Small; + static constexpr KDColor k_textColor = KDColorBlack; + static constexpr KDColor k_backgroundColor = Palette::GreyMiddle; static constexpr int k_numberOfTextViews = 9; int numberOfSubviews() const override; TextView * textViewAtIndex(int i) const override; diff --git a/apps/regression/base.de.i18n b/apps/regression/base.de.i18n index f262ffe0f..7c49c2e7d 100644 --- a/apps/regression/base.de.i18n +++ b/apps/regression/base.de.i18n @@ -18,4 +18,4 @@ Exponential = "Exponentielle" Power = "Potenz" Trigonometrical = "Trigonometrische" Logistic = "Logistische" -DataNotSuitableForRegression = " Daten sind nicht für dieses Regressionsmodell geeignete" +DataNotSuitableForRegression = "Daten sind nicht geeignet" diff --git a/apps/regression/base.es.i18n b/apps/regression/base.es.i18n index 615b1b296..99448e054 100644 --- a/apps/regression/base.es.i18n +++ b/apps/regression/base.es.i18n @@ -18,4 +18,4 @@ Exponential = "Exponencial" Power = "Potencial" Trigonometrical = "Trigonometrica" Logistic = "Logistica" -DataNotSuitableForRegression = " Datos no adecuados para este modelo de regresión" +DataNotSuitableForRegression = "Datos no adecuados" diff --git a/apps/regression/base.fr.i18n b/apps/regression/base.fr.i18n index acf5196a8..dae5817a5 100644 --- a/apps/regression/base.fr.i18n +++ b/apps/regression/base.fr.i18n @@ -18,4 +18,4 @@ Exponential = "Exponentielle" Power = "Puissance" Trigonometrical = "Trigonométrique" Logistic = "Logistique" -DataNotSuitableForRegression = " Les données ne conviennent pas à ce modèle de régression" +DataNotSuitableForRegression = "Les données ne conviennent pas" diff --git a/apps/regression/base.pt.i18n b/apps/regression/base.pt.i18n index 993aa766e..33edbb367 100644 --- a/apps/regression/base.pt.i18n +++ b/apps/regression/base.pt.i18n @@ -18,4 +18,4 @@ Exponential = "Exponencial" Power = "Potencia" Trigonometrical = "Trigonometrica" Logistic = "Logistica" -DataNotSuitableForRegression = " Dados não adequados para este modelo de regressão" +DataNotSuitableForRegression = "Dados não adequados" diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index bb444af38..9a0a84556 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -9,6 +9,7 @@ using namespace Shared; static inline float min(float x, float y) { return (xy ? x : y); } +static inline int maxInt(int x, int y) { return (x>y ? x : y); } namespace Regression { @@ -68,6 +69,10 @@ void GraphController::selectRegressionCurve() { m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]); } +Poincare::Context * GraphController::globalContext() { + return const_cast(static_cast(app()->container()))->globalContext(); +} + CurveView * GraphController::curveView() { return &m_view; } @@ -154,11 +159,11 @@ void GraphController::reloadBannerView() { // Set formula Model * model = m_store->modelForSeries(selectedSeriesIndex()); - m_bannerView.setMessageAtIndex(model->formulaMessage(), 3); + I18n::Message formula = model->formulaMessage(); + m_bannerView.setMessageAtIndex(formula, 3); // Get the coefficients - Poincare::Context * globContext = const_cast(static_cast(app()->container()))->globalContext(); - double * coefficients = m_store->coefficientsForSeries(selectedSeriesIndex(), globContext); + double * coefficients = m_store->coefficientsForSeries(selectedSeriesIndex(), globalContext()); bool coefficientsAreDefined = true; for (int i = 0; i < model->numberOfCoefficients(); i++) { if (std::isnan(coefficients[i])) { @@ -167,13 +172,22 @@ void GraphController::reloadBannerView() { } } if (!coefficientsAreDefined) { + // Force the "Data not suitable" message to be on the next line + int numberOfCharToCompleteLine = maxInt(Ion::Display::Width/(KDText::charSize(m_bannerView.fontSize()).width())- strlen(I18n::translate(formula)), 0); + numberOfChar = 0; + for (int i = 0; i < numberOfCharToCompleteLine-1; i++) { + buffer[numberOfChar++] = ' '; + } + buffer[numberOfChar] = 0; + m_bannerView.setLegendAtIndex(buffer, 4); + const char * dataNotSuitableMessage = I18n::translate(I18n::Message::DataNotSuitableForRegression); - m_bannerView.setLegendAtIndex(const_cast(dataNotSuitableMessage), 4); - for (int i = 5; i < m_bannerView.numberOfTextviews(); i++) { - char empty[] = {0}; - m_bannerView.setLegendAtIndex(empty, i); - } - return; + m_bannerView.setLegendAtIndex(const_cast(dataNotSuitableMessage), 5); + for (int i = 6; i < m_bannerView.numberOfTextviews(); i++) { + char empty[] = {0}; + m_bannerView.setLegendAtIndex(empty, i); + } + return; } char coefficientName = 'a'; for (int i = 0; i < model->numberOfCoefficients(); i++) { @@ -258,9 +272,8 @@ bool GraphController::moveCursorHorizontally(int direction) { return false; } double x = direction > 0 ? m_cursor->x() + m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit : - m_cursor->x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit; - Poincare::Context * globContext = const_cast(static_cast(app()->container()))->globalContext(); - double y = m_store->yValueForXValue(*m_selectedSeriesIndex, x, globContext); + m_cursor->x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit; + double y = m_store->yValueForXValue(*m_selectedSeriesIndex, x, globalContext()); m_cursor->moveTo(x, y); m_store->panToMakePointVisible(x, y, cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio); return true; @@ -270,7 +283,7 @@ bool GraphController::moveCursorVertically(int direction) { int closestRegressionSeries = -1; int closestDotSeries = -1; int dotSelected = -1; - Poincare::Context * globContext = const_cast(static_cast(app()->container()))->globalContext(); + Poincare::Context * globContext = globalContext(); if (*m_selectedDotIndex == -1) { // The current cursor is on a regression diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h index bf3abb67d..1a2a9cda1 100644 --- a/apps/regression/graph_controller.h +++ b/apps/regression/graph_controller.h @@ -28,6 +28,7 @@ private: constexpr static int k_maxLegendLength = 16; constexpr static int k_maxNumberOfCharacters = 50; constexpr static float k_viewHeight = 174.0f; + Poincare::Context * globalContext(); Shared::CurveView * curveView() override; Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override; bool handleEnter() override; diff --git a/apps/shared/double_pair_store.cpp b/apps/shared/double_pair_store.cpp index dc44171a3..1a4a1064e 100644 --- a/apps/shared/double_pair_store.cpp +++ b/apps/shared/double_pair_store.cpp @@ -131,19 +131,29 @@ bool DoublePairStore::seriesNumberOfAbscissaeGreaterOrEqualTo(int series, int i) } uint32_t DoublePairStore::storeChecksum() const { - /* Ideally, we would only compute the checksum of the first m_numberOfPairs - * pairs. However, the two values of a pair are not stored consecutively. We - * thus compute the checksum on all pairs and ensure to set the pair at 0 - * when removing them. */ - size_t dataLengthInBytes = k_numberOfSeries*k_maxNumberOfPairs*k_numberOfColumnsPerSeries*sizeof(double); - assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 - return Ion::crc32((uint32_t *)m_data, dataLengthInBytes/sizeof(uint32_t)); + uint32_t checkSumPerSeries[k_numberOfSeries]; + for (int i = 0; i < k_numberOfSeries; i++) { + checkSumPerSeries[i] = storeChecksumForSeries(i); + } + return Ion::crc32(checkSumPerSeries, k_numberOfSeries); } uint32_t DoublePairStore::storeChecksumForSeries(int series) const { - size_t dataLengthInBytes = k_maxNumberOfPairs*k_numberOfColumnsPerSeries*sizeof(double); - assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 - return Ion::crc32((uint32_t *)m_data[series], dataLengthInBytes/sizeof(uint32_t)); + /* Ideally, we would compute the checksum of the first m_numberOfPairs pairs. + * However, the two values of a pair are not stored consecutively. We thus + * compute the checksum of the x values of the pairs, then we compute the + * checksum of the y values of the pairs, and finally we compute the checksum + * of the checksums. + * We cannot simply put "empty" values to 0 and compute the checksum of the + * whole data, because adding or removing (0, 0) "real" data pairs would not + * change the checksum. */ + size_t dataLengthInBytesPerDataColumn = m_numberOfPairs[series]*sizeof(double); + assert((dataLengthInBytesPerDataColumn & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 + uint32_t checkSumPerColumn[k_numberOfColumnsPerSeries]; + for (int i = 0; i < k_numberOfColumnsPerSeries; i++) { + checkSumPerColumn[i] = Ion::crc32((uint32_t *)m_data[series][i], dataLengthInBytesPerDataColumn/sizeof(uint32_t)); + } + return Ion::crc32(checkSumPerColumn, k_numberOfColumnsPerSeries); } double DoublePairStore::defaultValue(int series, int i, int j) const { diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 8602c5b0e..1ef6b6a0e 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -7,6 +7,9 @@ using namespace Poincare; +static inline int min(int x, int y) { return (xy ? x : y); } + namespace Shared { StoreController::ContentView::ContentView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate) : diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 483a8785e..003a98214 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -31,4 +31,9 @@ i18n_files += $(addprefix apps/statistics/,\ base.pt.i18n\ ) +tests += $(addprefix apps/statistics/test/,\ + store.cpp\ +) +test_objs += $(addprefix apps/statistics/, store.o) + app_images += apps/statistics/stat_icon.png diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index d605b1b25..7224eca88 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -245,16 +245,18 @@ double Store::sortedElementAtCumulatedFrequency(int series, double k, bool creat // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity assert(k >= 0.0 && k <= 1.0); double totalNumberOfElements = sumOfOccurrences(series); + double numberOfElementsAtFrequencyK = totalNumberOfElements * k; + double bufferValues[numberOfPairsOfSeries(series)]; memcpy(bufferValues, m_data[series][0], numberOfPairsOfSeries(series)*sizeof(double)); int sortedElementIndex = 0; - double cumulatedFrequency = 0.0; - while (cumulatedFrequency < k) { + double cumulatedNumberOfElements = 0.0; + while (cumulatedNumberOfElements < numberOfElementsAtFrequencyK-DBL_EPSILON) { sortedElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series)); bufferValues[sortedElementIndex] = DBL_MAX; - cumulatedFrequency += m_data[series][1][sortedElementIndex] / totalNumberOfElements; + cumulatedNumberOfElements += m_data[series][1][sortedElementIndex]; } - if (createMiddleElement && std::fabs(cumulatedFrequency - k) < DBL_EPSILON) { + if (createMiddleElement && std::fabs(cumulatedNumberOfElements - numberOfElementsAtFrequencyK) < DBL_EPSILON) { int nextElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series)); if (bufferValues[nextElementIndex] != DBL_MAX) { return (m_data[series][0][sortedElementIndex] + m_data[series][0][nextElementIndex]) / 2.0; diff --git a/apps/statistics/test/store.cpp b/apps/statistics/test/store.cpp new file mode 100644 index 000000000..ecca0596e --- /dev/null +++ b/apps/statistics/test/store.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include "../store.h" + +namespace Statistics { + +void assert_value_approximately_equal_to(double d1, double d2) { + assert((std::isnan(d1) && std::isnan(d2)) + || (std::isinf(d1) && std::isinf(d2) && d1*d2 > 0 /*same sign*/) + || fabs(d1-d2) < 0.001); +} + +void assert_data_statictics_equal_to(double n[], double v[], int numberOfData, double sumOfOccurrences, double maxValue, double minValue, double range, double mean, double variance, double standardDeviation, double sampleStandardDeviation, double firstQuartile, double thirdQuartile, double quartileRange, double median, double sum, double squaredValueSum) { + Store store; + int seriesIndex = 0; + + // Set the data in the store + for (int i = 0; i < numberOfData; i++) { + store.set(n[i], seriesIndex, 0, i); + store.set(v[i], seriesIndex, 1, i); + } + + // Compare the statistics + assert_value_approximately_equal_to(standardDeviation * standardDeviation, variance); + assert_value_approximately_equal_to(store.sumOfOccurrences(seriesIndex), sumOfOccurrences); + assert_value_approximately_equal_to(store.maxValue(seriesIndex), maxValue); + assert_value_approximately_equal_to(store.minValue(seriesIndex), minValue); + assert_value_approximately_equal_to(store.range(seriesIndex), range); + assert_value_approximately_equal_to(store.mean(seriesIndex), mean); + assert_value_approximately_equal_to(store.variance(seriesIndex), variance); + assert_value_approximately_equal_to(store.standardDeviation(seriesIndex), standardDeviation); + assert_value_approximately_equal_to(store.sampleStandardDeviation(seriesIndex), sampleStandardDeviation); + assert_value_approximately_equal_to(store.firstQuartile(seriesIndex), firstQuartile); + assert_value_approximately_equal_to(store.thirdQuartile(seriesIndex), thirdQuartile); + assert_value_approximately_equal_to(store.quartileRange(seriesIndex), quartileRange); + assert_value_approximately_equal_to(store.median(seriesIndex), median); + assert_value_approximately_equal_to(store.sum(seriesIndex), sum); + assert_value_approximately_equal_to(store.squaredValueSum(seriesIndex), squaredValueSum); +} + +QUIZ_CASE(data_statistics) { + /* 1 2 3 4 + * 1 1 1 1 */ + double n1[4] = {1.0, 2.0, 3.0, 4.0}; + double v1[4] = {1.0, 1.0, 1.0, 1.0}; + assert_data_statictics_equal_to( + n1, + v1, + 4, + /* sumOfOccurrences */ 4.0, + /* maxValue */ 4.0, + /* minValue */ 1.0, + /* range */ 3.0, + /* mean */ 2.5, + /* variance */ 1.25, + /* standardDeviation */ 1.118, + /* sampleStandardDeviation */ 1.291, + /* firstQuartile */ 1.0, + /* thirdQuartile */ 3.0, + /* quartileRange */ 2.0, + /* median */ 2.5, + /* sum */ 10.0, + /* squaredValueSum */ 30.0); + + + /* 1 2 3 4 5 6 7 8 9 10 11 + * 1 1 1 1 1 1 1 1 1 1 1 */ + + double n2[11] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0}; + double v2[11] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + assert_data_statictics_equal_to( + n2, + v2, + 11, + /* sumOfOccurrences */ 11.0, + /* maxValue */ 11.0, + /* minValue */ 1.0, + /* range */ 10.0, + /* mean */ 6.0, + /* variance */ 10.0, + /* standardDeviation */ 3.1623, + /* sampleStandardDeviation */ 3.3166, + /* firstQuartile */ 3.0, + /* thirdQuartile */ 9.0, + /* quartileRange */ 6.0, + /* median */ 6.0, + /* sum */ 66.0, + /* squaredValueSum */ 506.0); + + /* 1 2 3 4 5 6 7 8 9 10 11 12 + * 1 1 1 1 1 1 1 1 1 1 1 1 */ + + double n3[12] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0}; + double v3[12] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + assert_data_statictics_equal_to( + n3, + v3, + 12, + /* sumOfOccurrences */ 12.0, + /* maxValue */ 12.0, + /* minValue */ 1.0, + /* range */ 11.0, + /* mean */ 6.5, + /* variance */ 11.917, + /* standardDeviation */ 3.4521, + /* sampleStandardDeviation */ 3.6056, + /* firstQuartile */ 3.0, + /* thirdQuartile */ 9.0, + /* quartileRange */ 6.0, + /* median */ 6.5, + /* sum */ 78.0, + /* squaredValueSum */ 650.0); + + /* 1 2 3 5 10 + * 0.2 0.05 0.3 0.0001 0.4499 */ + double n4[5] = {1.0, 2.0, 3.0, 5.0, 10.0}; + double v4[5] = {0.2, 0.05, 0.3, 0.0001, 0.4499}; + assert_data_statictics_equal_to( + n4, + v4, + 5, + /* sumOfOccurrences */ 1.0, + /* maxValue */ 10.0, + /* minValue */ 1.0, + /* range */ 9.0, + /* mean */ 5.6995, + /* variance */ 15.6082, + /* standardDeviation */ 3.9507, + /* sampleStandardDeviation */ INFINITY, + /* firstQuartile */ 2.0, + /* thirdQuartile */ 10.0, + /* quartileRange */ 8.0, + /* median */ 3.0, + /* sum */ 5.6995, + /* squaredValueSum */ 48.0925); + + /* 1 -2 3 5 10 + * 0.4 0.00005 0.9 0.4 0.5 */ + double n5[5] = {1.0, -2.0, 3.0, 5.0, 10.0}; + double v5[5] = {0.4, 0.00005, 0.9, 0.4, 0.5}; + assert_data_statictics_equal_to( + n5, + v5, + 5, + /* sumOfOccurrences */ 2.2, + /* maxValue */ 10.0, + /* minValue */ -2.0, + /* range */ 12.0, + /* mean */ 4.5908, + /* variance */ 10.06, + /* standardDeviation */ 3.1719, + /* sampleStandardDeviation */ 4.2947, + /* firstQuartile */ 3.0, + /* thirdQuartile */ 5.0, + /* quartileRange */ 2.0, + /* median */ 3.0, + /* sum */ 10.1, + /* squaredValueSum */ 68.500); + + /* -7 -10 12 5 -2 + * 4 5 3 1 9 */ + double n6[6] = {-7.0, -10.0, 1.0, 2.0, 5.0, -2.0}; + double v6[6] = {4.0, 5.0, 3.0, 0.5, 1.0, 9.0}; + assert_data_statictics_equal_to( + n6, + v6, + 6, + /* sumOfOccurrences */ 22.5, + /* maxValue */ 5.0, + /* minValue */ -10.0, + /* range */ 15.0, + /* mean */ -3.8667, + /* variance */ 18.9155, + /* standardDeviation */ 4.3492, + /* sampleStandardDeviation */ 4.4492, + /* firstQuartile */ -7.0, + /* thirdQuartile */ -2.0, + /* quartileRange */ 5.0, + /* median */ -2.0, + /* sum */ -87.0, + /* squaredValueSum */ 762.0); + + /* 1 1 1 10 3 -1 3 + * 1 1 1 0 0 0 1 */ + double n7[7] = {1.0, 1.0, 1.0, 10.0, 3.0, -1.0, 3.0}; + double v7[7] = {1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0}; + assert_data_statictics_equal_to( + n7, + v7, + 7, + /* sumOfOccurrences */ 4.0, + /* maxValue */ 3.0, + /* minValue */ 1.0, + /* range */ 2.0, + /* mean */ 1.5, + /* variance */ 0.75, + /* standardDeviation */ 0.866, + /* sampleStandardDeviation */ 1.0, + /* firstQuartile */ 1.0, + /* thirdQuartile */ 1.0, + /* quartileRange */ 0.0, + /* median */ 1.0, + /* sum */ 6.0, + /* squaredValueSum */ 12.0); + +} + +} diff --git a/apps/variable_box_controller.h b/apps/variable_box_controller.h index 14f450ed0..37916635d 100644 --- a/apps/variable_box_controller.h +++ b/apps/variable_box_controller.h @@ -42,7 +42,7 @@ private: #endif Matrix }; - constexpr static int k_maxNumberOfDisplayedRows = 6; //240/40 + constexpr static int k_maxNumberOfDisplayedRows = 6; //240/Matrix::ToolboxRowHeight #if LIST_VARIABLES constexpr static int k_numberOfMenuRows = 3; #else diff --git a/build/config.mak b/build/config.mak index 2b6e610a3..08597b617 100644 --- a/build/config.mak +++ b/build/config.mak @@ -3,7 +3,7 @@ PLATFORM ?= device DEBUG ?= 0 -EPSILON_VERSION ?= 1.6.0 +EPSILON_VERSION ?= 1.7.0 EPSILON_ONBOARDING_APP ?= 1 EPSILON_SOFTWARE_UPDATE_PROMPT ?= 1 EPSILON_APPS ?= calculation graph code statistics probability solver sequence regression settings diff --git a/build/targets.device.mak b/build/targets.device.mak index 7e610b06d..5a277dd20 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -23,7 +23,7 @@ products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products))) %.map: %.elf @echo "LDMAP $@" - $(Q) $(LD) $^ $(LDFLAGS) -M -Map $@ -o /dev/null + $(Q) $(LD) $^ $(LDFLAGS) -Wl,-M -Wl,-Map=$@ -o /dev/null .PHONY: %_memory_map %_memory_map: %.map diff --git a/build/toolchain.arm-gcc.mak b/build/toolchain.arm-gcc.mak index 845994b2d..5549509f1 100644 --- a/build/toolchain.arm-gcc.mak +++ b/build/toolchain.arm-gcc.mak @@ -1,6 +1,6 @@ CC = arm-none-eabi-gcc CXX = arm-none-eabi-g++ -LD = arm-none-eabi-ld.bfd +LD = arm-none-eabi-gcc GDB = arm-none-eabi-gdb OBJCOPY = arm-none-eabi-objcopy SIZE = arm-none-eabi-size @@ -9,6 +9,7 @@ ifeq ($(DEBUG),1) SFLAGS += -ggdb3 else SFLAGS += -fdata-sections -ffunction-sections -LDFLAGS += --gc-sections +LDFLAGS += -Wl,--gc-sections endif SFLAGS += -mthumb -march=armv7e-m -mfloat-abi=hard -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 +LDFLAGS += $(SFLAGS) -lgcc -Wl,-T,$(LDSCRIPT) diff --git a/escher/include/escher/button.h b/escher/include/escher/button.h index 9722c0a5f..fc3633a98 100644 --- a/escher/include/escher/button.h +++ b/escher/include/escher/button.h @@ -6,6 +6,7 @@ #include #include #include +#include class Button : public HighlightCell, public Responder { public: @@ -13,6 +14,7 @@ public: void setMessage(I18n::Message message); bool handleEvent(Ion::Events::Event event) override; void setHighlighted(bool highlight) override; + virtual KDColor highlightedBackgroundColor() const { return Palette::Select; } Responder * responder() override { return this; } diff --git a/escher/src/button.cpp b/escher/src/button.cpp index 97dd9814e..1f2bbf33f 100644 --- a/escher/src/button.cpp +++ b/escher/src/button.cpp @@ -38,7 +38,7 @@ bool Button::handleEvent(Ion::Events::Event event) { void Button::setHighlighted(bool highlight) { HighlightCell::setHighlighted(highlight); - KDColor backgroundColor = highlight? Palette::Select : KDColorWhite; + KDColor backgroundColor = highlight? highlightedBackgroundColor() : KDColorWhite; m_messageTextView.setBackgroundColor(backgroundColor); markRectAsDirty(bounds()); } diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 88813e7c6..54d57548a 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -76,7 +76,7 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool if (resultExpression.isUninitialized()) { m_contentView.cursor()->insertText(text); } else { - LayoutRef resultLayoutRef = resultExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits()); + LayoutRef resultLayoutRef = resultExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::PrintFloat::k_numberOfStoredSignificantDigits); if (currentNumberOfLayouts + resultLayoutRef.numberOfDescendants(true) >= k_maxNumberOfLayouts) { return true; } diff --git a/escher/src/switch_view.cpp b/escher/src/switch_view.cpp index 23ebd23a6..a88dfb7a0 100644 --- a/escher/src/switch_view.cpp +++ b/escher/src/switch_view.cpp @@ -59,7 +59,7 @@ void SwitchView::drawRect(KDContext * ctx, KDRect rect) const { KDColor mainColor = m_state ? Palette::YellowDark : Palette::GreyDark; KDRect frame(width - k_switchWidth, heightCenter -switchHalfHeight, k_switchWidth, k_switchHeight); ctx->blendRectWithMask(frame, mainColor, (const uint8_t *)switchMask, s_switchWorkingBuffer); - KDCoordinate onOffX = width - (m_state ? k_switchWidth : k_onOffSize); + KDCoordinate onOffX = width - (m_state ? k_onOffSize : k_switchWidth); KDRect onOffFrame(onOffX, heightCenter -switchHalfHeight, k_onOffSize, k_onOffSize); ctx->blendRectWithMask(onOffFrame, KDColorWhite, (const uint8_t *)onOffMask, s_switchWorkingBuffer); } diff --git a/ion/include/ion/led.h b/ion/include/ion/led.h index 8ca251504..2d4475eab 100644 --- a/ion/include/ion/led.h +++ b/ion/include/ion/led.h @@ -6,7 +6,9 @@ namespace Ion { namespace LED { +KDColor getColor(); void setColor(KDColor c); +void setBlinking(uint16_t periodInMilliseconds, float dutyCycle); } } diff --git a/ion/src/device/bench/command/display.cpp b/ion/src/device/bench/command/display.cpp index 98e536d54..224c83d47 100644 --- a/ion/src/device/bench/command/display.cpp +++ b/ion/src/device/bench/command/display.cpp @@ -1,6 +1,7 @@ #include "command.h" #include #include +#include namespace Ion { namespace Device { @@ -46,6 +47,8 @@ void Display(const char * input) { } } + int numberOfInvalidPixels = 0; + for (int i=0; isetTIM3EN(true); RCC.APB1ENR()->setPWREN(true); @@ -279,15 +281,21 @@ void initClocks() { RCC.APB2ENR()->set(apb2enr); } -void shutdownClocks() { +void shutdownClocks(bool keepLEDAwake) { // APB2 bus RCC.APB2ENR()->set(0x00008000); // Reset value - // AHB1 - RCC.APB1ENR()->set(0x00000400); - + // APB1 + class RCC::APB1ENR apb1enr(0x00000400); // Reset value // AHB1 bus - RCC.AHB1ENR()->set(0); // Reset value + class RCC::AHB1ENR ahb1enr(0); // Reset value + if (keepLEDAwake) { + apb1enr.setTIM3EN(true); + ahb1enr.setGPIOBEN(true); + ahb1enr.setGPIOCEN(true); + } + RCC.APB1ENR()->set(apb1enr); + RCC.AHB1ENR()->set(ahb1enr); RCC.AHB3ENR()->setFSMCEN(false); } diff --git a/ion/src/device/device.h b/ion/src/device/device.h index b227f1aa7..c3332fd77 100644 --- a/ion/src/device/device.h +++ b/ion/src/device/device.h @@ -12,9 +12,9 @@ void coreReset(); void jumpReset(); void initPeripherals(); -void shutdownPeripherals(); +void shutdownPeripherals(bool keepLEDAwake = false); void initClocks(); -void shutdownClocks(); +void shutdownClocks(bool keepLEDAwake = false); /* The serial number is 96 bits long. That's equal to 16 digits in base 64. We * expose a convenient "copySerialNumber" routine which can be called without diff --git a/ion/src/device/led.cpp b/ion/src/device/led.cpp index f4c7037ee..028e8e846 100644 --- a/ion/src/device/led.cpp +++ b/ion/src/device/led.cpp @@ -1,14 +1,40 @@ #include #include +#include "device.h" #include "led.h" #include "regs/regs.h" // Public Ion::LED methods +static KDColor sLedColor = KDColorBlack; + +KDColor Ion::LED::getColor() { + return sLedColor; +} + void Ion::LED::setColor(KDColor c) { - TIM3.CCR2()->set(Device::dutyCycleForUInt8(c.red())); - TIM3.CCR3()->set(Device::dutyCycleForUInt8(c.blue())); - TIM3.CCR4()->set(Device::dutyCycleForUInt8(c.green())); + sLedColor = c; + + /* Active all RGB colors */ + TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::PWM1); + TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::PWM1); + TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::PWM1); + + /* Set the PWM duty cycles to display the right color */ + constexpr float maxColorValue = (float)((1 << 8) -1); + Device::setPeriodAndDutyCycles(Device::Mode::PWM, c.red()/maxColorValue, c.green()/maxColorValue, c.blue()/maxColorValue); +} + +void Ion::LED::setBlinking(uint16_t period, float dutyCycle) { + /* We want to use the PWM at a slow rate to display a seeable blink. + * Consequently, we do not use PWM to display the right color anymore but to + * blink. We cannot use the PWM to display the exact color so we 'project the + * color on 3 bits' : all colors have 2 states - active or not. */ + TIM3.CCMR()->setOC2M(sLedColor.red() > 0 ? TIM::CCMR::OCM::PWM1 : TIM::CCMR::OCM::ForceInactive); + TIM3.CCMR()->setOC4M(sLedColor.green() > 0 ? TIM::CCMR::OCM::PWM1 : TIM::CCMR::OCM::ForceInactive); + TIM3.CCMR()->setOC3M(sLedColor.blue() > 0 ? TIM::CCMR::OCM::PWM1 : TIM::CCMR::OCM::ForceInactive); + + Device::setPeriodAndDutyCycles(Device::Mode::Blink, dutyCycle, dutyCycle, dutyCycle, period); } // Private Ion::Device::LED methods @@ -45,23 +71,6 @@ void shutdownGPIO() { } void initTimer() { - /* Let's set the prescaler to 1. Increasing the prescaler would slow down the - * modulation, which can be useful when debugging. */ - TIM3.PSC()->set(1); - - /* Pulse width modulation mode allows you to generate a signal with a - * frequency determined by the value of the TIMx_ARR register and a duty cycle - * determined by the value of the TIMx_CCRx register. */ - TIM3.ARR()->set(PWMPeriod); - TIM3.CCR2()->set(0); - TIM3.CCR3()->set(0); - TIM3.CCR4()->set(0); - - // Set Channels 2-4 as outputs, PWM mode 1 - TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::PWM1); - TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::PWM1); - TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::PWM1); - // Output preload enable for channels 2-4 TIM3.CCMR()->setOC2PE(true); TIM3.CCMR()->setOC3PE(true); @@ -81,23 +90,42 @@ void initTimer() { } void shutdownTimer() { - TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::ForceInactive); - TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::ForceInactive); - TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::ForceInactive); + TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::ForceInactive); + TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::ForceInactive); + TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::ForceInactive); } -void enforceState(bool red, bool green, bool blue) { - bool states[3] = {red, green, blue}; - for (int i=0; i<3; i++) { - GPIOPin p = RGBPins[i]; - if (states[i]) { - p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Output); - p.group().ODR()->set(p.pin(), true); - } else { - p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Analog); - p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None); - } +/* Pulse width modulation mode allows you to generate a signal with a + * frequency determined by the value of the TIMx_ARR register and a duty cycle + * determined by the value of the TIMx_CCRx register. */ + +void setPeriodAndDutyCycles(Mode mode, float dutyCycleRed, float dutyCycleGreen, float dutyCycleBlue, uint16_t period) { + switch (mode) { + case Mode::PWM: + /* Let's set the prescaler to 1. Increasing the prescaler would slow down + * the modulation, which can be useful when debugging or when we want an + * actual blinking. */ + TIM3.PSC()->set(1); + TIM3.ARR()->set(PWMPeriod); + period = PWMPeriod; + break; + case Mode::Blink: + int systemClockFreq = 96; + /* We still want to do PWM, but at a rate slow enough to blink. Ideally, + * we want to pre-scale the period to be able to set it in milliseconds; + * however, as the prescaler is cap by 2^16-1, we divide it by a factor + * and correct the period consequently. */ + int factor = 2; + // TODO: explain the 2 ? + TIM3.PSC()->set(systemClockFreq*1000/factor); + period *= factor; + TIM3.ARR()->set(period); + break; } + + TIM3.CCR2()->set(dutyCycleRed*period); + TIM3.CCR3()->set(dutyCycleBlue*period); + TIM3.CCR4()->set(dutyCycleGreen*period); } } diff --git a/ion/src/device/led.h b/ion/src/device/led.h index b4b368c5e..fe46994d9 100644 --- a/ion/src/device/led.h +++ b/ion/src/device/led.h @@ -14,10 +14,20 @@ namespace Device { * PC7 | LED red | Alternate Function 2 | TIM3_CH2 */ +enum class Mode { + PWM, + Blink +}; + +enum class Color { + Red, + Green, + Blue +}; + void init(); void shutdown(); -/* This call bypasses the timer, and immediately enforces a given LED state. */ -void enforceState(bool red, bool green, bool blue); +void setPeriodAndDutyCycles(Mode mode, float dutyCycleRed, float dutyCycleGreen, float dutyCycleBlue, uint16_t period = 0); void initGPIO(); void shutdownGPIO(); @@ -31,11 +41,6 @@ constexpr static GPIOPin RGBPins[] = { constexpr uint16_t PWMPeriod = 40000; -inline uint16_t dutyCycleForUInt8(uint8_t value) { - /* This function is a linear function from colors [0->255] to duty cycles [0->PWMPeriod].*/ - return ((uint32_t)value)*(PWMPeriod/255); -} - } } } diff --git a/ion/src/device/power.cpp b/ion/src/device/power.cpp index 948b9eb89..021d75cc6 100644 --- a/ion/src/device/power.cpp +++ b/ion/src/device/power.cpp @@ -9,6 +9,7 @@ #include "regs/regs.h" void Ion::Power::suspend(bool checkIfPowerKeyReleased) { + bool isLEDActive = Ion::LED::getColor() != KDColorBlack; if (checkIfPowerKeyReleased) { /* Wait until power is released to avoid restarting just after suspending */ bool isPowerDown = true; @@ -17,28 +18,28 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) { isPowerDown = scan.keyDown(Keyboard::Key::B2); } } - Device::shutdownPeripherals(); + Device::shutdownPeripherals(isLEDActive); PWR.CR()->setLPDS(true); // Turn the regulator off. Takes longer to wake up. PWR.CR()->setFPDS(true); // Put the flash to sleep. Takes longer to wake up. - CM4.SCR()->setSLEEPDEEP(true); + CM4.SCR()->setSLEEPDEEP(!isLEDActive); - WakeUp::Device::onUSBPlugging(); -#if LED_WHILE_CHARGING - WakeUp::Device::onChargingEvent(); -#endif while (1) { -#if LED_WHILE_CHARGING +#if EPSILON_LED_WHILE_CHARGING /* Update LEDS * if the standby mode was stopped due to a "stop charging" event, we wait * a while to be sure that the plug state of the USB is up-to-date. */ msleep(200); - LED::Device::enforceState(Battery::isCharging(), USB::isPlugged(), false); + //Ion::LED::setCharging(Ion::USB::isPlugged(), Ion::Battery::isCharging()); #endif WakeUp::Device::onPowerKeyDown(); + WakeUp::Device::onUSBPlugging(); +#if EPSILON_LED_WHILE_CHARGING + WakeUp::Device::onChargingEvent(); +#endif - Device::shutdownClocks(); + Device::shutdownClocks(isLEDActive); /* To enter sleep, we need to issue a WFE instruction, which waits for the * event flag to be set and then clears it. However, the event flag might @@ -47,7 +48,7 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) { * to clear it, and then a second WFE to wait for a _new_ event. */ asm("sev"); asm("wfe"); - msleep(1); + asm("nop"); asm("wfe"); Device::initClocks(); diff --git a/ion/src/device/regs/rcc.h b/ion/src/device/regs/rcc.h index 4ef6ffbea..0536441d4 100644 --- a/ion/src/device/regs/rcc.h +++ b/ion/src/device/regs/rcc.h @@ -9,7 +9,7 @@ public: public: REGS_BOOL_FIELD(HSION, 0); REGS_BOOL_FIELD(HSEON, 16); - REGS_BOOL_FIELD(HSERDY, 17); + REGS_BOOL_FIELD_R(HSERDY, 17); REGS_BOOL_FIELD(PLLON, 24); REGS_BOOL_FIELD(PLLRDY, 25); }; @@ -56,7 +56,7 @@ public: AHBDividedBy8 = 6, AHBDividedBy16 = 7 }; - void setPPRE1(APBPrescaler p) volatile { setBitRange(12, 10, (uint32_t)p); } + void setPPRE1(APBPrescaler r) volatile { setBitRange(12, 10, (uint32_t)r); } }; class AHB1ENR : public Register32 { @@ -107,6 +107,19 @@ public: REGS_BOOL_FIELD(SYSCFGEN, 14); }; + class AHB1LPENR : public Register32 { + public: + using Register32::Register32; + REGS_BOOL_FIELD(GPIOBLPEN, 1); + REGS_BOOL_FIELD(GPIOCLPEN, 2); + }; + + class APB1LPENR : public Register32 { + public: + using Register32::Register32; + REGS_BOOL_FIELD(TIM3LPEN, 1); + }; + class DCKCFGR2 : Register32 { public: REGS_BOOL_FIELD(CK48MSEL, 27); @@ -122,6 +135,8 @@ public: REGS_REGISTER_AT(AHB3ENR, 0x38); REGS_REGISTER_AT(APB1ENR, 0x40); REGS_REGISTER_AT(APB2ENR, 0x44); + REGS_REGISTER_AT(AHB1LPENR, 0x50); + REGS_REGISTER_AT(APB1LPENR, 0x60); REGS_REGISTER_AT(DCKCFGR2, 0x94); private: constexpr uint32_t Base() const { diff --git a/ion/src/device/regs/tim.h b/ion/src/device/regs/tim.h index c614c82f1..d76f8e293 100644 --- a/ion/src/device/regs/tim.h +++ b/ion/src/device/regs/tim.h @@ -3,6 +3,7 @@ #include "register.h" +template class TIM { public: class CR1 : Register16 { @@ -63,10 +64,10 @@ public: class PSC : public Register16 {}; class ARR : public Register16 {}; - class CCR1 : public Register16 {}; - class CCR2 : public Register16 {}; - class CCR3 : public Register16 {}; - class CCR4 : public Register16 {}; + class CCR1 : public RegisterWidth {}; + class CCR2 : public RegisterWidth {}; + class CCR3 : public RegisterWidth {}; + class CCR4 : public RegisterWidth {}; constexpr TIM(int i) : m_index(i) {} REGS_REGISTER_AT(CR1, 0x0); @@ -81,12 +82,19 @@ public: REGS_REGISTER_AT(BDTR, 0x44); private: constexpr uint32_t Base() const { - return (m_index == 1 ? 0x40010000 : 0x40000000 + 0x400*(m_index-2)); - }; + return (m_index == 1 ? 0x40010000 : + (m_index <= 7 ? 0x40000000 + 0x400*(m_index-2) : + (m_index == 8 ? 0x40010400 : + (m_index <= 11 ? 0x40014000 + 0x400*(m_index-9) : + 0x40001800 + 0x400*(m_index-12) + ) + ) + ) + ); + } int m_index; }; -constexpr TIM TIM1(1); -constexpr TIM TIM3(3); +constexpr TIM TIM3(3); #endif diff --git a/ion/src/device/usb/Makefile b/ion/src/device/usb/Makefile index 468372384..8873590c8 100644 --- a/ion/src/device/usb/Makefile +++ b/ion/src/device/usb/Makefile @@ -51,7 +51,7 @@ dfu_objs += ion/src/device/usb.o dfu_objs += ion/src/device/base64.o dfu_objs += ion/src/device/flash.o -ion/src/device/usb/dfu.elf: LDFLAGS = --gc-sections -T ion/src/device/usb/dfu.ld +ion/src/device/usb/dfu.elf: LDSCRIPT = ion/src/device/usb/dfu.ld ion/src/device/usb/dfu.elf: $(usb_objs) $(dfu_objs) ion/src/device/usb/dfu.o: ion/src/device/usb/dfu.bin diff --git a/ion/src/device/wakeup.cpp b/ion/src/device/wakeup.cpp index d54ea0f83..3582d6ad3 100644 --- a/ion/src/device/wakeup.cpp +++ b/ion/src/device/wakeup.cpp @@ -31,7 +31,7 @@ void onUSBPlugging() { SYSCFG.EXTICR3()->setEXTI(USB::Device::VbusPin.pin(), USB::Device::VbusPin.group()); EXTI.EMR()->set(USB::Device::VbusPin.pin(), true); -#if LED_WHILE_CHARGING +#if EPSILON_LED_WHILE_CHARGING EXTI.FTSR()->set(USB::Device::VbusPin.pin(), true); #endif EXTI.RTSR()->set(USB::Device::VbusPin.pin(), true); diff --git a/ion/src/shared/dummy/led.cpp b/ion/src/shared/dummy/led.cpp index 12452f37b..097aff116 100644 --- a/ion/src/shared/dummy/led.cpp +++ b/ion/src/shared/dummy/led.cpp @@ -1,4 +1,15 @@ #include -void Ion::LED::setColor(KDColor c) { +namespace Ion { +namespace LED { + +KDColor getColor() { + return KDColorBlack; +} + +void setColor(KDColor c) {} + +void setBlinking(uint16_t period, float dutyCycle) {} + +} } diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 9f73f59b1..9185e96b4 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -1,6 +1,5 @@ objs += $(addprefix ion/src/simulator/, \ init.o\ - led.o\ ) objs += $(addprefix ion/src/simulator/boot/, main.o) @@ -17,6 +16,7 @@ objs += $(addprefix ion/src/shared/, \ dummy/backlight.o \ dummy/battery.o \ dummy/fcc_id.o \ + dummy/led.o \ dummy/serial_number.o \ dummy/usb.o \ ) diff --git a/ion/src/simulator/led.cpp b/ion/src/simulator/led.cpp deleted file mode 100644 index a48d4ca13..000000000 --- a/ion/src/simulator/led.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void Ion::LED::setColor(KDColor c) { -} - - diff --git a/liba/Makefile b/liba/Makefile index 9dc39e4de..da54983c1 100644 --- a/liba/Makefile +++ b/liba/Makefile @@ -138,58 +138,4 @@ tests += $(addprefix liba/test/, \ # In practice we're always using liba on such a target. objs += $(addprefix liba/src/aeabi-rt/, \ atexit.o \ - double.o \ - llsl.o \ - llsr.o \ - long.o \ - memclr.o \ - memcpy.o \ -) - -liba/src/external/softfloat/src/%.o: CFLAGS += -Iliba/src/external/softfloat/include -Iliba/src/external/softfloat/src/8086 -Iliba/src/external/softfloat/port -liba/src/external/softfloat/src/s_roundPackToF64.o: CFLAGS += -w -liba/src/external/softfloat/src/s_roundPackToF32.o: CFLAGS += -w -# s_roundPackToF64 and s_roundPackToF32 are throwing warnings - -objs += $(addprefix liba/src/external/softfloat/src/, \ - 8086/s_commonNaNToF32UI.o \ - 8086/s_commonNaNToF64UI.o \ - 8086/s_f32UIToCommonNaN.o \ - 8086/s_f64UIToCommonNaN.o \ - 8086/s_propagateNaNF64UI.o \ - 8086/softfloat_raiseFlags.o \ - f32_to_f64.o \ - f32_to_i64_r_minMag.o\ - f64_add.o \ - f64_div.o \ - f64_eq.o \ - f64_le.o \ - f64_lt.o \ - f64_mul.o \ - f64_sub.o \ - f64_to_i32_r_minMag.o \ - f64_to_i64_r_minMag.o \ - f64_to_f32.o \ - i32_to_f64.o \ - i64_to_f32.o \ - i64_to_f64.o \ - s_addMagsF64.o \ - s_approxRecip32_1.o \ - s_approxRecip_1Ks.o \ - s_countLeadingZeros32.o \ - s_countLeadingZeros64.o \ - s_countLeadingZeros8.o \ - s_mul64To128M.o \ - s_normRoundPackToF64.o \ - s_normSubnormalF32Sig.o \ - s_normSubnormalF64Sig.o \ - s_roundPackToF32.o \ - s_roundPackToF64.o \ - s_shiftRightJam32.o \ - s_shiftRightJam64.o \ - s_shortShiftRightJam64.o \ - s_subMagsF64.o \ - softfloat_state.o \ - ui32_to_f64.o \ - ui64_to_f64.o \ ) diff --git a/poincare/include/poincare/evaluation.h b/poincare/include/poincare/evaluation.h index 9f5e678b5..f0bc3f752 100644 --- a/poincare/include/poincare/evaluation.h +++ b/poincare/include/poincare/evaluation.h @@ -46,7 +46,6 @@ public: * are enable which can lead to weird code: * ie, you can write: 'Complex a(2); MatrixComplex b(a);' * */ - static_assert(sizeof(U) == sizeof(Evaluation), "Size mismatch"); return *reinterpret_cast(const_cast *>(this)); } diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index 97d459e54..80faedd12 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -163,7 +163,7 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print } /* Case 1: Decimal mode */ int deltaCharMantissa = exponent < 0 ? -exponent+1 : 0; - strlcpy(buffer+currentChar+deltaCharMantissa, tempBuffer, bufferSize-deltaCharMantissa-currentChar); + strlcpy(buffer+currentChar+deltaCharMantissa, tempBuffer, max(0, bufferSize-deltaCharMantissa-currentChar)); if (exponent < 0) { for (int i = 0; i <= -exponent; i++) { if (currentChar >= bufferSize-1) { return bufferSize-1; } diff --git a/poincare/src/evaluation.cpp b/poincare/src/evaluation.cpp index b0be70e18..30f98125f 100644 --- a/poincare/src/evaluation.cpp +++ b/poincare/src/evaluation.cpp @@ -16,7 +16,6 @@ Expression Evaluation::complexToExpression(Preferences::ComplexFormat complex template Evaluation Evaluation::childAtIndex(int i) const; template Evaluation Evaluation::childAtIndex(int i) const; - template Expression Evaluation::complexToExpression(Preferences::ComplexFormat) const; template Expression Evaluation::complexToExpression(Preferences::ComplexFormat) const; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index d3227d8a9..c376abe59 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -492,8 +492,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU } } } - - // a^(b+c) -> Rational(a^b)*a^c with a and b rational and a != 0 + // a^(b+c+...) -> Rational(a^b)*a^c with a and b rational and a != 0 if (!letPowerAtRoot && childAtIndex(0).type() == ExpressionNode::Type::Rational && !childAtIndex(0).convert().isZero() diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index d76dc88d4..c59fae5b0 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -166,6 +166,10 @@ QUIZ_CASE(poincare_decimal_to_text) { assert_expression_prints_to(Decimal(0.00000099999999999995), "9.9999999999995E-7", ScientificMode, 14); assert_expression_prints_to(Decimal(0.000000999999999999995), "0.000001", DecimalMode); assert_expression_prints_to(Decimal(0.000000999999999901200121020102010201201201021099995), "9.999999999012E-7", DecimalMode, 14); + assert_expression_prints_to(Decimal(9999999999999.6), "9999999999999.6", DecimalMode, 14); + assert_expression_prints_to(Decimal(99999999999999.6), "1E14", DecimalMode, 14); + assert_expression_prints_to(Decimal(999999999999999.6), "1E15", DecimalMode, 14); + assert_expression_prints_to(Decimal(9999999999999999.6), "1E16", DecimalMode, 14); } QUIZ_CASE(poincare_approximation_to_text) { diff --git a/poincare/test/power.cpp b/poincare/test/power.cpp index 16081e443..163797bfd 100644 --- a/poincare/test/power.cpp +++ b/poincare/test/power.cpp @@ -92,4 +92,5 @@ QUIZ_CASE(poincare_power_simplify) { assert_parsed_expression_simplify_to("(1+R(2)+R(3))^5", "296+224*R(2)+184*R(3)+120*R(6)"); assert_parsed_expression_simplify_to("(P+R(2)+R(3)+x)^(-3)", "1/(11*R(2)+9*R(3)+15*x+6*R(6)*x+3*R(2)*x^2+3*R(3)*x^2+x^3+15*P+6*R(6)*P+6*R(2)*x*P+6*R(3)*x*P+3*x^2*P+3*R(2)*P^2+3*R(3)*P^2+3*x*P^2+P^3)"); assert_parsed_expression_simplify_to("1.0066666666667^60", "(10066666666667/10000000000000)^60"); + assert_parsed_expression_simplify_to("2^(6+P+x)", "64*2^(x+P)"); }