mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
Merge remote-tracking branch 'github/master' into Tree
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 (x<y ? x : y); }
|
||||
|
||||
static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
|
||||
if (tokenKind == MP_TOKEN_STRING) {
|
||||
return StringColor;
|
||||
@@ -36,7 +38,7 @@ static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
|
||||
return KDColorBlack;
|
||||
}
|
||||
|
||||
static inline int TokenLength(mp_lexer_t * lex) {
|
||||
static inline size_t TokenLength(mp_lexer_t * lex) {
|
||||
if (lex->tok_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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
#include <escher.h>
|
||||
#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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef APPS_LED_TIMER_H
|
||||
#define APPS_LED_TIMER_H
|
||||
|
||||
#include <escher.h>
|
||||
|
||||
class LedTimer : public Timer {
|
||||
public:
|
||||
LedTimer();
|
||||
private:
|
||||
bool fire() override;
|
||||
bool m_on;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -9,6 +9,7 @@ using namespace Shared;
|
||||
|
||||
static inline float min(float x, float y) { return (x<y ? x : y); }
|
||||
static inline float max(float x, float y) { return (x>y ? 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<AppsContainer *>(static_cast<const AppsContainer *>(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<AppsContainer *>(static_cast<const AppsContainer *>(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<char *>(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<char *>(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<AppsContainer *>(static_cast<const AppsContainer *>(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<AppsContainer *>(static_cast<const AppsContainer *>(app()->container()))->globalContext();
|
||||
Poincare::Context * globContext = globalContext();
|
||||
|
||||
if (*m_selectedDotIndex == -1) {
|
||||
// The current cursor is on a regression
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
static inline int min(int x, int y) { return (x<y ? x : y); }
|
||||
static inline int max(int x, int y) { return (x>y ? x : y); }
|
||||
|
||||
namespace Shared {
|
||||
|
||||
StoreController::ContentView::ContentView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate) :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
210
apps/statistics/test/store.cpp
Normal file
210
apps/statistics/test/store.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include <quiz.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <escher/responder.h>
|
||||
#include <escher/message_text_view.h>
|
||||
#include <escher/invocation.h>
|
||||
#include <escher/palette.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
namespace Ion {
|
||||
namespace LED {
|
||||
|
||||
KDColor getColor();
|
||||
void setColor(KDColor c);
|
||||
void setBlinking(uint16_t periodInMilliseconds, float dutyCycle);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "command.h"
|
||||
#include <ion.h>
|
||||
#include <ion/src/device/display.h>
|
||||
#include <poincare.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
@@ -46,6 +47,8 @@ void Display(const char * input) {
|
||||
}
|
||||
}
|
||||
|
||||
int numberOfInvalidPixels = 0;
|
||||
|
||||
for (int i=0; i<Ion::Display::Width/stampWidth; i++) {
|
||||
for (int j=0; j<Ion::Display::Height/stampHeight; j++) {
|
||||
for (int i=0;i<stampWidth*stampHeight; i++) {
|
||||
@@ -54,14 +57,15 @@ void Display(const char * input) {
|
||||
Ion::Display::pullRect(KDRect(i*stampWidth, j*stampHeight, stampWidth, stampHeight), stamp);
|
||||
for (int i=0;i<stampWidth*stampHeight; i++) {
|
||||
if (stamp[i] != c) {
|
||||
reply(sKO);
|
||||
return;
|
||||
numberOfInvalidPixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply(sOK);
|
||||
char response[16] = {'D', 'E', 'L', 'T', 'A', '='};
|
||||
Poincare::Integer(numberOfInvalidPixels).writeTextInBuffer(response+6, sizeof(response)-6);
|
||||
reply(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
objs += $(addprefix ion/src/device/boot/, isr.o rt0.o)
|
||||
LDFLAGS += -T ion/src/device/boot/flash.ld
|
||||
LDSCRIPT = ion/src/device/boot/flash.ld
|
||||
|
||||
@@ -169,7 +169,7 @@ void initPeripherals() {
|
||||
SWD::Device::init();
|
||||
}
|
||||
|
||||
void shutdownPeripherals() {
|
||||
void shutdownPeripherals(bool keepLEDAwake) {
|
||||
SWD::Device::shutdown();
|
||||
Console::Device::shutdown();
|
||||
#if USE_SD_CARD
|
||||
@@ -177,7 +177,9 @@ void shutdownPeripherals() {
|
||||
#endif
|
||||
USB::Device::shutdown();
|
||||
Battery::Device::shutdown();
|
||||
LED::Device::shutdown();
|
||||
if (!keepLEDAwake) {
|
||||
LED::Device::shutdown();
|
||||
}
|
||||
Keyboard::Device::shutdown();
|
||||
Backlight::Device::shutdown();
|
||||
Display::Device::shutdown();
|
||||
@@ -265,7 +267,7 @@ void initClocks() {
|
||||
RCC.AHB3ENR()->setFSMCEN(true);
|
||||
|
||||
// APB1 bus
|
||||
// We're using TIM3
|
||||
// We're using TIM3 for the LEDs
|
||||
RCC.APB1ENR()->setTIM3EN(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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,14 +1,40 @@
|
||||
#include <ion/led.h>
|
||||
#include <ion/display.h>
|
||||
#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<Register16>::CCMR::OCM::PWM1);
|
||||
TIM3.CCMR()->setOC4M(TIM<Register16>::CCMR::OCM::PWM1);
|
||||
TIM3.CCMR()->setOC3M(TIM<Register16>::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<Register16>::CCMR::OCM::PWM1 : TIM<Register16>::CCMR::OCM::ForceInactive);
|
||||
TIM3.CCMR()->setOC4M(sLedColor.green() > 0 ? TIM<Register16>::CCMR::OCM::PWM1 : TIM<Register16>::CCMR::OCM::ForceInactive);
|
||||
TIM3.CCMR()->setOC3M(sLedColor.blue() > 0 ? TIM<Register16>::CCMR::OCM::PWM1 : TIM<Register16>::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<Register16>::CCMR::OCM::ForceInactive);
|
||||
TIM3.CCMR()->setOC4M(TIM<Register16>::CCMR::OCM::ForceInactive);
|
||||
TIM3.CCMR()->setOC3M(TIM<Register16>::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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "register.h"
|
||||
|
||||
template <typename RegisterWidth>
|
||||
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<Register16> TIM3(3);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
#include <ion/led.h>
|
||||
|
||||
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) {}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <ion/led.h>
|
||||
|
||||
void Ion::LED::setColor(KDColor c) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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 \
|
||||
)
|
||||
|
||||
@@ -46,7 +46,6 @@ public:
|
||||
* are enable which can lead to weird code:
|
||||
* ie, you can write: 'Complex<float> a(2); MatrixComplex<float> b(a);'
|
||||
* */
|
||||
|
||||
static_assert(sizeof(U) == sizeof(Evaluation), "Size mismatch");
|
||||
return *reinterpret_cast<U *>(const_cast<Evaluation<T> *>(this));
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -16,7 +16,6 @@ Expression Evaluation<T>::complexToExpression(Preferences::ComplexFormat complex
|
||||
|
||||
template Evaluation<float> Evaluation<float>::childAtIndex(int i) const;
|
||||
template Evaluation<double> Evaluation<double>::childAtIndex(int i) const;
|
||||
|
||||
template Expression Evaluation<float>::complexToExpression(Preferences::ComplexFormat) const;
|
||||
template Expression Evaluation<double>::complexToExpression(Preferences::ComplexFormat) const;
|
||||
|
||||
|
||||
@@ -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<Rational>().isZero()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user