-
Notifications
You must be signed in to change notification settings - Fork 30
/
MultiPointMap.cpp
117 lines (100 loc) · 2.88 KB
/
MultiPointMap.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#ifndef MultiPointMap_h
#define MultiPointMap_h
#include "Arduino.h"
#include <EEPROM.h>
class MultiPointMap {
public:
/**
* Initialize a function that maps values using a multi-linear scale defined by equidistant
* fixed points along the specified range. This is used to implement DACs calibration, and
* it's adapted from Befaco MIDI Thing and Emilie Gillet's CVpal.
*/
void init(uint16_t range = 4000) {
this->step = range / N; // Distance between two consecutive fixed points
this->reset();
}
/**
* Map the given value to another value, interpolating between a pair of fixed points
*/
uint16_t map(uint16_t value) {
uint8_t interval = value / this->step; // Index of the interval in which the given value falls
if (interval > N - 1) interval = N - 1;
int16_t a = interval == 0 ? 0 : this->points[interval - 1]; // Low interpolation point
int16_t b = this->points[interval]; // High interpolation point
return a + ((int32_t)(value - interval * step) * (b - a)) / step; // Linear interpolation
}
/**
* Get the value of a fixed point
*/
uint16_t get(uint8_t i) {
return this->points[i];
}
/**
* Set the value of a fixed point
*/
uint16_t set(uint8_t i, uint16_t value) {
this->points[i] = value;
}
/**
* Returns the distance between two consecutive points of the multi-linear scale
*/
uint16_t getStep() {
return this->step;
}
/**
* Return the number of points in the multi-linear scale
*/
uint8_t size() {
return N;
}
/**
* Load the map from the EEPROM memory starting from the given address.
* If the loaded data is invalid, the points are initialized linearly.
* Return the number of bytes read.
*/
int load(int address) {
int size = 0;
uint16_t checksum = 0, checksumLoaded = 0;
for (uint8_t i = 0; i < N; i++) {
EEPROM.get(address + size, this->points[i]);
checksum += this->points[i];
size += sizeof(this->points[i]);
}
EEPROM.get(address + size, checksumLoaded);
size += sizeof(checksumLoaded);
if (checksum != checksumLoaded) {
this->reset();
}
return size;
}
/**
* Write the map to the EEPROM memory starting from the given address.
* Return the number of bytes written.
*/
int save(int address) {
int size = 0;
uint16_t checksum = 0;
for (uint8_t i = 0; i < N; i++) {
EEPROM.put(address + size, this->points[i]);
checksum += this->points[i];
size += sizeof(this->points[i]);
}
EEPROM.put(address + size, checksum);
size += sizeof(checksum);
return size;
}
/**
* Initialize the points of the multi-linear scale linearly.
* The first point is assumed to be zero.
*/
void reset() {
for (uint8_t i = 0; i < N; i++) {
this->points[i] = (i + 1) * this->getStep();
}
}
private:
static const uint8_t N = 8;
uint16_t step;
uint16_t points[N];
};
#endif