Skip to content

Commit

Permalink
knitting_machine: closer simulation of carriage magnets
Browse files Browse the repository at this point in the history
My KH-910 has a belt phase signal edge right in the middle
of its G carriage's right K magnet being detected by the right
position sensor, which means reading the belt phase signal
as soon as the right position sensor is triggered yields a different
result depending on carriage direction.
  • Loading branch information
jonathanperret committed Oct 13, 2024
1 parent 3934ed9 commit a3e242a
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 55 deletions.
50 changes: 28 additions & 22 deletions test/mocks/knitting_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
#include "knitting_machine.h"

const int STEPS_PER_NEEDLE = 4;
const int BELT_PHASE_OFFSET = 9;
const float MAGNET_SENSOR_RANGE = 0.75; // in needle widths

const KnittingMachine::qneedle_t
KnittingMachine::LEFT_POSITION_SENSOR_POSITION =
KnittingMachine::qneedle_t::fromNeedle(0);
KnittingMachine::qneedle_t::fromNeedle(-0.5);
const KnittingMachine::qneedle_t
KnittingMachine::RIGHT_POSITION_SENSOR_POSITION =
KnittingMachine::qneedle_t::fromNeedle(199);
KnittingMachine::qneedle_t::fromNeedle(200);

bool KnittingMachine::getEncoderOutput1() const {
return (m_beltPosition + 1) % STEPS_PER_NEEDLE >= 2;
Expand All @@ -25,7 +27,7 @@ bool KnittingMachine::getBeltPhase() const {
if (!m_hasBeltShift) {
return false;
}
return m_beltPosition >= (beltPeriod() / 2);
return ((m_beltPosition + beltPeriod() + BELT_PHASE_OFFSET) % beltPeriod()) >= (beltPeriod() / 2);
}

void KnittingMachine::moveBeltRight() {
Expand All @@ -36,32 +38,25 @@ void KnittingMachine::moveBeltLeft() {
m_beltPosition = (m_beltPosition + (beltPeriod() - 1)) % beltPeriod();
}

float KnittingMachine::getLeftPositionSensorVoltage() {
int carriageOffset =
(LEFT_POSITION_SENSOR_POSITION - m_carriagePosition).closestNeedle();
for (std::pair<int, bool> magnet : m_carriageMagnets) {
// TODO: simulate the fact that magnets trigger sensors for
// 2-3 needle widths, not just right in front.
if (magnet.first == carriageOffset) {

float KnittingMachine::getPositionSensorVoltage(qneedle_t sensorNeedlePos) const {
float sensorPosition = sensorNeedlePos.asNeedle();
for (std::pair<float, bool> magnet : m_carriageMagnets) {
float magnetPosition = m_carriagePosition.asNeedle() + magnet.first;
if (std::abs(sensorPosition - magnetPosition) <= MAGNET_SENSOR_RANGE) {
return magnet.second ? POSITION_SENSOR_HIGH_VOLTAGE
: POSITION_SENSOR_LOW_VOLTAGE;
}
}
return POSITION_SENSOR_MID_VOLTAGE;
}

float KnittingMachine::getLeftPositionSensorVoltage() {
return getPositionSensorVoltage(LEFT_POSITION_SENSOR_POSITION);
}

float KnittingMachine::getRightPositionSensorVoltage() {
int carriageOffset =
(RIGHT_POSITION_SENSOR_POSITION - m_carriagePosition).closestNeedle();
for (std::pair<int, bool> magnet : m_carriageMagnets) {
// TODO: simulate the fact that magnets trigger sensors for
// 2-3 needle widths, not just right in front.
if (magnet.first == carriageOffset) {
return magnet.second ? POSITION_SENSOR_HIGH_VOLTAGE
: POSITION_SENSOR_LOW_VOLTAGE;
}
}
return POSITION_SENSOR_MID_VOLTAGE;
return getPositionSensorVoltage(RIGHT_POSITION_SENSOR_POSITION);
}

float KnittingMachine::getRightPositionSensorKSignal() {
Expand All @@ -70,10 +65,17 @@ float KnittingMachine::getRightPositionSensorKSignal() {
: POSITION_SENSOR_MID_VOLTAGE;
}

void KnittingMachine::addCarriageMagnet(int offsetFromCenter, bool polarity) {
void KnittingMachine::addCarriageMagnet(float offsetFromCenter, bool polarity) {
m_carriageMagnets.push_back(std::make_pair(offsetFromCenter, polarity));
}

void KnittingMachine::addGCarriageMagnets() {
addCarriageMagnet(12.25, false);
addCarriageMagnet(10.5, true);
addCarriageMagnet(-10.5, true);
addCarriageMagnet(-12.25, false);
}

void KnittingMachine::putCarriageCenterInFrontOfNeedle(int position) {
m_carriagePosition =
qneedle_t::fromNeedle(position); // convert to 1/4 of needles
Expand All @@ -84,6 +86,10 @@ int KnittingMachine::getCarriageCenterNeedle() {
}

bool KnittingMachine::carriageEngagesBelt() const {
// TODO disengage carriage when it moves outside the bed
// TODO simulate carriage belt hooks (currently simulates
// a single hook at carriage center)
// TODO simulate belt slack
int period = STEPS_PER_NEEDLE * m_solenoidCount;
if (m_hasBeltShift) {
period /= 2;
Expand Down
28 changes: 21 additions & 7 deletions test/mocks/knitting_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define KNITTINGMACHINE_H_

#include <cmath>
#include <cstdint>
#include <tuple>
#include <vector>

Expand Down Expand Up @@ -68,7 +69,12 @@ class KnittingMachine {
* (positive = right)
* \param polarity the magnet's polarity — (true = North, like K carriage)
*/
void addCarriageMagnet(int offsetFromCenter, bool polarity);
void addCarriageMagnet(float offsetFromCenter, bool polarity);

/**
* Helper to add all G-carriage magnets (as measured on a KG-89)
*/
void addGCarriageMagnets();

/**
* Set the carriage's position on the bed. This does not move the belt,
Expand Down Expand Up @@ -134,14 +140,17 @@ class KnittingMachine {
--value;
return *this;
}
float asNeedle() const {
return value / 4.0;
}
int closestNeedle() const {
return std::round(value / 4.0);
return std::round(asNeedle());
}
int leftNeedle() const {
return std::floor(value / 4.0);
return std::floor(asNeedle());
}
static qneedle_t fromNeedle(int needle) {
return {needle * 4};
static qneedle_t fromNeedle(float needle) {
return {(int)std::round(needle * 4)};
}
};

Expand All @@ -165,6 +174,11 @@ class KnittingMachine {
*/
bool carriageEngagesBelt() const;

/**
* Get the voltage at a position sensor given its position
*/
float getPositionSensorVoltage(qneedle_t sensorNeedlePosition) const;

/**
* How many solenoids the machine has
*/
Expand All @@ -184,7 +198,7 @@ class KnittingMachine {
* A belt position of 0 represents a belt position where an
* elongated hole is in front of the left position sensor.
*/
uint8_t m_beltPosition = 0;
std::uint8_t m_beltPosition = 0;

/**
* The carriage position in 1/4 needles.
Expand All @@ -197,7 +211,7 @@ class KnittingMachine {
* A list of carriage magnets, defined by their offset from the carriage
* center and their polarity. \see addCarriageMagnet()
*/
std::vector<std::pair<int, bool>> m_carriageMagnets;
std::vector<std::pair<float, bool>> m_carriageMagnets;
};

#endif // KNITTINGMACHINE_H_
71 changes: 45 additions & 26 deletions test/test_knitting_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,7 @@ TEST(KnittingMachine, LCarriageDetectionOnTheLeft) {
TEST(KnittingMachine, GCarriageDetectionOnTheLeft) {
KnittingMachine km;

km.addCarriageMagnet(12, false);
km.addCarriageMagnet(11, true);
km.addCarriageMagnet(-11, true);
km.addCarriageMagnet(-12, false);
km.addGCarriageMagnets();

km.putCarriageCenterInFrontOfNeedle(-12);
ASSERT_THAT(km.getLeftPositionSensorVoltage(), Lt(0.4));
Expand All @@ -113,10 +110,10 @@ TEST(KnittingMachine, GCarriageDetectionOnTheLeft) {
km.putCarriageCenterInFrontOfNeedle(0);
ASSERT_THAT(km.getLeftPositionSensorVoltage(), AllOf(Gt(0.4), Lt(3.4)));

km.putCarriageCenterInFrontOfNeedle(11);
km.putCarriageCenterInFrontOfNeedle(10);
ASSERT_THAT(km.getLeftPositionSensorVoltage(), Gt(3.4));

km.putCarriageCenterInFrontOfNeedle(12);
km.putCarriageCenterInFrontOfNeedle(11);
ASSERT_THAT(km.getLeftPositionSensorVoltage(), Lt(0.4));
}

Expand All @@ -129,7 +126,7 @@ TEST(KnittingMachine, KCarriageDetectionOnTheRight) {
ASSERT_THAT(km.getRightPositionSensorVoltage(), AllOf(Gt(0.4), Lt(3.4)));
ASSERT_THAT(km.getRightPositionSensorKSignal(), AllOf(Gt(1), Lt(4)));

km.putCarriageCenterInFrontOfNeedle(199);
km.putCarriageCenterInFrontOfNeedle(200);

ASSERT_THAT(km.getRightPositionSensorVoltage(), Gt(3.4));
ASSERT_THAT(km.getRightPositionSensorKSignal(), Lt(0.4));
Expand All @@ -140,7 +137,6 @@ TEST(KnittingMachine, KCarriageDetectionOnTheRight) {
ASSERT_THAT(km.getRightPositionSensorKSignal(), AllOf(Gt(1), Lt(4)));
}


TEST(KnittingMachine, MoveCarriageIncrementally) {
KnittingMachine km;

Expand Down Expand Up @@ -179,26 +175,13 @@ TEST(KnittingMachine, BeltPhaseSignal) {

for (int i = 0; i < 8 * 4; i++) {
km.moveBeltLeft();
ASSERT_TRUE(km.getBeltPhase());
}

km.moveBeltLeft();
ASSERT_FALSE(km.getBeltPhase());

for (int i = 0; i < 8 * 4; i++) {
km.moveBeltRight();
ASSERT_TRUE(km.getBeltPhase());
}
ASSERT_TRUE(km.getBeltPhase());

for (int i = 0; i < 8 * 4; i++) {
km.moveBeltRight();
ASSERT_FALSE(km.getBeltPhase());
}

km.moveBeltRight();
ASSERT_TRUE(km.getBeltPhase());

km.putCarriageCenterInFrontOfNeedle(0);
ASSERT_FALSE(km.getBeltPhase());
}

TEST(KnittingMachine, CarriageMovesBeltOnlyWhenInSync) {
Expand All @@ -207,7 +190,7 @@ TEST(KnittingMachine, CarriageMovesBeltOnlyWhenInSync) {
ASSERT_FALSE(km.getBeltPhase());

// Setting the carriage in a position where its belt hooks don't engage belt
// holes.
// holes (because they are offset).
km.putCarriageCenterInFrontOfNeedle(1);

// So when it moves to the left, the belt doesn't move, its phase doesn't
Expand All @@ -218,7 +201,43 @@ TEST(KnittingMachine, CarriageMovesBeltOnlyWhenInSync) {

// Now that the carriage is at a belt position divisible by 8, it's engaged
// and the belt moves.
while (km.moveCarriageCenterTowardsNeedle(-1)) {
ASSERT_TRUE(km.getBeltPhase());
while (km.moveCarriageCenterTowardsNeedle(-4)) {
}
ASSERT_TRUE(km.getBeltPhase());
}

/**
* Helper to print the value of all signals while moving a carriage
* across the bed.
* The output can be extracted and compared with a similar scan
* recorded on an actual machine.
* Sample `ctest` invocation to extract data:
*
* ctest --test-dir test/build -V -R KCarriageScanBed|grep SCAN=|cut -d= -f2
*/
void doBedScan(KnittingMachine &km) {
float centerNeedle = -32;
km.putCarriageCenterInFrontOfNeedle(centerNeedle);

while (km.moveCarriageCenterTowardsNeedle(231)) {
centerNeedle += 1.0 / 4;
printf("SCAN=%g\t%g\t%g\t%g\t%d\n", centerNeedle,
km.getLeftPositionSensorVoltage(),
km.getRightPositionSensorVoltage(),
km.getRightPositionSensorKSignal(), km.getBeltPhase() ? 5 : 0);
}
}

TEST(KnittingMachine, GCarriageScanBed) {
KnittingMachine km;

km.addGCarriageMagnets();
doBedScan(km);
}

TEST(KnittingMachine, KCarriageScanBed) {
KnittingMachine km;

km.addCarriageMagnet(0, true);
doBedScan(km);
}

0 comments on commit a3e242a

Please sign in to comment.