1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4 
5    This file is part of the SANE package.
6 
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef BACKEND_GENESYS_MOTOR_H
22 #define BACKEND_GENESYS_MOTOR_H
23 
24 #include <algorithm>
25 #include <cstdint>
26 #include <vector>
27 #include "enums.h"
28 #include "sensor.h"
29 #include "value_filter.h"
30 
31 namespace genesys {
32 
33 /*  Describes a motor acceleration curve.
34 
35     Definitions:
36         v - speed in steps per pixeltime
37         w - speed in pixel times per step. w = 1 / v
38         a - acceleration in steps per pixeltime squared
39         s - distance travelled in steps
40         t - time in pixeltime
41 
42     The physical mode defines the curve in physical quantities. We assume that the scanner head
43     accelerates from standstill to the target speed uniformly. Then:
44 
45     v(t) = v(0) + a * t                                                                         (2)
46 
47     Where `a` is acceleration, `t` is time. Also we can calculate the travelled distance `s`:
48 
49     s(t) = v(0) * t + a * t^2 / 2                                                               (3)
50 
51     The actual motor slope is defined as the duration of each motor step. That means we need to
52     define speed in terms of travelled distance.
53 
54     Solving (3) for `t` gives:
55 
56            sqrt( v(0)^2 + 2 * a * s ) - v(0)
57     t(s) = ---------------------------------                                                    (4)
58                           a
59 
60     Combining (4) and (2) will yield:
61 
62     v(s) = sqrt( v(0)^2 + 2 * a * s )                                                           (5)
63 
64     The data in the slope struct MotorSlope corresponds to the above in the following way:
65 
66     maximum_start_speed is `w(0) = 1/v(0)`
67 
68     maximum_speed is defines maximum speed which should not be exceeded
69 
70     minimum_steps is not used
71 
72     g is `a`
73 
74     Given the start and target speeds on a known motor curve, `a` can be computed as follows:
75 
76         v(t1)^2 - v(t0)^2
77     a = -----------------                                                                       (6)
78                2 * s
79 
80     Here `v(t0)` and `v(t1)` are the start and target speeds and `s` is the number of step required
81     to reach the target speeds.
82 */
83 struct MotorSlope
84 {
85     // initial speed in pixeltime per step
86     unsigned initial_speed_w = 0;
87 
88     // max speed in pixeltime per step
89     unsigned max_speed_w = 0;
90 
91     // maximum number of steps in the table
92     unsigned max_step_count;
93 
94     // acceleration in steps per pixeltime squared.
95     float acceleration = 0;
96 
97     unsigned get_table_step_shifted(unsigned step, StepType step_type) const;
98 
99     static MotorSlope create_from_steps(unsigned initial_w, unsigned max_w,
100                                         unsigned steps);
101 };
102 
103 struct MotorSlopeTable
104 {
105     std::vector<std::uint16_t> table;
106 
107     void slice_steps(unsigned count, unsigned step_multiplier);
108 
109     // expands the table by the given number of steps
110     void expand_table(unsigned count, unsigned step_multiplier);
111 
pixeltime_sumgenesys::MotorSlopeTable112     std::uint64_t pixeltime_sum() const { return pixeltime_sum_; }
113 
114     void generate_pixeltime_sum();
115 private:
116     std::uint64_t pixeltime_sum_ = 0;
117 };
118 
119 unsigned get_slope_table_max_size(AsicType asic_type);
120 
121 MotorSlopeTable create_slope_table_for_speed(const MotorSlope& slope, unsigned target_speed_w,
122                                              StepType step_type, unsigned steps_alignment,
123                                              unsigned min_size, unsigned max_size);
124 
125 std::ostream& operator<<(std::ostream& out, const MotorSlope& slope);
126 
127 struct MotorProfile
128 {
129     MotorProfile() = default;
MotorProfilegenesys::MotorProfile130     MotorProfile(const MotorSlope& a_slope, StepType a_step_type, unsigned a_max_exposure) :
131         slope{a_slope}, step_type{a_step_type}, max_exposure{a_max_exposure}
132     {}
133 
134     MotorSlope slope;
135     StepType step_type = StepType::FULL;
136     int motor_vref = -1;
137 
138     // the resolutions this profile is good for
139     ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY;
140     // the scan method this profile is good for. If the list is empty, good for any method.
141     ValueFilterAny<ScanMethod> scan_methods = VALUE_FILTER_ANY;
142 
143     unsigned max_exposure = 0; // 0 - any exposure
144 };
145 
146 std::ostream& operator<<(std::ostream& out, const MotorProfile& profile);
147 
148 struct Genesys_Motor
149 {
150     Genesys_Motor() = default;
151 
152     // id of the motor description
153     MotorId id = MotorId::UNKNOWN;
154     // motor base steps. Unit: 1/inch
155     int base_ydpi = 0;
156     // slopes to derive individual slopes from
157     std::vector<MotorProfile> profiles;
158     // slopes to derive individual slopes from for fast moving
159     std::vector<MotorProfile> fast_profiles;
160 
get_slope_with_step_typegenesys::Genesys_Motor161     MotorSlope& get_slope_with_step_type(StepType step_type)
162     {
163         for (auto& p : profiles) {
164             if (p.step_type == step_type)
165                 return p.slope;
166         }
167         throw SaneException("No motor profile with step type");
168     }
169 
get_slope_with_step_typegenesys::Genesys_Motor170     const MotorSlope& get_slope_with_step_type(StepType step_type) const
171     {
172         for (const auto& p : profiles) {
173             if (p.step_type == step_type)
174                 return p.slope;
175         }
176         throw SaneException("No motor profile with step type");
177     }
178 
max_step_typegenesys::Genesys_Motor179     StepType max_step_type() const
180     {
181         if (profiles.empty()) {
182             throw std::runtime_error("Profiles table is empty");
183         }
184         StepType step_type = StepType::FULL;
185         for (const auto& p : profiles) {
186             step_type = static_cast<StepType>(
187                     std::max(static_cast<unsigned>(step_type),
188                              static_cast<unsigned>(p.step_type)));
189         }
190         return step_type;
191     }
192 };
193 
194 std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor);
195 
196 } // namespace genesys
197 
198 #endif // BACKEND_GENESYS_MOTOR_H
199