incyclist-devices 2.2.9 → 2.2.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import ICyclingMode, { CyclingModeProperyType, UpdateRequest } from "./types";
|
|
1
|
+
import ICyclingMode, { CyclingModeProperyType, IVirtualShifting, UpdateRequest } from "./types";
|
|
2
2
|
import PowerBasedCyclingModeBase from "./power-base";
|
|
3
3
|
import { IAdapter } from "../types";
|
|
4
|
-
export
|
|
4
|
+
export type GearConfigEntry = {
|
|
5
|
+
chainConfig: number[];
|
|
6
|
+
cassetteConfig: number[];
|
|
7
|
+
wheelCirc: number;
|
|
8
|
+
numGears: number;
|
|
9
|
+
};
|
|
10
|
+
export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase implements ICyclingMode, IVirtualShifting {
|
|
5
11
|
protected static config: {
|
|
6
12
|
name: string;
|
|
7
13
|
isSIM: boolean;
|
|
@@ -26,10 +32,18 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
|
|
|
26
32
|
options?: undefined;
|
|
27
33
|
})[];
|
|
28
34
|
};
|
|
35
|
+
protected gear: any;
|
|
29
36
|
constructor(adapter: IAdapter, props?: any);
|
|
30
37
|
getBikeInitRequest(): UpdateRequest;
|
|
31
38
|
checkForResetOrEmpty(request: UpdateRequest): UpdateRequest | undefined;
|
|
32
39
|
protected checkSlope(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
33
40
|
protected checkEmptyRequest(newRequest: UpdateRequest): void;
|
|
34
41
|
sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
|
|
42
|
+
initGears(): Promise<number>;
|
|
43
|
+
gearUp(numGears: number): Promise<number>;
|
|
44
|
+
protected switchGear: (gear: any, prevGear: any) => number;
|
|
45
|
+
protected findBestGear(): any;
|
|
46
|
+
getCurrentGearConfig(): GearConfigEntry;
|
|
47
|
+
protected getDefaultCadence(): number;
|
|
48
|
+
protected getDefaultPower(): number;
|
|
35
49
|
}
|
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
13
|
};
|
|
5
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
15
|
const types_1 = require("./types");
|
|
7
16
|
const power_base_1 = __importDefault(require("./power-base"));
|
|
17
|
+
const calculations_1 = __importDefault(require("../utils/calculations"));
|
|
18
|
+
const NUM_GEARS = 30;
|
|
19
|
+
const GearConfig = {
|
|
20
|
+
race: { chainConfig: [34, 50], cassetteConfig: [11, 36], wheelCirc: 2125, numGears: NUM_GEARS },
|
|
21
|
+
mountain: { chainConfig: [26, 36], cassetteConfig: [10, 44], wheelCirc: 2344, numGears: NUM_GEARS },
|
|
22
|
+
triathlon: { chainConfig: [36, 52], cassetteConfig: [11, 30], wheelCirc: 2125, numGears: NUM_GEARS },
|
|
23
|
+
};
|
|
8
24
|
class SmartTrainerCyclingMode extends power_base_1.default {
|
|
9
25
|
constructor(adapter, props) {
|
|
10
26
|
super(adapter, props);
|
|
27
|
+
this.switchGear = (gear, prevGear) => {
|
|
28
|
+
var _a;
|
|
29
|
+
const { chainConfig, cassetteConfig, wheelCirc, numGears } = this.getCurrentGearConfig();
|
|
30
|
+
const { pedalRpm, speed, power, slope } = (_a = this.getData()) !== null && _a !== void 0 ? _a : {};
|
|
31
|
+
const m = this.getWeight();
|
|
32
|
+
let cadence = pedalRpm !== null && pedalRpm !== void 0 ? pedalRpm : this.getDefaultCadence();
|
|
33
|
+
let currentPower = power !== null && power !== void 0 ? power : this.getDefaultPower();
|
|
34
|
+
const prevGearSpeed = calculations_1.default.calculateSpeedBike(prevGear, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
35
|
+
const targetGearSpeed = calculations_1.default.calculateSpeedBike(gear, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
36
|
+
const targetGearPower = calculations_1.default.calculatePower(m, targetGearSpeed / 3.6, 0);
|
|
37
|
+
const prevGearPower = calculations_1.default.calculatePower(m, prevGearSpeed / 3.6, 0);
|
|
38
|
+
const targetPower = currentPower + (targetGearPower - prevGearPower);
|
|
39
|
+
const slopeTarget = calculations_1.default.calculateSlope(m, targetPower, speed / 3.6);
|
|
40
|
+
console.log('Gear', gear, 'power', targetPower, 'speed', speed.toFixed(1), 'slope', slope.toFixed(1), 'virtual slope', slopeTarget.toFixed(1));
|
|
41
|
+
return slopeTarget;
|
|
42
|
+
};
|
|
11
43
|
this.initLogger('SmartTrainerMode');
|
|
12
44
|
}
|
|
13
45
|
getBikeInitRequest() {
|
|
@@ -28,11 +60,16 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
28
60
|
newRequest.slope = parseFloat(request.slope.toFixed(1));
|
|
29
61
|
this.data.slope = newRequest.slope;
|
|
30
62
|
try {
|
|
63
|
+
if (this.gear !== undefined) {
|
|
64
|
+
const slopeTarget = this.switchGear(this.gear, this.gear);
|
|
65
|
+
newRequest.slope = slopeTarget;
|
|
66
|
+
}
|
|
31
67
|
const slopeAdj = this.getSetting('slopeAdj');
|
|
32
68
|
if (slopeAdj !== undefined)
|
|
33
69
|
newRequest.slope = newRequest.slope * slopeAdj / 100;
|
|
34
70
|
}
|
|
35
|
-
catch (
|
|
71
|
+
catch (err) {
|
|
72
|
+
this.logger.logEvent({ message: "error", fn: '', error: err.message, request, prev: this.prevRequest, data: this.getData() });
|
|
36
73
|
}
|
|
37
74
|
}
|
|
38
75
|
}
|
|
@@ -61,6 +98,66 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
61
98
|
}
|
|
62
99
|
return newRequest;
|
|
63
100
|
}
|
|
101
|
+
initGears() {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
this.gear = this.findBestGear();
|
|
104
|
+
return this.gear;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
gearUp(numGears) {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
let target = this.gear + numGears;
|
|
110
|
+
if (target > NUM_GEARS)
|
|
111
|
+
target = NUM_GEARS;
|
|
112
|
+
if (target < 1)
|
|
113
|
+
target = 1;
|
|
114
|
+
this.gear = target;
|
|
115
|
+
return this.gear;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
findBestGear() {
|
|
119
|
+
var _a;
|
|
120
|
+
const { chainConfig, cassetteConfig, wheelCirc, numGears } = this.getCurrentGearConfig();
|
|
121
|
+
const { pedalRpm, speed, power, slope } = (_a = this.getData()) !== null && _a !== void 0 ? _a : {};
|
|
122
|
+
const m = this.getWeight();
|
|
123
|
+
let cadence = pedalRpm !== null && pedalRpm !== void 0 ? pedalRpm : this.getDefaultCadence();
|
|
124
|
+
let currentPower = power !== null && power !== void 0 ? power : this.getDefaultPower();
|
|
125
|
+
let minDiff, gearInitial;
|
|
126
|
+
if (speed === 0) {
|
|
127
|
+
const speedInitial = calculations_1.default.calculateSpeed(m, currentPower, slope);
|
|
128
|
+
for (let i = 1; i <= numGears; i++) {
|
|
129
|
+
const speed = calculations_1.default.calculateSpeedBike(i, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
130
|
+
const diff = Math.abs(speed - speedInitial);
|
|
131
|
+
if (!minDiff || diff < minDiff) {
|
|
132
|
+
minDiff = diff;
|
|
133
|
+
gearInitial = i;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
for (let i = 1; i <= numGears; i++) {
|
|
139
|
+
const speed = calculations_1.default.calculateSpeedBike(i, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
140
|
+
const targetPower = calculations_1.default.calculatePower(m, speed / 3.6, slope);
|
|
141
|
+
const diff = Math.abs(currentPower - targetPower);
|
|
142
|
+
if (!minDiff || diff < minDiff) {
|
|
143
|
+
minDiff = diff;
|
|
144
|
+
gearInitial = i;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return gearInitial;
|
|
149
|
+
}
|
|
150
|
+
getCurrentGearConfig() {
|
|
151
|
+
var _a;
|
|
152
|
+
const type = (_a = this.getSetting('bikeType')) !== null && _a !== void 0 ? _a : 'Race';
|
|
153
|
+
return GearConfig[type.toLowerCase()];
|
|
154
|
+
}
|
|
155
|
+
getDefaultCadence() {
|
|
156
|
+
return 90;
|
|
157
|
+
}
|
|
158
|
+
getDefaultPower() {
|
|
159
|
+
return 120;
|
|
160
|
+
}
|
|
64
161
|
}
|
|
65
162
|
SmartTrainerCyclingMode.config = {
|
|
66
163
|
name: "Smart Trainer",
|
package/lib/modes/types.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ export default interface ICyclingMode {
|
|
|
47
47
|
setModeProperty(name: string, value: any): void;
|
|
48
48
|
getModeProperty(name: string): any;
|
|
49
49
|
}
|
|
50
|
+
export interface IVirtualShifting {
|
|
51
|
+
initGears(): Promise<number>;
|
|
52
|
+
gearUp(numGears: number): Promise<number>;
|
|
53
|
+
}
|
|
50
54
|
export type CyclingModeConfig = {
|
|
51
55
|
isERG?: boolean;
|
|
52
56
|
isSIM?: boolean;
|
|
@@ -69,7 +73,7 @@ export declare class CyclingMode implements ICyclingMode {
|
|
|
69
73
|
getSetting(_name: string): void;
|
|
70
74
|
getSettings(): Settings;
|
|
71
75
|
setModeProperty(_name: string, _value: any): void;
|
|
72
|
-
getModeProperty(_name: string):
|
|
76
|
+
getModeProperty(_name: string): any;
|
|
73
77
|
getConfig(): CyclingModeConfig;
|
|
74
78
|
isERG(): boolean;
|
|
75
79
|
isSIM(): boolean;
|
|
@@ -3,6 +3,7 @@ export declare class IllegalArgumentException extends Error {
|
|
|
3
3
|
}
|
|
4
4
|
export default class C {
|
|
5
5
|
static calculateSpeed(m: number, power: number, slope: number, props?: any): number;
|
|
6
|
+
static calculateSlope(m: number, P: number, v: number, props?: any): number;
|
|
6
7
|
static calculatePower(m: number, v: number, slope: number, props?: any): number;
|
|
7
8
|
static calculateSpeedDaum(gear: number, rpm: number, bikeType?: string | number): number;
|
|
8
9
|
static calculateSpeedBike(gear: number, rpm: number, chain: number[], cassette: number[], props?: {
|
|
@@ -19,13 +19,14 @@ class IllegalArgumentException extends Error {
|
|
|
19
19
|
exports.IllegalArgumentException = IllegalArgumentException;
|
|
20
20
|
class C {
|
|
21
21
|
static calculateSpeed(m, power, slope, props = {}) {
|
|
22
|
+
var _a, _b, _c, _d;
|
|
22
23
|
if (m === undefined || m === null || m < 0)
|
|
23
24
|
throw new IllegalArgumentException("m must be a positive number");
|
|
24
25
|
if (power === undefined || power === null || power < 0)
|
|
25
26
|
throw new IllegalArgumentException("power must be a positive number");
|
|
26
|
-
const _rho = props.rho
|
|
27
|
-
const _cRR = props.cRR
|
|
28
|
-
const _cwA = props.cwA
|
|
27
|
+
const _rho = (_a = props.rho) !== null && _a !== void 0 ? _a : rho;
|
|
28
|
+
const _cRR = (_b = props.cRR) !== null && _b !== void 0 ? _b : cRR;
|
|
29
|
+
const _cwA = (_c = props.cwA) !== null && _c !== void 0 ? _c : ((_d = cwABike[props.bikeType || 'race']) !== null && _d !== void 0 ? _d : cwABike.race);
|
|
29
30
|
let sl = Math.atan(slope / 100);
|
|
30
31
|
let c1 = 0.5 * _rho * _cwA;
|
|
31
32
|
let c2 = (sl + _cRR) * m * g;
|
|
@@ -58,14 +59,29 @@ class C {
|
|
|
58
59
|
}
|
|
59
60
|
return 0;
|
|
60
61
|
}
|
|
62
|
+
static calculateSlope(m, P, v, props = {}) {
|
|
63
|
+
var _a, _b, _c;
|
|
64
|
+
if (m === undefined || m === null || m < 0)
|
|
65
|
+
throw new IllegalArgumentException("m must be a positive number");
|
|
66
|
+
if (P === undefined || P === null || P < 0)
|
|
67
|
+
throw new IllegalArgumentException("power must be a positive number");
|
|
68
|
+
if (v === undefined || v === null || v === 0)
|
|
69
|
+
throw new IllegalArgumentException("v must be a positive number");
|
|
70
|
+
const _rho = (_a = props.rho) !== null && _a !== void 0 ? _a : rho;
|
|
71
|
+
const _cRR = (_b = props.cRR) !== null && _b !== void 0 ? _b : cRR;
|
|
72
|
+
const _cwA = (_c = props.cwA) !== null && _c !== void 0 ? _c : (cwABike[props.bikeType || 'race'] || cwABike.race);
|
|
73
|
+
const sl = (P - (0.5 * _rho * _cwA) * Math.pow(v, 3.0)) / (m * g * v) - _cRR;
|
|
74
|
+
return Math.tan(Math.asin(sl)) * 100;
|
|
75
|
+
}
|
|
61
76
|
static calculatePower(m, v, slope, props = {}) {
|
|
77
|
+
var _a, _b, _c;
|
|
62
78
|
if (m === undefined || m === null || m < 0)
|
|
63
79
|
throw new IllegalArgumentException("m must be a positive number");
|
|
64
80
|
if (v === undefined || v === null || v < 0)
|
|
65
81
|
throw new IllegalArgumentException("v must be a positive number");
|
|
66
|
-
let _rho = props.rho
|
|
67
|
-
let _cRR = props.cRR
|
|
68
|
-
let _cwA = props.cwA
|
|
82
|
+
let _rho = (_a = props.rho) !== null && _a !== void 0 ? _a : rho;
|
|
83
|
+
let _cRR = (_b = props.cRR) !== null && _b !== void 0 ? _b : cRR;
|
|
84
|
+
let _cwA = (_c = props.cwA) !== null && _c !== void 0 ? _c : cwABike[props.bikeType || 'race'];
|
|
69
85
|
let sl = Math.sin(Math.atan(slope / 100));
|
|
70
86
|
let P = (0.5 * _rho * _cwA) * Math.pow(v, 3.0) + (sl + _cRR) * m * g * v;
|
|
71
87
|
return P;
|