Merge "Pass voltage through to the trajectory python wrapper."
diff --git a/aos/build/queues/output/q_file.rb b/aos/build/queues/output/q_file.rb
index 1e39247..3bbdaed 100644
--- a/aos/build/queues/output/q_file.rb
+++ b/aos/build/queues/output/q_file.rb
@@ -118,8 +118,7 @@
type_class.add_member(:public,member_func)
#cpp_tree.cc_file.add_funct(member_func)
member_func.args << "const char *name";
- member_func.args << "uint32_t hash";
- member_func.add_cons("::aos::QueueGroup","name", "hash")
+ member_func.add_cons("::aos::QueueGroup","name")
@queues.each do |queue|
member_func.args << "const char *#{queue.name}_name";
member_func.add_cons(queue.name,"#{queue.name}_name")
@@ -169,7 +168,6 @@
cons_call = CPP::FuncCall.new("new #{type_name}")
cons_call.args.push(@loc.queue_name(@name).inspect)
- cons_call.args.push(@type.hash_with_name(@loc.queue_name(@name).inspect))
@type.queues.collect do |queue|
cons_call.args.push(@loc.queue_name(@name + "." + queue.name).inspect)
end
diff --git a/aos/containers/BUILD b/aos/containers/BUILD
new file mode 100644
index 0000000..c139195
--- /dev/null
+++ b/aos/containers/BUILD
@@ -0,0 +1,55 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "ring_buffer",
+ hdrs = [
+ "ring_buffer.h",
+ ],
+)
+
+cc_test(
+ name = "ring_buffer_test",
+ srcs = [
+ "ring_buffer_test.cc",
+ ],
+ deps = [
+ ":ring_buffer",
+ "//aos/testing:googletest",
+ ],
+)
+
+cc_library(
+ name = "priority_queue",
+ hdrs = [
+ "priority_queue.h",
+ ],
+)
+
+cc_test(
+ name = "priority_queue_test",
+ srcs = [
+ "priority_queue_test.cc",
+ ],
+ deps = [
+ ":priority_queue",
+ "//aos/testing:googletest",
+ ],
+)
+
+cc_library(
+ name = "sized_array",
+ hdrs = [
+ "sized_array.h",
+ ],
+)
+
+cc_test(
+ name = "sized_array_test",
+ srcs = [
+ "sized_array_test.cc",
+ ],
+ deps = [
+ ":sized_array",
+ "//aos/testing:googletest",
+ ],
+)
diff --git a/aos/containers/priority_queue.h b/aos/containers/priority_queue.h
new file mode 100644
index 0000000..0bc94e7
--- /dev/null
+++ b/aos/containers/priority_queue.h
@@ -0,0 +1,148 @@
+#ifndef AOS_CONTAINERS_PRIORITY_QUEUE_H_
+#define AOS_CONTAINERS_PRIORITY_QUEUE_H_
+
+#include <array>
+#include <iterator>
+
+namespace aos {
+
+// Creates a priority queue which will never exceed a particular size.
+// Data: The type of the data to store.
+// buffer_size: The max number of Data to store.
+// Compare: A comparison function. If std::less were used here, then the
+// smallest element would be discarded first, see
+// https://en.cppreference.com/w/cpp/named_req/Compare
+// The lowest priority elements will be discarded first to maintain buffer_size.
+// Note that as a "queue" this is a bit incomplete because there is no mechanism
+// to pop from the queue.
+template <typename Data, size_t buffer_size, typename Compare>
+class PriorityQueue {
+ public:
+ class iterator {
+ public:
+ explicit iterator(PriorityQueue *queue, size_t idx)
+ : queue_(queue), idx_(idx) {}
+ iterator &operator++() {
+ idx_ = queue_->next_idx(idx_);
+ return *this;
+ }
+ iterator operator++(int) {
+ iterator retval = *this;
+ ++(*this);
+ return retval;
+ }
+ iterator &operator--() {
+ idx_ = queue_->prev_idx(idx_);
+ return *this;
+ }
+ iterator operator--(int) {
+ iterator retval = *this;
+ --(*this);
+ return retval;
+ }
+ bool operator==(iterator other) const {
+ return queue_ == other.queue_ && idx_ == other.idx_;
+ }
+ bool operator!=(iterator other) const { return !(*this == other); }
+ Data &operator*() { return queue_->get(idx_); }
+ Data *operator->() { return &queue_->get(idx_); }
+
+ private:
+ PriorityQueue *queue_;
+ size_t idx_;
+ };
+
+ constexpr PriorityQueue() {}
+
+ // Inserts data into the queue and returns an iterator for the inserted
+ // element. If the queue was already full, then the lowest priority element
+ // will be discarded. If data is lower priority than all the current elements
+ // and the queue is full, then data is ignored and end() is returned.
+ // PushFromBottom starts the search from the bottom of the queue.
+ // TODO(james): If performance becomes an issue, improve search.
+ iterator PushFromBottom(const Data &data) {
+ size_t lower_idx = buffer_size;
+ size_t upper_idx = bottom_;
+ // Find our spot in the queue:
+ while (upper_idx != buffer_size && !cmp_(data, list_[upper_idx].data)) {
+ lower_idx = upper_idx;
+ upper_idx = list_[upper_idx].upper_idx;
+ if (upper_idx == buffer_size) {
+ break;
+ }
+ }
+ return InsertInList(data, lower_idx, upper_idx);
+ }
+
+ size_t size() const { return size_; }
+ bool empty() const { return size_ == 0; }
+ bool full() const { return size_ == buffer_size; }
+
+ Data &top() { return list_[top_].data; }
+ Data &get(size_t idx) { return list_[idx].data; }
+ iterator begin() { return iterator(this, bottom_); }
+ iterator end() { return iterator(this, buffer_size); }
+
+ // Gets the index of the next (higher valued) element in the queue.
+ size_t next_idx(size_t idx) const { return list_[idx].upper_idx; }
+ // Gets the index of the previous (lower valued) element in the queue.
+ size_t prev_idx(size_t idx) const { return list_[idx].lower_idx; }
+ private:
+ struct Datum {
+ // A list element with data and the indices of the next highest/lowest
+ // priority elements.
+ Data data;
+ // Values of buffer_size indicate that we are at the beginning or end of
+ // the queue.
+ size_t lower_idx = buffer_size;
+ size_t upper_idx = buffer_size;
+ };
+
+ // Insert an element above lower_idx and below upper_idx.
+ iterator InsertInList(const Data &data, size_t lower_idx, size_t upper_idx) {
+ // For inserting new elements, when we are initially filling the queue we
+ // will increment upwards in the array; once full, we just evict the
+ // lowest priority element.
+ size_t insertion_idx = size();
+ if (full()) {
+ if (upper_idx == bottom_) {
+ // this item is lower priority than everyone else, don't insert it.
+ return end();
+ }
+ // Eject the lowest priority element.
+ insertion_idx = bottom_;
+ if (lower_idx == insertion_idx) {
+ lower_idx = buffer_size;
+ }
+ --size_;
+ bottom_ = list_[bottom_].upper_idx;
+ list_[bottom_].lower_idx = buffer_size;
+ }
+ if (upper_idx != buffer_size) {
+ list_[upper_idx].lower_idx = insertion_idx;
+ }
+ if (lower_idx != buffer_size) {
+ list_[lower_idx].upper_idx = insertion_idx;
+ }
+ if (bottom_ == upper_idx) {
+ bottom_ = insertion_idx;
+ }
+ if (top_ == lower_idx) {
+ top_ = insertion_idx;
+ }
+ list_[insertion_idx].data = data;
+ list_[insertion_idx].upper_idx = upper_idx;
+ list_[insertion_idx].lower_idx = lower_idx;
+ ++size_;
+ return iterator(this, insertion_idx);
+ }
+ ::std::array<Datum, buffer_size> list_;
+ // Index of the bottom and top of the queue.
+ size_t bottom_ = buffer_size, top_ = buffer_size;
+ // Number of elements currently in the queue.
+ size_t size_ = 0;
+ Compare cmp_;
+};
+} // namespace aos
+
+#endif // AOS_CONTAINERS_PRIORITY_QUEUE_H_
diff --git a/aos/containers/priority_queue_test.cc b/aos/containers/priority_queue_test.cc
new file mode 100644
index 0000000..2c45990
--- /dev/null
+++ b/aos/containers/priority_queue_test.cc
@@ -0,0 +1,145 @@
+#include "aos/containers/priority_queue.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+// Effectively copies the implementation of ::std::less just to demonstrate how
+// things work.
+class ExampleCompare {
+ public:
+ constexpr bool operator()(const int &lhs, const int &rhs) const {
+ return lhs < rhs;
+ }
+};
+
+class PriorityQueueTest : public ::testing::Test {
+ public:
+ PriorityQueueTest() {}
+ protected:
+ PriorityQueue<int, 10, ExampleCompare> queue_;
+};
+
+TEST_F(PriorityQueueTest, DefaultIsEmpty) {
+ ASSERT_EQ(0u, queue_.size());
+ ASSERT_TRUE(queue_.empty());
+ ASSERT_FALSE(queue_.full());
+}
+
+TEST_F(PriorityQueueTest, CanAddData) {
+ auto it = queue_.PushFromBottom(5);
+ ASSERT_EQ(1u, queue_.size());
+ ASSERT_FALSE(queue_.empty());
+ ASSERT_FALSE(queue_.full());
+ EXPECT_EQ(5, *it);
+ EXPECT_EQ(5, queue_.get(0));
+ EXPECT_EQ(5, queue_.top());
+ EXPECT_EQ(queue_.begin(), it);
+ // Also check pre/post-fix increment/decrement operators
+ EXPECT_EQ(queue_.end(), ++it);
+ EXPECT_EQ(queue_.begin(), --it);
+ EXPECT_EQ(queue_.begin(), it++);
+ EXPECT_EQ(queue_.end(), it--);
+ EXPECT_EQ(queue_.begin(), it);
+}
+
+TEST_F(PriorityQueueTest, PriorityInsertion) {
+ queue_.PushFromBottom(10);
+ queue_.PushFromBottom(20);
+ queue_.PushFromBottom(15);
+ auto it = queue_.PushFromBottom(11);
+ ASSERT_EQ(4u, queue_.size());
+ ASSERT_FALSE(queue_.full());
+ ::std::vector<int> reverse_expected{20, 15, 11};
+ EXPECT_EQ(20, queue_.top());
+ for (; it != queue_.end(); ++it) {
+ EXPECT_EQ(reverse_expected.back(), *it);
+ reverse_expected.pop_back();
+ }
+ ASSERT_TRUE(reverse_expected.empty());
+}
+
+TEST_F(PriorityQueueTest, FullBufferInsertTop) {
+ for (int ii = 0; ii < 10; ++ii) {
+ queue_.PushFromBottom(ii);
+ }
+ ASSERT_EQ(10u, queue_.size());
+ ASSERT_TRUE(queue_.full());
+ // Check adding value at top.
+ queue_.PushFromBottom(100);
+ ASSERT_EQ(10u, queue_.size());
+ ASSERT_TRUE(queue_.full());
+ ::std::vector<int> reverse_expected{100, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+ EXPECT_EQ(100, queue_.top());
+ for (int val : queue_) {
+ EXPECT_EQ(reverse_expected.back(), val);
+ reverse_expected.pop_back();
+ }
+ ASSERT_TRUE(reverse_expected.empty());
+}
+
+TEST_F(PriorityQueueTest, FullBufferInsertMiddle) {
+ for (int ii = 9; ii >= 0; --ii) {
+ queue_.PushFromBottom(ii);
+ }
+ // Check adding value in the middle.
+ queue_.PushFromBottom(5);
+
+ ::std::vector<int> reverse_expected{9, 8, 7, 6, 5, 5, 4, 3, 2, 1};
+ EXPECT_EQ(9, queue_.top());
+ for (int val : queue_) {
+ EXPECT_EQ(reverse_expected.back(), val);
+ reverse_expected.pop_back();
+ }
+ ASSERT_TRUE(reverse_expected.empty());
+}
+
+TEST_F(PriorityQueueTest, FullBufferInsertBelowBottom) {
+ for (int ii = 9; ii >= 0; --ii) {
+ queue_.PushFromBottom(ii);
+ }
+ // Check adding value at the bottom where it will be dropped.
+ queue_.PushFromBottom(-1);
+
+ ::std::vector<int> reverse_expected{9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+ EXPECT_EQ(9, queue_.top());
+ for (int val : queue_) {
+ EXPECT_EQ(reverse_expected.back(), val);
+ reverse_expected.pop_back();
+ }
+ ASSERT_TRUE(reverse_expected.empty());
+}
+
+TEST_F(PriorityQueueTest, FullBufferInsertBottom) {
+ for (int ii = 18; ii >= 0; ii -= 2) {
+ queue_.PushFromBottom(ii);
+ }
+ ASSERT_TRUE(queue_.full());
+ // Check adding value at the bottom where it displaces the bottom value.
+ queue_.PushFromBottom(1);
+
+ ::std::vector<int> reverse_expected{18, 16, 14, 12, 10, 8, 6, 4, 2, 1};
+ EXPECT_EQ(18, queue_.top());
+ for (int val : queue_) {
+ EXPECT_EQ(reverse_expected.back(), val);
+ reverse_expected.pop_back();
+ }
+ ASSERT_TRUE(reverse_expected.empty());
+}
+
+// Check that operator-> works as expected on the iterator.
+struct TestStruct {
+ int a;
+ friend bool operator<(const TestStruct &lhs, const TestStruct &rhs) {
+ return lhs.a < rhs.a;
+ }
+};
+TEST(PriorirtyQueueTest, MemberAccess) {
+ PriorityQueue<TestStruct, 10, ::std::less<TestStruct>> q;
+ auto it = q.PushFromBottom({11});
+ EXPECT_EQ(11, it->a);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/ring_buffer/ring_buffer.h b/aos/containers/ring_buffer.h
similarity index 98%
rename from aos/ring_buffer/ring_buffer.h
rename to aos/containers/ring_buffer.h
index 9f93a8c..d4cd92a 100644
--- a/aos/ring_buffer/ring_buffer.h
+++ b/aos/containers/ring_buffer.h
@@ -13,7 +13,7 @@
public:
static constexpr size_t kBufferSize = buffer_size;
- RingBuffer() {}
+ constexpr RingBuffer() {}
// Add an item to the RingBuffer, overwriting the oldest element if necessary
void Push(const Data &data) {
diff --git a/aos/ring_buffer/ring_buffer_test.cc b/aos/containers/ring_buffer_test.cc
similarity index 98%
rename from aos/ring_buffer/ring_buffer_test.cc
rename to aos/containers/ring_buffer_test.cc
index a01b315..5fb2331 100644
--- a/aos/ring_buffer/ring_buffer_test.cc
+++ b/aos/containers/ring_buffer_test.cc
@@ -1,4 +1,4 @@
-#include "aos/ring_buffer/ring_buffer.h"
+#include "aos/containers/ring_buffer.h"
#include "gtest/gtest.h"
diff --git a/aos/containers/sized_array.h b/aos/containers/sized_array.h
new file mode 100644
index 0000000..0b8b447
--- /dev/null
+++ b/aos/containers/sized_array.h
@@ -0,0 +1,110 @@
+#ifndef AOS_CONTAINERS_SIZED_ARRAY_H_
+#define AOS_CONTAINERS_SIZED_ARRAY_H_
+
+#include <array>
+
+namespace aos {
+
+// An array along with a variable size. This is a simple variable-size container
+// with a fixed maximum size.
+//
+// Note that it default-constructs N T instances at construction time. This
+// simplifies the internal bookkeeping a lot (I believe this can be
+// all-constexpr in C++17), but makes it a poor choice for complex T.
+template <typename T, size_t N>
+class SizedArray {
+ private:
+ using array = std::array<T, N>;
+
+ public:
+ using value_type = typename array::value_type;
+ using size_type = typename array::size_type;
+ using difference_type = typename array::difference_type;
+ using reference = typename array::reference;
+ using const_reference = typename array::const_reference;
+ using pointer = typename array::pointer;
+ using const_pointer = typename array::const_pointer;
+ using iterator = typename array::iterator;
+ using const_iterator = typename array::const_iterator;
+ using reverse_iterator = typename array::reverse_iterator;
+ using const_reverse_iterator = typename array::const_reverse_iterator;
+
+ constexpr SizedArray() = default;
+ SizedArray(const SizedArray &) = default;
+ SizedArray(SizedArray &&) = default;
+ SizedArray &operator=(const SizedArray &) = default;
+ SizedArray &operator=(SizedArray &&) = default;
+
+ reference at(size_t i) {
+ check_index(i);
+ return array_.at(i);
+ }
+ const_reference at(size_t i) const {
+ check_index(i);
+ return array_.at(i);
+ }
+
+ reference operator[](size_t i) { return array_[i]; }
+ const_reference operator[](size_t i) const { return array_[i]; }
+
+ reference front() { return array_.front(); }
+ const_reference front() const { return array_.front(); }
+
+ reference back() { return array_[size_ - 1]; }
+ const_reference back() const { return array_[size_ - 1]; }
+
+ T *data() { return array_.data(); }
+ const T *data() const { return array_.data(); }
+
+ iterator begin() { return array_.begin(); }
+ const_iterator begin() const { return array_.begin(); }
+ const_iterator cbegin() const { return array_.cbegin(); }
+
+ iterator end() { return array_.begin() + size_; }
+ const_iterator end() const { return array_.begin() + size_; }
+ const_iterator cend() const { return array_.cbegin() + size_; }
+
+ reverse_iterator rbegin() { return array_.rend() - size_; }
+ const_reverse_iterator rbegin() const { return array_.rend() - size_; }
+ const_reverse_iterator crbegin() const { return array_.crend() - size_; }
+
+ reverse_iterator rend() { return array_.rend(); }
+ const_reverse_iterator rend() const { return array_.rend(); }
+ const_reverse_iterator crend() const { return array_.crend(); }
+
+ bool empty() const { return size_ == 0; }
+ bool full() const { return size() == max_size(); }
+
+ size_t size() const { return size_; }
+ constexpr size_t max_size() const { return array_.max_size(); }
+
+ void push_back(const T &t) {
+ array_.at(size_) = t;
+ ++size_;
+ }
+ void push_back(T &&t) {
+ array_.at(size_) = std::move(t);
+ ++size_;
+ }
+
+ void pop_back() {
+ if (empty()) {
+ __builtin_trap();
+ }
+ --size_;
+ }
+
+ private:
+ void check_index(size_t i) const {
+ if (i >= size_) {
+ __builtin_trap();
+ }
+ }
+
+ array array_;
+ size_t size_ = 0;
+};
+
+} // namespace aos
+
+#endif // AOS_CONTAINERS_SIZED_ARRAY_H_
diff --git a/aos/containers/sized_array_test.cc b/aos/containers/sized_array_test.cc
new file mode 100644
index 0000000..04a8e41
--- /dev/null
+++ b/aos/containers/sized_array_test.cc
@@ -0,0 +1,139 @@
+#include "aos/containers/sized_array.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+// Tests the various ways of accessing elements.
+TEST(SizedArrayTest, ElementAccess) {
+ SizedArray<int, 5> a;
+ a.push_back(9);
+ a.push_back(7);
+ a.push_back(1);
+ EXPECT_EQ(9, a[0]);
+ EXPECT_EQ(7, a[1]);
+ EXPECT_EQ(1, a[2]);
+ EXPECT_EQ(9, a.data()[0]);
+ EXPECT_EQ(7, a.data()[1]);
+ EXPECT_EQ(1, a.data()[2]);
+ EXPECT_EQ(9, a.at(0));
+ EXPECT_EQ(7, a.at(1));
+ EXPECT_EQ(1, a.at(2));
+ EXPECT_EQ(9, a.front());
+ EXPECT_EQ(1, a.back());
+
+ a.pop_back();
+ EXPECT_EQ(9, a.front());
+ EXPECT_EQ(7, a.back());
+}
+
+// Tests the accessors that don't access data.
+TEST(SizedArrayTest, Accessors) {
+ SizedArray<int, 5> a;
+ EXPECT_TRUE(a.empty());
+ EXPECT_FALSE(a.full());
+ EXPECT_EQ(0u, a.size());
+ EXPECT_EQ(5u, a.max_size());
+
+ a.push_back(9);
+ EXPECT_FALSE(a.empty());
+ EXPECT_FALSE(a.full());
+ EXPECT_EQ(1u, a.size());
+ EXPECT_EQ(5u, a.max_size());
+
+ a.push_back(9);
+ EXPECT_FALSE(a.empty());
+ EXPECT_FALSE(a.full());
+ EXPECT_EQ(2u, a.size());
+ EXPECT_EQ(5u, a.max_size());
+
+ a.push_back(9);
+ EXPECT_FALSE(a.empty());
+ EXPECT_FALSE(a.full());
+ EXPECT_EQ(3u, a.size());
+ EXPECT_EQ(5u, a.max_size());
+
+ a.push_back(9);
+ EXPECT_FALSE(a.empty());
+ EXPECT_FALSE(a.full());
+ EXPECT_EQ(4u, a.size());
+ EXPECT_EQ(5u, a.max_size());
+
+ a.push_back(9);
+ EXPECT_FALSE(a.empty());
+ EXPECT_TRUE(a.full());
+ EXPECT_EQ(5u, a.size());
+ EXPECT_EQ(5u, a.max_size());
+}
+
+// Tests the various kinds of iterator.
+TEST(SizedArrayTest, Iterators) {
+ SizedArray<int, 5> a;
+ EXPECT_EQ(a.begin(), a.end());
+ EXPECT_EQ(a.cbegin(), a.cend());
+ EXPECT_EQ(a.rbegin(), a.rend());
+ EXPECT_EQ(a.crbegin(), a.crend());
+ a.push_back(9);
+ a.push_back(7);
+ a.push_back(1);
+
+ {
+ auto iterator = a.begin();
+ ASSERT_NE(iterator, a.end());
+ EXPECT_EQ(9, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.end());
+ EXPECT_EQ(7, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.end());
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+ EXPECT_EQ(iterator, a.end());
+ }
+
+ {
+ auto iterator = a.cbegin();
+ ASSERT_NE(iterator, a.cend());
+ EXPECT_EQ(9, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.cend());
+ EXPECT_EQ(7, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.cend());
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+ EXPECT_EQ(iterator, a.cend());
+ }
+
+ {
+ auto iterator = a.rbegin();
+ ASSERT_NE(iterator, a.rend());
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.rend());
+ EXPECT_EQ(7, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.rend());
+ EXPECT_EQ(9, *iterator);
+ ++iterator;
+ EXPECT_EQ(iterator, a.rend());
+ }
+
+ {
+ auto iterator = a.crbegin();
+ ASSERT_NE(iterator, a.crend());
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.crend());
+ EXPECT_EQ(7, *iterator);
+ ++iterator;
+ ASSERT_NE(iterator, a.crend());
+ EXPECT_EQ(9, *iterator);
+ ++iterator;
+ EXPECT_EQ(iterator, a.crend());
+ }
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/controls/control_loop.h b/aos/controls/control_loop.h
index fd63e3f..335a1bb 100644
--- a/aos/controls/control_loop.h
+++ b/aos/controls/control_loop.h
@@ -22,21 +22,6 @@
virtual void Iterate() = 0;
};
-class SerializableControlLoop : public Runnable {
- public:
- // Returns the size of all the data to be sent when serialized.
- virtual size_t SeralizedSize() = 0;
- // Serialize the current data.
- virtual void Serialize(char *buffer) const = 0;
- // Serialize zeroed data in case the data is out of date.
- virtual void SerializeZeroMessage(char *buffer) const = 0;
- // Deserialize data into the control loop.
- virtual void Deserialize(const char *buffer) = 0;
- // Unique identifier for the control loop.
- // Most likely the hash of the queue group.
- virtual uint32_t UniqueID() = 0;
-};
-
// Control loops run this often, "starting" at time 0.
constexpr ::std::chrono::nanoseconds kLoopFrequency =
::std::chrono::milliseconds(5);
@@ -47,7 +32,7 @@
// It will then call the RunIteration method every cycle that it has enough
// valid data for the control loop to run.
template <class T>
-class ControlLoop : public SerializableControlLoop {
+class ControlLoop : public Runnable {
public:
// Create some convenient typedefs to reference the Goal, Position, Status,
// and Output structures.
@@ -78,14 +63,13 @@
}
// Constructs and sends a message on the output queue which sets everything to
- // a safe state (generally motors off). For some subclasses, this will be a
- // bit different (ie pistons).
- // The implementation here creates a new Output message, calls Zero() on it,
- // and then sends it.
- virtual void ZeroOutputs();
+ // a safe state. Default is to set everything to zero. Override Zero below
+ // to change that behavior.
+ void ZeroOutputs();
// Sets the output to zero.
- // Over-ride if a value of zero is not "off" for this subsystem.
+ // Override this if a value of zero (or false) is not "off" for this
+ // subsystem.
virtual void Zero(OutputType *output) { output->Zero(); }
// Runs the loop forever.
@@ -94,29 +78,6 @@
// Runs one cycle of the loop.
void Iterate() override;
- // Returns the name of the queue group.
- const char *name() { return control_loop_->name(); }
-
- // Methods to serialize all the data that should be sent over the network.
- size_t SeralizedSize() override { return control_loop_->goal->Size(); }
- void Serialize(char *buffer) const override {
- control_loop_->goal->Serialize(buffer);
- }
- void SerializeZeroMessage(char *buffer) const override {
- GoalType zero_goal;
- zero_goal.Zero();
- zero_goal.Serialize(buffer);
- }
-
- void Deserialize(const char *buffer) override {
- ScopedMessagePtr<GoalType> new_msg = control_loop_->goal.MakeMessage();
- new_msg->Deserialize(buffer);
- new_msg.Send();
- }
-
- uint32_t UniqueID() override { return control_loop_->hash(); }
-
-
protected:
static void Quit(int /*signum*/) {
run_ = false;
@@ -136,9 +97,6 @@
OutputType *output,
StatusType *status) = 0;
- T *queue_group() { return control_loop_; }
- const T *queue_group() const { return control_loop_; }
-
private:
static constexpr ::std::chrono::milliseconds kStaleLogInterval =
::std::chrono::milliseconds(100);
diff --git a/aos/queue.h b/aos/queue.h
index 1897773..47540f6 100644
--- a/aos/queue.h
+++ b/aos/queue.h
@@ -209,16 +209,13 @@
public:
// Constructs a queue group given its name and a unique hash of the name and
// type.
- QueueGroup(const char *name, uint32_t hash) : name_(name), hash_(hash) {}
+ QueueGroup(const char *name) : name_(name) {}
// Returns the name of the queue group.
const char *name() const { return name_.c_str(); }
- // Returns a unique hash representing this instance of the queue group.
- uint32_t hash() const { return hash_; }
private:
std::string name_;
- uint32_t hash_;
};
} // namespace aos
diff --git a/aos/queue_test.cc b/aos/queue_test.cc
index 8eeca6e..1c4ebdd 100644
--- a/aos/queue_test.cc
+++ b/aos/queue_test.cc
@@ -213,7 +213,6 @@
protected:
GroupTest()
: my_test_queuegroup(".aos.common.testing.test_queuegroup",
- 0x20561114,
".aos.common.testing.test_queuegroup.first",
".aos.common.testing.test_queuegroup.second") {}
@@ -226,16 +225,6 @@
::aos::testing::TestSharedMemory my_shm_;
};
-// Tests that the hash gets preserved.
-TEST_F(GroupTest, Hash) {
- EXPECT_EQ(static_cast<uint32_t>(0x20561114), my_test_queuegroup.hash());
-}
-
-// Tests that the hash works.
-TEST_F(GroupTest, RealHash) {
- EXPECT_EQ(static_cast<uint32_t>(0x93596b2f), test_queuegroup.hash());
-}
-
// Tests that name works.
TEST_F(GroupTest, Name) {
EXPECT_EQ(std::string(".aos.common.testing.test_queuegroup"),
diff --git a/aos/ring_buffer/BUILD b/aos/ring_buffer/BUILD
deleted file mode 100644
index 9f6ab06..0000000
--- a/aos/ring_buffer/BUILD
+++ /dev/null
@@ -1,19 +0,0 @@
-package(default_visibility = ["//visibility:public"])
-
-cc_library(
- name = "ring_buffer",
- hdrs = [
- "ring_buffer.h",
- ],
-)
-
-cc_test(
- name = "ring_buffer_test",
- srcs = [
- "ring_buffer_test.cc",
- ],
- deps = [
- ":ring_buffer",
- "//aos/testing:googletest",
- ],
-)
diff --git a/frc971/codelab/basic_test.cc b/frc971/codelab/basic_test.cc
index 0761cb2..5132407 100644
--- a/frc971/codelab/basic_test.cc
+++ b/frc971/codelab/basic_test.cc
@@ -20,7 +20,7 @@
class BasicSimulation {
public:
BasicSimulation()
- : basic_queue_(".frc971.codelab.basic_queue", 0x78d8e372,
+ : basic_queue_(".frc971.codelab.basic_queue",
".frc971.codelab.basic_queue.goal",
".frc971.codelab.basic_queue.position",
".frc971.codelab.basic_queue.output",
@@ -60,7 +60,7 @@
class BasicControlLoopTest : public ::aos::testing::ControlLoopTest {
public:
BasicControlLoopTest()
- : basic_queue_(".frc971.codelab.basic_queue", 0x78d8e372,
+ : basic_queue_(".frc971.codelab.basic_queue",
".frc971.codelab.basic_queue.goal",
".frc971.codelab.basic_queue.position",
".frc971.codelab.basic_queue.output",
diff --git a/frc971/control_loops/control_loops.q b/frc971/control_loops/control_loops.q
index 03e93fd..bebecd0 100644
--- a/frc971/control_loops/control_loops.q
+++ b/frc971/control_loops/control_loops.q
@@ -65,7 +65,7 @@
};
// The internal state of a zeroing estimator.
-struct AbsoluteEstimatorState {
+struct PotAndAbsoluteEncoderEstimatorState {
// If true, there has been a fatal error for the estimator.
bool error;
// If the joint has seen an index pulse and is zeroed.
diff --git a/frc971/control_loops/drivetrain/drivetrain_lib_test.cc b/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
index e4d320e..a6711e4 100644
--- a/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
+++ b/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
@@ -103,7 +103,7 @@
// TODO(aschuh) Do we want to test the clutch one too?
DrivetrainSimulation()
: drivetrain_plant_(new DrivetrainPlant(MakeDrivetrainPlant())),
- my_drivetrain_queue_(".frc971.control_loops.drivetrain", 0x8a8dde77,
+ my_drivetrain_queue_(".frc971.control_loops.drivetrain",
".frc971.control_loops.drivetrain.goal",
".frc971.control_loops.drivetrain.position",
".frc971.control_loops.drivetrain.output",
@@ -223,7 +223,7 @@
DrivetrainSimulation drivetrain_motor_plant_;
DrivetrainTest()
- : my_drivetrain_queue_(".frc971.control_loops.drivetrain", 0x8a8dde77,
+ : my_drivetrain_queue_(".frc971.control_loops.drivetrain",
".frc971.control_loops.drivetrain.goal",
".frc971.control_loops.drivetrain.position",
".frc971.control_loops.drivetrain.output",
diff --git a/frc971/control_loops/profiled_subsystem.q b/frc971/control_loops/profiled_subsystem.q
index 3bcd6c2..d1356da 100644
--- a/frc971/control_loops/profiled_subsystem.q
+++ b/frc971/control_loops/profiled_subsystem.q
@@ -113,7 +113,7 @@
float feedforwards_power;
// State of the estimator.
- .frc971.AbsoluteEstimatorState estimator_state;
+ .frc971.PotAndAbsoluteEncoderEstimatorState estimator_state;
};
struct IndexProfiledJointStatus {
diff --git a/frc971/control_loops/python/BUILD b/frc971/control_loops/python/BUILD
index 9080e9a..95607c9 100644
--- a/frc971/control_loops/python/BUILD
+++ b/frc971/control_loops/python/BUILD
@@ -149,3 +149,44 @@
"@matplotlib",
],
)
+
+py_library(
+ name = "angular_system",
+ srcs = ["angular_system.py"],
+ restricted_to = ["//tools:k8"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":controls",
+ "//aos/util:py_trapezoid_profile",
+ "//frc971/control_loops:python_init",
+ "@matplotlib",
+ ],
+)
+
+py_binary(
+ name = "path_edit",
+ srcs = [
+ "path_edit.py",
+ ],
+ visibility = ["//visibility:public"],
+ restricted_to = ["//tools:k8"],
+ deps = [
+ ":python_init",
+ ":libspline",
+ "@python_gtk",
+ ":basic_window",
+ ],
+)
+
+py_library(
+ name = "basic_window",
+ srcs = [
+ "basic_window.py",
+ "color.py",
+ ],
+ restricted_to = ["//tools:k8"],
+ deps = [
+ ":python_init",
+ "@python_gtk",
+ ],
+)
diff --git a/frc971/control_loops/python/angular_system.py b/frc971/control_loops/python/angular_system.py
new file mode 100755
index 0000000..8d375f8
--- /dev/null
+++ b/frc971/control_loops/python/angular_system.py
@@ -0,0 +1,381 @@
+#!/usr/bin/python
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import controls
+import numpy
+from matplotlib import pylab
+import glog
+
+
+class AngularSystemParams(object):
+ def __init__(self,
+ name,
+ motor,
+ G,
+ J,
+ q_pos,
+ q_vel,
+ kalman_q_pos,
+ kalman_q_vel,
+ kalman_q_voltage,
+ kalman_r_position,
+ dt=0.005):
+ self.name = name
+ self.motor = motor
+ self.G = G
+ self.J = J
+ self.q_pos = q_pos
+ self.q_vel = q_vel
+ self.kalman_q_pos = kalman_q_pos
+ self.kalman_q_vel = kalman_q_vel
+ self.kalman_q_voltage = kalman_q_voltage
+ self.kalman_r_position = kalman_r_position
+ self.dt = dt
+
+
+class AngularSystem(control_loop.ControlLoop):
+ def __init__(self, params, name="AngularSystem"):
+ super(AngularSystem, self).__init__(name)
+ self.params = params
+
+ self.motor = params.motor
+
+ # Gear ratio
+ self.G = params.G
+
+ # Moment of inertia in kg m^2
+ self.J = params.J + self.motor.motor_inertia / (self.G ** 2.0)
+
+ # Control loop time step
+ self.dt = params.dt
+
+ # State is [position, velocity]
+ # Input is [Voltage]
+ C1 = self.motor.Kt / (self.G * self.G * self.motor.resistance *
+ self.J * self.motor.Kv)
+ C2 = self.motor.Kt / (self.G * self.J * self.motor.resistance)
+
+ self.A_continuous = numpy.matrix([[0, 1], [0, -C1]])
+
+ # Start with the unmodified input
+ self.B_continuous = numpy.matrix([[0], [C2]])
+ glog.debug(repr(self.A_continuous))
+ glog.debug(repr(self.B_continuous))
+
+ self.C = numpy.matrix([[1, 0]])
+ self.D = numpy.matrix([[0]])
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ controllability = controls.ctrb(self.A, self.B)
+ glog.debug('Controllability of %d',
+ numpy.linalg.matrix_rank(controllability))
+ glog.debug('J: %f', self.J)
+ glog.debug('Stall torque: %f', self.motor.stall_torque / self.G)
+ glog.debug('Stall acceleration: %f',
+ self.motor.stall_torque / self.G / self.J)
+
+ glog.debug('Free speed is %f',
+ -self.B_continuous[1, 0] / self.A_continuous[1, 1] * 12.0)
+
+ self.Q = numpy.matrix([[(1.0 / (self.params.q_pos**2.0)), 0.0],
+ [0.0, (1.0 / (self.params.q_vel**2.0))]])
+
+ self.R = numpy.matrix([[(1.0 / (12.0**2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+
+ q_pos_ff = 0.005
+ q_vel_ff = 1.0
+ self.Qff = numpy.matrix([[(1.0 / (q_pos_ff**2.0)), 0.0],
+ [0.0, (1.0 / (q_vel_ff**2.0))]])
+
+ self.Kff = controls.TwoStateFeedForwards(self.B, self.Qff)
+
+ glog.debug('K %s', repr(self.K))
+ glog.debug('Poles are %s',
+ repr(numpy.linalg.eig(self.A - self.B * self.K)[0]))
+
+ self.Q = numpy.matrix([[(self.params.kalman_q_pos**2.0), 0.0],
+ [0.0, (self.params.kalman_q_vel**2.0)]])
+
+ self.R = numpy.matrix([[(self.params.kalman_r_position**2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+
+ glog.debug('Kal %s', repr(self.KalmanGain))
+
+ # The box formed by U_min and U_max must encompass all possible values,
+ # or else Austin's code gets angry.
+ self.U_max = numpy.matrix([[12.0]])
+ self.U_min = numpy.matrix([[-12.0]])
+
+ self.InitializeState()
+
+
+class IntegralAngularSystem(AngularSystem):
+ def __init__(self, params, name="IntegralAngularSystem"):
+ super(IntegralAngularSystem, self).__init__(params, name=name)
+
+ self.A_continuous_unaugmented = self.A_continuous
+ self.B_continuous_unaugmented = self.B_continuous
+
+ self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
+ self.A_continuous[0:2, 0:2] = self.A_continuous_unaugmented
+ self.A_continuous[0:2, 2] = self.B_continuous_unaugmented
+
+ self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
+ self.B_continuous[0:2, 0] = self.B_continuous_unaugmented
+
+ self.C_unaugmented = self.C
+ self.C = numpy.matrix(numpy.zeros((1, 3)))
+ self.C[0:1, 0:2] = self.C_unaugmented
+
+ self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+ self.B_continuous, self.dt)
+
+ self.Q = numpy.matrix(
+ [[(self.params.kalman_q_pos**2.0), 0.0, 0.0],
+ [0.0, (self.params.kalman_q_vel**2.0), 0.0],
+ [0.0, 0.0, (self.params.kalman_q_voltage**2.0)]])
+
+ self.R = numpy.matrix([[(self.params.kalman_r_position**2.0)]])
+
+ self.KalmanGain, self.Q_steady = controls.kalman(
+ A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
+
+ self.K_unaugmented = self.K
+ self.K = numpy.matrix(numpy.zeros((1, 3)))
+ self.K[0, 0:2] = self.K_unaugmented
+ self.K[0, 2] = 1
+
+ self.Kff = numpy.concatenate(
+ (self.Kff, numpy.matrix(numpy.zeros((1, 1)))), axis=1)
+
+ self.InitializeState()
+
+
+def RunTest(plant,
+ end_goal,
+ controller,
+ observer=None,
+ duration=1.0,
+ use_profile=True,
+ kick_time=0.5,
+ kick_magnitude=0.0,
+ max_velocity=10.0,
+ max_acceleration=70.0):
+ """Runs the plant with an initial condition and goal.
+
+ Args:
+ plant: plant object to use.
+ end_goal: end_goal state.
+ controller: AngularSystem object to get K from, or None if we should
+ use plant.
+ observer: AngularSystem object to use for the observer, or None if we
+ should use the actual state.
+ duration: float, time in seconds to run the simulation for.
+ kick_time: float, time in seconds to kick the robot.
+ kick_magnitude: float, disturbance in volts to apply.
+ max_velocity: float, The maximum velocity for the profile.
+ max_acceleration: float, The maximum acceleration for the profile.
+ """
+ t_plot = []
+ x_plot = []
+ v_plot = []
+ a_plot = []
+ x_goal_plot = []
+ v_goal_plot = []
+ x_hat_plot = []
+ u_plot = []
+ offset_plot = []
+
+ if controller is None:
+ controller = plant
+
+ vbat = 12.0
+
+ goal = numpy.concatenate(
+ (plant.X, numpy.matrix(numpy.zeros((1, 1)))), axis=0)
+
+ profile = TrapezoidProfile(plant.dt)
+ profile.set_maximum_acceleration(max_acceleration)
+ profile.set_maximum_velocity(max_velocity)
+ profile.SetGoal(goal[0, 0])
+
+ U_last = numpy.matrix(numpy.zeros((1, 1)))
+ iterations = int(duration / plant.dt)
+ for i in xrange(iterations):
+ t = i * plant.dt
+ observer.Y = plant.Y
+ observer.CorrectObserver(U_last)
+
+ offset_plot.append(observer.X_hat[2, 0])
+ x_hat_plot.append(observer.X_hat[0, 0])
+
+ next_goal = numpy.concatenate(
+ (profile.Update(end_goal[0, 0], end_goal[1, 0]),
+ numpy.matrix(numpy.zeros((1, 1)))),
+ axis=0)
+
+ ff_U = controller.Kff * (next_goal - observer.A * goal)
+
+ if use_profile:
+ U_uncapped = controller.K * (goal - observer.X_hat) + ff_U
+ x_goal_plot.append(goal[0, 0])
+ v_goal_plot.append(goal[1, 0])
+ else:
+ U_uncapped = controller.K * (end_goal - observer.X_hat)
+ x_goal_plot.append(end_goal[0, 0])
+ v_goal_plot.append(end_goal[1, 0])
+
+ U = U_uncapped.copy()
+ U[0, 0] = numpy.clip(U[0, 0], -vbat, vbat)
+ x_plot.append(plant.X[0, 0])
+
+ if v_plot:
+ last_v = v_plot[-1]
+ else:
+ last_v = 0
+
+ v_plot.append(plant.X[1, 0])
+ a_plot.append((v_plot[-1] - last_v) / plant.dt)
+
+ u_offset = 0.0
+ if t >= kick_time:
+ u_offset = kick_magnitude
+ plant.Update(U + u_offset)
+
+ observer.PredictObserver(U)
+
+ t_plot.append(t)
+ u_plot.append(U[0, 0])
+
+ ff_U -= U_uncapped - U
+ goal = controller.A * goal + controller.B * ff_U
+
+ if U[0, 0] != U_uncapped[0, 0]:
+ profile.MoveCurrentState(
+ numpy.matrix([[goal[0, 0]], [goal[1, 0]]]))
+
+ glog.debug('Time: %f', t_plot[-1])
+ glog.debug('goal_error %s', repr(end_goal - goal))
+ glog.debug('error %s', repr(observer.X_hat - end_goal))
+
+ pylab.subplot(3, 1, 1)
+ pylab.plot(t_plot, x_plot, label='x')
+ pylab.plot(t_plot, x_hat_plot, label='x_hat')
+ pylab.plot(t_plot, x_goal_plot, label='x_goal')
+ pylab.legend()
+
+ pylab.subplot(3, 1, 2)
+ pylab.plot(t_plot, u_plot, label='u')
+ pylab.plot(t_plot, offset_plot, label='voltage_offset')
+ pylab.legend()
+
+ pylab.subplot(3, 1, 3)
+ pylab.plot(t_plot, a_plot, label='a')
+ pylab.legend()
+
+ pylab.show()
+
+
+def PlotStep(params, R):
+ """Plots a step move to the goal.
+
+ Args:
+ R: numpy.matrix(2, 1), the goal"""
+ plant = AngularSystem(params, params.name)
+ controller = IntegralAngularSystem(params, params.name)
+ observer = IntegralAngularSystem(params, params.name)
+
+ # Test moving the system.
+ initial_X = numpy.matrix([[0.0], [0.0]])
+ augmented_R = numpy.matrix(numpy.zeros((3, 1)))
+ augmented_R[0:2, :] = R
+ RunTest(
+ plant,
+ end_goal=augmented_R,
+ controller=controller,
+ observer=observer,
+ duration=2.0,
+ use_profile=False,
+ kick_time=1.0,
+ kick_magnitude=0.0)
+
+
+def PlotKick(params, R):
+ """Plots a step motion with a kick at 1.0 seconds.
+
+ Args:
+ R: numpy.matrix(2, 1), the goal"""
+ plant = AngularSystem(params, params.name)
+ controller = IntegralAngularSystem(params, params.name)
+ observer = IntegralAngularSystem(params, params.name)
+
+ # Test moving the system.
+ initial_X = numpy.matrix([[0.0], [0.0]])
+ augmented_R = numpy.matrix(numpy.zeros((3, 1)))
+ augmented_R[0:2, :] = R
+ RunTest(
+ plant,
+ end_goal=augmented_R,
+ controller=controller,
+ observer=observer,
+ duration=2.0,
+ use_profile=False,
+ kick_time=1.0,
+ kick_magnitude=2.0)
+
+
+def PlotMotion(params, R, max_velocity=10.0, max_acceleration=70.0):
+ """Plots a trapezoidal motion.
+
+ Args:
+ R: numpy.matrix(2, 1), the goal,
+ max_velocity: float, The max velocity of the profile.
+ max_acceleration: float, The max acceleration of the profile.
+ """
+ plant = AngularSystem(params, params.name)
+ controller = IntegralAngularSystem(params, params.name)
+ observer = IntegralAngularSystem(params, params.name)
+
+ # Test moving the system.
+ initial_X = numpy.matrix([[0.0], [0.0]])
+ augmented_R = numpy.matrix(numpy.zeros((3, 1)))
+ augmented_R[0:2, :] = R
+ RunTest(
+ plant,
+ end_goal=augmented_R,
+ controller=controller,
+ observer=observer,
+ duration=2.0,
+ use_profile=True,
+ max_velocity=max_velocity,
+ max_acceleration=max_acceleration)
+
+
+def WriteAngularSystem(params, plant_files, controller_files, year_namespaces):
+ """Writes out the constants for a angular system to a file.
+
+ Args:
+ params: AngularSystemParams, the parameters defining the system.
+ plant_files: list of strings, the cc and h files for the plant.
+ controller_files: list of strings, the cc and h files for the integral
+ controller.
+ year_namespaces: list of strings, the namespace list to use.
+ """
+ # Write the generated constants out to a file.
+ angular_system = AngularSystem(params, params.name)
+ loop_writer = control_loop.ControlLoopWriter(
+ angular_system.name, [angular_system], namespaces=year_namespaces)
+ loop_writer.Write(plant_files[0], plant_files[1])
+
+ integral_angular_system = IntegralAngularSystem(params,
+ 'Integral' + params.name)
+ integral_loop_writer = control_loop.ControlLoopWriter(
+ integral_angular_system.name, [integral_angular_system],
+ namespaces=year_namespaces)
+ integral_loop_writer.Write(controller_files[0], controller_files[1])
diff --git a/y2018/control_loops/python/basic_window.py b/frc971/control_loops/python/basic_window.py
similarity index 100%
rename from y2018/control_loops/python/basic_window.py
rename to frc971/control_loops/python/basic_window.py
diff --git a/y2018/control_loops/python/color.py b/frc971/control_loops/python/color.py
similarity index 100%
rename from y2018/control_loops/python/color.py
rename to frc971/control_loops/python/color.py
diff --git a/frc971/control_loops/python/control_loop.py b/frc971/control_loops/python/control_loop.py
index 74cd389..9aa00df 100644
--- a/frc971/control_loops/python/control_loop.py
+++ b/frc971/control_loops/python/control_loop.py
@@ -587,6 +587,8 @@
self.Kv = (self.free_speed / (12.0 - self.resistance * self.free_current))
# Torque constant (N * m / A)
self.Kt = self.stall_torque / self.stall_current
+ # Motor inertia in kg m^2
+ self.motor_inertia = 0.000006
class MN3510(object):
def __init__(self):
diff --git a/frc971/control_loops/python/linear_system.py b/frc971/control_loops/python/linear_system.py
index 21fa4ec..322e41a 100755
--- a/frc971/control_loops/python/linear_system.py
+++ b/frc971/control_loops/python/linear_system.py
@@ -4,7 +4,6 @@
from frc971.control_loops.python import control_loop
from frc971.control_loops.python import controls
import numpy
-import sys
from matplotlib import pylab
import glog
@@ -48,7 +47,7 @@
self.G = params.G
self.radius = params.radius
- # 5.4 kg of moving mass for the linear_system
+ # Mass in kg
self.mass = params.mass + self.motor.motor_inertia / (
(self.G * self.radius)**2.0)
@@ -126,8 +125,6 @@
def __init__(self, params, name='IntegralLinearSystem'):
super(IntegralLinearSystem, self).__init__(params, name=name)
- self.kalman_q_voltage = params.kalman_q_voltage
-
self.A_continuous_unaugmented = self.A_continuous
self.B_continuous_unaugmented = self.B_continuous
@@ -174,20 +171,22 @@
use_profile=True,
kick_time=0.5,
kick_magnitude=0.0,
- max_velocity=0.3):
+ max_velocity=0.3,
+ max_acceleration=10.0):
"""Runs the plant with an initial condition and goal.
Args:
plant: plant object to use.
end_goal: end_goal state.
- controller: Intake object to get K from, or None if we should
+ controller: LinearSystem object to get K from, or None if we should
use plant.
- observer: Intake object to use for the observer, or None if we should
- use the actual state.
+ observer: LinearSystem object to use for the observer, or None if we
+ should use the actual state.
duration: float, time in seconds to run the simulation for.
kick_time: float, time in seconds to kick the robot.
kick_magnitude: float, disturbance in volts to apply.
max_velocity: float, the max speed in m/s to profile.
+ max_acceleration: float, the max acceleration in m/s/s to profile.
"""
t_plot = []
x_plot = []
@@ -208,7 +207,7 @@
(plant.X, numpy.matrix(numpy.zeros((1, 1)))), axis=0)
profile = TrapezoidProfile(plant.dt)
- profile.set_maximum_acceleration(10.0)
+ profile.set_maximum_acceleration(max_acceleration)
profile.set_maximum_velocity(max_velocity)
profile.SetGoal(goal[0, 0])
@@ -337,12 +336,13 @@
kick_magnitude=2.0)
-def PlotMotion(params, R, max_velocity=0.3):
+def PlotMotion(params, R, max_velocity=0.3, max_acceleration=10.0):
"""Plots a trapezoidal motion.
Args:
R: numpy.matrix(2, 1), the goal,
max_velocity: float, The max velocity of the profile.
+ max_acceleration: float, The max acceleration of the profile.
"""
plant = LinearSystem(params, params.name)
controller = IntegralLinearSystem(params, params.name)
@@ -359,7 +359,8 @@
observer=observer,
duration=2.0,
use_profile=True,
- max_velocity=max_velocity)
+ max_velocity=max_velocity,
+ max_acceleration=max_acceleration)
def WriteLinearSystem(params, plant_files, controller_files, year_namespaces):
diff --git a/y2018/control_loops/python/path_edit.py b/frc971/control_loops/python/path_edit.py
similarity index 100%
rename from y2018/control_loops/python/path_edit.py
rename to frc971/control_loops/python/path_edit.py
diff --git a/frc971/zeroing/zeroing.cc b/frc971/zeroing/zeroing.cc
index 14d26c9..7bf2b27 100644
--- a/frc971/zeroing/zeroing.cc
+++ b/frc971/zeroing/zeroing.cc
@@ -3,20 +3,13 @@
#include <algorithm>
#include <cmath>
#include <limits>
+#include <numeric>
#include <vector>
#include "frc971/zeroing/wrap.h"
namespace frc971 {
namespace zeroing {
-namespace {
-
-bool compare_encoder(const PotAndAbsolutePosition &left,
- const PotAndAbsolutePosition &right) {
- return left.encoder < right.encoder;
-}
-
-} // namespace
PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
const constants::PotAndIndexPulseZeroingConstants &constants)
@@ -253,15 +246,16 @@
return r;
}
-PotAndAbsEncoderZeroingEstimator::PotAndAbsEncoderZeroingEstimator(
+PotAndAbsoluteEncoderZeroingEstimator::PotAndAbsoluteEncoderZeroingEstimator(
const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
: constants_(constants) {
relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
offset_samples_.reserve(constants_.average_filter_size);
+ buffered_samples_.reserve(constants_.moving_buffer_size);
Reset();
}
-void PotAndAbsEncoderZeroingEstimator::Reset() {
+void PotAndAbsoluteEncoderZeroingEstimator::Reset() {
first_offset_ = 0.0;
pot_relative_encoder_offset_ = 0.0;
offset_ = 0.0;
@@ -293,7 +287,7 @@
// of samples and check that the buffered samples are not different than the
// zeroing threshold. At any point that the samples differ too much, do not
// update estimates based on those samples.
-void PotAndAbsEncoderZeroingEstimator::UpdateEstimate(
+void PotAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
const PotAndAbsolutePosition &info) {
// Check for Abs Encoder NaN value that would mess up the rest of the zeroing
// code below. NaN values are given when the Absolute Encoder is disconnected.
@@ -325,12 +319,15 @@
} else {
// Have enough samples to start determining if the robot is moving or not.
buffered_samples_[buffered_samples_idx_] = info;
- auto max_value =
- ::std::max_element(buffered_samples_.begin(), buffered_samples_.end(),
- compare_encoder)->encoder;
- auto min_value =
- ::std::min_element(buffered_samples_.begin(), buffered_samples_.end(),
- compare_encoder)->encoder;
+ const auto minmax_value = ::std::minmax_element(
+ buffered_samples_.begin(), buffered_samples_.end(),
+ [](const PotAndAbsolutePosition &left,
+ const PotAndAbsolutePosition &right) {
+ return left.encoder < right.encoder;
+ });
+ const double min_value = minmax_value.first->encoder;
+ const double max_value = minmax_value.second->encoder;
+
if (::std::abs(max_value - min_value) < constants_.zeroing_threshold) {
// Robot isn't moving, use middle sample to determine offsets.
moving = false;
@@ -344,47 +341,39 @@
const int middle_index =
(buffered_samples_idx_ + (constants_.moving_buffer_size - 1) / 2) %
constants_.moving_buffer_size;
-
- // Compute the sum of all the offset samples.
- double relative_to_absolute_offset_sum = 0.0;
- for (size_t i = 0; i < relative_to_absolute_offset_samples_.size(); ++i) {
- relative_to_absolute_offset_sum +=
- relative_to_absolute_offset_samples_[i];
- }
+ const PotAndAbsolutePosition &sample = buffered_samples_[middle_index];
// Compute the average offset between the absolute encoder and relative
// encoder. If we have 0 samples, assume it is 0.
double average_relative_to_absolute_offset =
relative_to_absolute_offset_samples_.size() == 0
? 0.0
- : relative_to_absolute_offset_sum /
+ : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
+ relative_to_absolute_offset_samples_.end(),
+ 0.0) /
relative_to_absolute_offset_samples_.size();
const double adjusted_incremental_encoder =
- buffered_samples_[middle_index].encoder +
- average_relative_to_absolute_offset;
+ sample.encoder + average_relative_to_absolute_offset;
// Now, compute the nearest absolute encoder value to the offset relative
// encoder position.
const double adjusted_absolute_encoder =
Wrap(adjusted_incremental_encoder,
- buffered_samples_[middle_index].absolute_encoder -
- constants_.measured_absolute_position,
+ sample.absolute_encoder - constants_.measured_absolute_position,
constants_.one_revolution_distance);
// Reverse the math on the previous line to compute the absolute encoder.
// Do this by taking the adjusted encoder, and then subtracting off the
// second argument above, and the value that was added by Wrap.
filtered_absolute_encoder_ =
- ((buffered_samples_[middle_index].encoder +
- average_relative_to_absolute_offset) -
+ ((sample.encoder + average_relative_to_absolute_offset) -
(-constants_.measured_absolute_position +
(adjusted_absolute_encoder -
- (buffered_samples_[middle_index].absolute_encoder -
- constants_.measured_absolute_position))));
+ (sample.absolute_encoder - constants_.measured_absolute_position))));
const double relative_to_absolute_offset =
- adjusted_absolute_encoder - buffered_samples_[middle_index].encoder;
+ adjusted_absolute_encoder - sample.encoder;
// Add the sample and update the average with the new reading.
const size_t relative_to_absolute_offset_samples_size =
@@ -412,29 +401,22 @@
// Now compute the offset between the pot and relative encoder.
if (offset_samples_.size() < constants_.average_filter_size) {
- offset_samples_.push_back(buffered_samples_[middle_index].pot -
- buffered_samples_[middle_index].encoder);
+ offset_samples_.push_back(sample.pot - sample.encoder);
} else {
- offset_samples_[samples_idx_] = buffered_samples_[middle_index].pot -
- buffered_samples_[middle_index].encoder;
+ offset_samples_[samples_idx_] = sample.pot - sample.encoder;
}
// Drop the oldest sample when we run this function the next time around.
samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
- double pot_relative_encoder_offset_sum = 0.0;
- for (size_t i = 0; i < offset_samples_.size(); ++i) {
- pot_relative_encoder_offset_sum += offset_samples_[i];
- }
pot_relative_encoder_offset_ =
- pot_relative_encoder_offset_sum / offset_samples_.size();
+ ::std::accumulate(offset_samples_.begin(), offset_samples_.end(), 0.0) /
+ offset_samples_.size();
- offset_ = Wrap(buffered_samples_[middle_index].encoder +
- pot_relative_encoder_offset_,
- average_relative_to_absolute_offset +
- buffered_samples_[middle_index].encoder,
+ offset_ = Wrap(sample.encoder + pot_relative_encoder_offset_,
+ average_relative_to_absolute_offset + sample.encoder,
constants_.one_revolution_distance) -
- buffered_samples_[middle_index].encoder;
+ sample.encoder;
if (offset_ready()) {
if (!zeroed_) {
first_offset_ = offset_;
@@ -460,8 +442,8 @@
position_ = offset_ + info.encoder;
}
-PotAndAbsEncoderZeroingEstimator::State
-PotAndAbsEncoderZeroingEstimator::GetEstimatorState() const {
+PotAndAbsoluteEncoderZeroingEstimator::State
+PotAndAbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
State r;
r.error = error_;
r.zeroed = zeroed_;
diff --git a/frc971/zeroing/zeroing.h b/frc971/zeroing/zeroing.h
index 07cc43a..4d4f13d 100644
--- a/frc971/zeroing/zeroing.h
+++ b/frc971/zeroing/zeroing.h
@@ -215,12 +215,12 @@
// Estimates the position with an absolute encoder which also reports
// incremental counts, and a potentiometer.
-class PotAndAbsEncoderZeroingEstimator
+class PotAndAbsoluteEncoderZeroingEstimator
: public ZeroingEstimator<PotAndAbsolutePosition,
- constants::PotAndAbsoluteEncoderZeroingConstants,
- AbsoluteEstimatorState> {
+ constants::PotAndAbsoluteEncoderZeroingConstants,
+ PotAndAbsoluteEncoderEstimatorState> {
public:
- explicit PotAndAbsEncoderZeroingEstimator(
+ explicit PotAndAbsoluteEncoderZeroingEstimator(
const constants::PotAndAbsoluteEncoderZeroingConstants &constants);
// Resets the internal logic so it needs to be re-zeroed.
diff --git a/frc971/zeroing/zeroing_test.cc b/frc971/zeroing/zeroing_test.cc
index 7948bf4..2a35764 100644
--- a/frc971/zeroing/zeroing_test.cc
+++ b/frc971/zeroing/zeroing_test.cc
@@ -39,7 +39,7 @@
}
void MoveTo(PositionSensorSimulator *simulator,
- PotAndAbsEncoderZeroingEstimator *estimator,
+ PotAndAbsoluteEncoderZeroingEstimator *estimator,
double new_position) {
PotAndAbsolutePosition sensor_values_;
simulator->MoveTo(new_position);
@@ -338,7 +338,7 @@
sim.Initialize(start_pos, index_diff / 3.0, 0.0,
constants.measured_absolute_position);
- PotAndAbsEncoderZeroingEstimator estimator(constants);
+ PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
MoveTo(&sim, &estimator, start_pos);
@@ -366,7 +366,7 @@
sim.Initialize(start_pos, index_diff / 3.0, 0.0,
constants.measured_absolute_position);
- PotAndAbsEncoderZeroingEstimator estimator(constants);
+ PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
// We tolerate a couple NANs before we start.
PotAndAbsolutePosition sensor_values;
@@ -402,7 +402,7 @@
sim.Initialize(start_pos, index_diff / 3.0, 0.0,
constants.measured_absolute_position);
- PotAndAbsEncoderZeroingEstimator estimator(constants);
+ PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
MoveTo(&sim, &estimator, start_pos + i * index_diff);
@@ -419,7 +419,7 @@
PotAndAbsoluteEncoderZeroingConstants constants{
kSampleSize, 1, 0.3, 0.1, kMovingBufferSize, kIndexErrorFraction};
- PotAndAbsEncoderZeroingEstimator estimator(constants);
+ PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
PotAndAbsolutePosition sensor_values;
sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
diff --git a/third_party/pycrc/.gitignore b/third_party/pycrc/.gitignore
new file mode 100644
index 0000000..a72d066
--- /dev/null
+++ b/third_party/pycrc/.gitignore
@@ -0,0 +1,10 @@
+.svn
+.*.swp
+*.pyc
+*.pyo
+__pycache__
+build
+doc/pycrc.1
+doc/pycrc.html
+doc/docbook.css
+test/pycrc_files.tar.gz
diff --git a/third_party/pycrc/.travis.yml b/third_party/pycrc/.travis.yml
new file mode 100644
index 0000000..3c04ca9
--- /dev/null
+++ b/third_party/pycrc/.travis.yml
@@ -0,0 +1,15 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+ - "3.2"
+ - "3.3"
+ - "3.4"
+ - "3.5"
+ - "3.6"
+
+install:
+ python setup.py install
+
+script:
+ python test/test.py -av
diff --git a/third_party/pycrc/AUTHORS b/third_party/pycrc/AUTHORS
new file mode 100644
index 0000000..e81101d
--- /dev/null
+++ b/third_party/pycrc/AUTHORS
@@ -0,0 +1,14 @@
+Thomas Pircher: main developer.
+
+Matthias Urlichs: removed the unused 'direct' parameter and added a fix not to
+ recurse into main() when an unknown algorithm is selected.
+
+Marti Raudsepp: improved spacing for the table-driven algorithm.
+
+Stephan Brumme: his implementation of the slice-by-x algorithm was used as a
+ basis for the implementation in pycrc.
+
+Danjel McGougan: whose Universal Crc project was highly influential in the
+ implementation of widths < 8 in the table-driven algorithm.
+
+André Hartmann, ashelly and others for minor fixes.
diff --git a/third_party/pycrc/CHANGELOG.md b/third_party/pycrc/CHANGELOG.md
new file mode 100644
index 0000000..a086a96
--- /dev/null
+++ b/third_party/pycrc/CHANGELOG.md
@@ -0,0 +1,511 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+For a detailed list of changes see the [pycrc GitHub][pycrc github] page.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+
+
+## [v0.9.1] - 2017-09-09
+
+### Added
+- Added setup.py script to install pycrc on the system, if desired.
+- Added checks about possibly unsuitable polynomials. Use the --force-poly
+ option to override the error message.
+
+### Changed
+- Completely rewritten the code generator back-end. The new back-end is more
+ flexible and allows for a better optimisation of the generated expressions.
+- Moved the python code under the `pycrc` directory and changed how the pycrc
+ sub-modules are included. Before one would write `import crc_xxx`, now one
+ would write `import pycrc.xxx`.
+- New license for the documentation:
+ [Creative Commons Attribution-Share Alike 4.0 Unported License][CC-BY-SA-4.0].
+
+### Fixed
+- Fixed binary files handling with Python 2.7.
+ Fixes #11. Thanks to James Bowman.
+- Fixed some spelling. Thanks to Frank (ftheile) and ashelly.
+- Fixed Header Guard generation. Don't use underscores to start the header
+ guard name. Thanks to Andre Hartmann.
+
+
+## [v0.9] - 2016-01-06
+
+### Added
+- Added Stephan Brumme to the `AUTHORS` file as his implementation of the
+ slice-by algorithm is the basis for pycrc's implementation.
+- Added a new option `--slice-by`. This option is still experimental and
+ limited in its use.
+
+### Changed
+- Documented the experimental `--slice-by option`.
+- Simplified the implementation where Width is less than 8 bits.
+- Run the code through `pylint`.
+- __API change__: changed the names of the member variables from `CamelCase` to the
+ format suggested in `PEP 0008` (lowercase letters and words separated by
+ underscore).
+
+### Fixed
+- Suppressed the `crc_reflect` function where not needed. Addresses part of #8.
+ Thanks to Craig McQueen.
+- Allow strings with values greater than 0x80 in `--check-hexstring`.
+- When the CRC width is less than 8 then the `bit-by-bit` algorithm needs to
+ apply the CRC mask to the final value.
+ Fixes #10. Thanks to Steve Geo.
+- Fixed the initial value of the 16-bit `CCITT` algorithm. Renamed the model
+ from `ccitt` to `crc-16-ccitt`.
+ Fixes #7. Thanks to Craig McQueen.
+
+
+## [v0.8.3] - 2015-08-31
+
+### Changed
+- pycrc has a new [homepage][pycrc home].
+- The [issue tracker on GitHub][pycrc issues]
+ is now advertised as the preferred issue tracker.
+- Applied some minor optimisations to the generated table-driven code.
+- Belatedly added an authors file. Should I have forgotten to mention someone
+ please don't hesitate to send a mail.
+- Upgraded documentation to DocBook 5.
+- Removed sourceforge mailing list from `README.md` in an effort to move pycrc
+ away from sourceforge.
+- Removed the experimental `--bitwise-expression` option to facilitate
+ restructuring of the code.
+- The preferred format for the input data for the Python API is now a byte
+ array. But if a string is supplied it is decoded as UTF-8 string.
+ Alternative formats are not supported and must be passed to the functions
+ as byte arrays.
+- Changed the signature of the `crc_update()` function: the data argument is
+ now a pointer to void to improve compatibility with C++.
+ Thanks to Kamil Szczygieł.
+ This closes GitHub issue #4.
+
+
+## [v0.8.2] - 2014-12-04
+
+### Changed
+- Smaller code cleanups.
+- Stated more clearly that the bitwise expression algorithm is experimental in
+ the documentation.
+- Fixed a typo in the documentation.
+ The description previously stated:
+ "The reflected value of 0xa3 (10100010b) is 0x45 (01000101b)"
+ but this should be:
+ "The reflected value of 0xa2 (10100010b) is 0x45 (01000101b)"
+ Thanks to Andreas Nebenfuehr for reporting the mistake.
+- Small cleanups.
+ Added a tests for special cases. For now, added `crc-5` with non-inverted
+ input. This test is currently failing.
+- Removed warning about even polynomials. As Lars Pötter rightly pointed out,
+ polynomials may be even.
+ Added a caveat emptor about even polinomials in the documentation.
+
+### Fixed
+- The table-driven code for polynomials of width < 8 using a table index
+ width < 8 was producing a wrong checksum.
+ Thanks to Radosław Gancarz.
+- Updated the generated code to cope with big Widths (>32 bits) on 32 bit
+ processors.
+ Since C89 does not give a way to specify the minimum length of a data type,
+ the test does not validate C89 code using Widths > 32.
+ For C99, the `uint_fastN_t` data types are used or long long, if the Width is
+ unknown.
+
+
+## [v0.8.1] - 2013-05-17
+
+### Changed
+- Updated [qm.py from GitHub][qm github].
+- Explicitly stated that the output of pycrc is not considered a substantial
+ part of the code of pycrc in `README.md`.
+- Re-organised the symbol table: grouped the code by functionality, not by
+ algorithm.
+- The input to the CRC routines can now be bytes or strings.
+- Minor formatting change in the manpage.
+- Better python3 compatibility.
+- Added the files generated with the `bwe` algorithm to `check_files.sh`.
+
+### Fixed
+- Fixed a bug in the handling of hexstrings in Python3.
+ Thanks to Matthias Kuehlewein.
+- Added `--check-hexstring` to the tests.
+- Remove obsolete and unused `direct` parameter.
+ Merge pull request #2 from smurfix/master.
+ Thanks to Matthias Urlichs.
+- Don't recurse into main() when an unknown algorithm is selected.
+ Merge pull request #2 from smurfix/master.
+ Thanks to Matthias Urlichs.
+
+
+## [v0.8] - 2013-01-04
+
+### Added
+- Merged (private) bitwise-expression branch to main.
+ This adds the highly experimental `bitwise-expression` (`bwe`) code generator
+ target, which might one day be almost as fast as the table-driven code but
+ much smaller.
+ At the moment the generated code is bigger and slower than any other
+ algorithm, so use at your own risk.
+
+### Changed
+- Now it is possible to specify the `--include` option multiple times.
+- Completely revisited and reworked the documentation.
+- Updated the command line help screen with more useful command descriptions.
+- Removed the `-*- coding: Latin-1 -*-` string.
+- Updated the copyright year to 2013.
+- Renamed abbreviations to `bbb`, `bbf`, `tbl`.
+- It is possible now to abbreviate the algorithms (`bbb` for `bit-by-bit`,
+ `bbf` for `bit-by-bit-fast` and `tbl` for `table-driven`).
+ Added the list of supported CRC models to the error message when an
+ unknown model parameter was supplied.
+- Documented the possibility to abbreviate the algorithms. Minor
+ improvements in the documentation.
+- Added a note to `README.md` that version 0.7.10 of pycrc is the last one
+ known to work with Python 2.4.
+- Updated a link to the list of CRC models.
+- Renamed i`README` to `README.md`.
+- Updated link to the [Catalogue of parametrised CRC algorithms][crc catalogue].
+
+
+## [v0.7.11] - 2012-10-20
+
+### Changed
+- Improved Python3 compatibility. pycrc now requires Python 2.6 or later.
+- Added a test for compiled standard models.
+
+### Fixed
+- Fixed a wrong `check` value of the `crc-64-jones` model.
+- Don't use `snprintf()` with `c89` code, changed to `sprintf()`.
+- Deleted `test.sh` shell script and replaced it with `test.py`.
+
+
+## [v0.7.10] - 2012-02-13
+
+### Added
+- Added the models `crc-12-3gpp`, `crc-16-genibus`, `crc-32-bzip2` and `crc-64-xz`.
+ Taken from [Greg Cook's Catalogue of parametrised CRC algorithms][crc catalogue].
+
+### Changed
+- Bad-looking C code generated; make sure the `bit-by-bit`(`-fast`) code does not
+ contain two instructions on one line. Thanks to "intgr" for the fix.
+- Some small code clean-up: use `set()` when appropriate.
+
+### Fixed
+- Fixed a mistake in the man page that still used the old model name
+ `crc-32mpeg` instead of `crc-32-mpeg`. Thanks to Marek Erban.
+
+
+## [v0.7.9] - 2011-12-08
+
+### Fixed
+- Fixed a bug in the generated C89 code that included `stdint.h`.
+ Thanks to Frank (ftheile).
+ Closes issue 3454356.
+- Fixed a bug in the generated C89 code when using a 64 bit CRC.
+- Using the `--verbose` option made pycrc quit without error message.
+
+
+## [v0.7.8] - 2011-07-10
+
+### Changed
+- When generating C code for the C89 or ANSI standard, don't include `stdint.h`.
+ This closes issue 3338930
+- If no output file name is given while generating a C file, then pycrc will
+ `#include` a hypothetical `pycrc.h` file instead of a `stdout.h` file.
+ Also, added a comment on that line to make debugging easier.
+ Closes issue 3325109.
+- Removed unused variable `this_option_optind` in the generated option parser.
+
+
+## [v0.7.7] - 2011-02-11
+
+### Changed
+- Updated the copyright year.
+ Fixed some coding style issues found by `pylint` and `pychecker`.
+
+### Fixed
+- Substituted the deprecated function `atoi()` with `int()`. Closes issue 3136566.
+ Thanks to Tony Smith.
+- Updated the documentation using Windows-style calls to the Python interpreter.
+
+
+## [v0.7.6] - 2010-10-21
+
+### Changed
+- Rewritten macro parser from scratch. Simplified the macro language.
+- Changed a simple division (/) to a integer division (//) for Python3
+ compatibility.
+
+### Fixed
+- Fixed a minor bug in the command line parsing of the generated main function.
+
+
+## [v0.7.5] - 2010-03-28
+
+### Added
+- Python implementation of the `table-driven` algorithm can handle widths less
+ than 8.
+- Suppressed warnings of unused cfg structure on partially defined models.
+
+### Removed
+- Removed the half-baken and confusing `--direct` option.
+
+### Changed
+- C/C++ code can now be generated for the table-driven algorithm with widths
+ that are not byte-aligned or less than 8.
+ This feature was heavily inspired by a similar feature in Danjel McGougan's
+ [Universal Crc][universal crc].
+- __API change__: introduced new variable `crc_shift`, member of the `crc_cfg_t`
+ structure, which must be initialised manually when the width was undefined
+ during C/C++ code generation.
+- Minor code cleanup.
+
+
+## [v0.7.4] - 2010-01-24
+
+### Added
+- Added `crc-16-modbus`. Closes issue 2896611.
+
+### Changed
+- Changed the `xor-in` value of the `crc-64-jones` model.
+- Set xmodem parameters equal to the `zmodem` parameters.
+- Generate more uniform error messages.
+- Added a warning for even polynomials.
+
+### Fixed
+- Fix for unused variable argv.
+ Closes issue 2893224. Thanks to Marko von Oppen.
+
+
+## [v0.7.3] - 2009-10-25
+
+### Added
+- Added `crc-64-jones` CRC model. Thanks to Waterspirit.
+
+### Changed
+- Renamed `crc-32mpeg` to `crc-32-mpeg`.
+
+
+## [v0.7.2] - 2009-09-30
+
+### Fixed
+- Fixed a bug that caused the result of the Python `table-driven` code not
+ being evaluated at all.
+ Closes issue 2870630. Thanks to Ildar Muslukhov.
+
+
+## [v0.7.1] - 2009-04-05
+
+### Added
+- Added `crc-32mpeg`. Thanks to Thomas Edwards.
+
+
+## [v0.7] - 2009-02-27
+
+### Added
+- Added the `--direct` option.
+- Added `--check-hexstring` option. Closes issue 2545183.
+ Thanks to Arnim Littek.
+- Added a check for extra arguments on the command line.
+ Closes issue 2545185. Thanks to Arnim Littek.
+
+### Changed
+- Added one more example in the documentation.
+
+
+## [v0.6.7] - 2008-12-11
+
+### Changed
+- Run Python's 2to3 script on all files.
+ Check the code on a x64 platform.
+- Fixed a bug that raised an exception when an unknown model was selected.
+
+
+## [v0.6.6] - 2008-06-05
+
+### Changed
+- New license for the documentation:
+ [Creative Commons Attribution-Share Alike 3.0 Unported License][CC-BY-SA-3.0].
+
+### Fixed
+- Fixed a bug in the `print_params()` function. Closes issue 1985197.
+ Thanks to Artur Lipowski.
+
+
+## [v0.6.5] - 2008-03-03
+
+### Added
+- Added dallas-1-wire 8 bit CRC.
+- Added `r-crc-16 model` (DECT (cordless digital standard) packets A-field
+ according to ETSI EN 300 175-3 v2.1.1).
+ Thanks to "raimondo".
+- Added `--crc-type` and `--include-file` options.
+- Added new file to handle CRC models.
+
+### Changed
+- Added extern "C" declaration to the generated C header file.
+ Thanks to Nathan Royer.
+- Changed the API to take the CRC model direct as parameter. Deleted the need
+ for an obscure `opt` object.
+
+### Fixed
+- Fixed a problem with the generated code for bit-by-bit-fast algorithms.
+ Thanks to Hans Bacher.
+
+
+## [v0.6.4] - 2007-12-05
+
+### Fixed
+- Fixed a bug in the code generator for the `table-driven`
+ algorithm. Thanks to Tom McDermott. Closes issue 1843774
+
+
+## [v0.6.3] - 2007-10-13
+
+### Added
+- Added new models: `crc-5`, `crc-15`, `crc-16-usb`, `crc-24`, `crc-64`.
+ The new models are taken from Ray Burr's CrcMoose.
+
+### Fixed
+- Fixed some portability problems in the generated code.
+ Thanks to Helmut Bauer. Closes issue 1812894.
+- The option `--check-file` works now with `--width` < 8. Closes issue 1794343.
+- Removed unnecessary restriction on the width when using the `bit-by-bit-fast`
+ algorithm. Closes issue 1794344.
+
+
+## [v0.6.2] - 2007-08-25
+
+### Changed
+- Simplified the table-driven code. Closes issue 1727128.
+- Changed the macro language syntax to a better format.
+- Renamed `crc_code_gen.py` to `crc_parser.py`.
+- Documented the usage of the `crc_*` modules.
+
+### Fixed
+- The parameter to `--check-string` was ignored. Closes issue 1781637.
+
+
+## [v0.6.1] - 2007-08-12
+
+### Added
+- Added test for C89 compilation.
+- Added a test case to loop over the input bytes one by one.
+
+### Removed
+- Deleted obsolete options.
+
+### Changed
+- Tidied up the documentation.
+ Code cleanup.
+
+### Fixed
+- Bugfix in the source code generator for C89:
+ Compilation error due to mismatch of parameters in the `crc_finalize`
+ funtion.
+- Changes related to 919107: Code generator includes `reflect()` function even
+ if not needed.
+- Fixed a typo in the C89 source code generator.
+ Thanks to Helmut Bauer.
+
+
+## [v0.6] - 2007-05-21
+
+### Added
+- Added the `--std` option to generate C89 (ANSI) compliant code.
+- Added a new check to the test script which validate all possible
+ combination of undefined parameters.
+- Made the generated main function cope with command line arguments.
+- Added the `--generate` table option.
+- Added a template engine for the code generation. Split up `pycrc.py` into
+ smaller modules.
+- Added obsolete options again tor legacy reasons.
+- Added a better handling of the `--model` parameter.
+
+### Changed
+- Reduced the size of the symbol table by re-arranging items.
+- Changed licence to the MIT licence. This makes the additional clause for
+ generated source code obsolete.
+ Changed all command line options with underscores to hyphen (e.g.
+ `table_driven` becomes `table-driven`).
+ Added the option `--generate` which obsoletes the old options `--generate_c`
+ `--generate_h` etc.
+
+
+## [v0.5] - 2007-03-25
+
+### Fixed
+- Fixed bug 1686404: unhandled exception when called with both options
+ `--table_idx_width` and `--check_file`.
+- Eliminated useless declaration of `crc_reflect`, when not used.
+- Corrected typos in the documentation.
+
+
+## [v0.4] - 2007-01-26
+
+### Added
+- Added more parameter sets (now supported: `crc-8`, `crc-16`, `citt`, `kermit`,
+ `x-25`, `xmodem`, `zmodem`, `crc-32`, `crc-32c`, `posix`, `jam`, `xfer`) from
+ [Greg Cook's Catalogue of parametrised CRC algorithms][crc catalogue].
+- Added Doxygen documentation strings to the functions.
+- Added the `--symbol_prefix` option.
+- Added the `--check_file` option.
+- Added a non-regression test on the generated C source.
+
+### Changed
+- Eliminated needless documentation of not generated functions.
+- Many corrections to the manual (thanks Francesca) Documented the new
+ parameter sets.
+- Added some new tests, disabled the random loop.
+
+### Fixed
+- Corrected many typos and bad phrasing (still a lot to do) Documented the
+ `--symbol_prefix` option.
+
+
+## [v0.3] - 2007-01-14
+
+### Added
+- First public release pycrc v0.3
+
+
+
+[Unreleased]: https://github.com/tpircher/pycrc
+[v0.9.1]: https://github.com/tpircher/pycrc/releases/tag/v0.9.1
+[v0.9]: https://github.com/tpircher/pycrc/releases/tag/v0.9
+[v0.8.3]: https://github.com/tpircher/pycrc/releases/tag/v0.8.3
+[v0.8.2]: https://github.com/tpircher/pycrc/releases/tag/v0.8.2
+[v0.8.1]: https://github.com/tpircher/pycrc/releases/tag/v0.8.1
+[v0.8]: https://github.com/tpircher/pycrc/releases/tag/v0.8
+[v0.7.11]: https://github.com/tpircher/pycrc/releases/tag/v0.7.11
+[v0.7.10]: https://github.com/tpircher/pycrc/releases/tag/v0.7.10
+[v0.7.9]: https://github.com/tpircher/pycrc/releases/tag/v0.7.9
+[v0.7.8]: https://github.com/tpircher/pycrc/releases/tag/v0.7.8
+[v0.7.7]: https://github.com/tpircher/pycrc/releases/tag/v0.7.7
+[v0.7.6]: https://github.com/tpircher/pycrc/releases/tag/v0.7.6
+[v0.7.5]: https://github.com/tpircher/pycrc/releases/tag/v0.7.5
+[v0.7.4]: https://github.com/tpircher/pycrc/releases/tag/v0.7.4
+[v0.7.3]: https://github.com/tpircher/pycrc/releases/tag/v0.7.3
+[v0.7.2]: https://github.com/tpircher/pycrc/releases/tag/v0.7.2
+[v0.7.1]: https://github.com/tpircher/pycrc/releases/tag/v0.7.1
+[v0.7]: https://github.com/tpircher/pycrc/releases/tag/v0.7
+[v0.6.7]: https://github.com/tpircher/pycrc/releases/tag/v0.6.7
+[v0.6.6]: https://github.com/tpircher/pycrc/releases/tag/v0.6.6
+[v0.6.5]: https://github.com/tpircher/pycrc/releases/tag/v0.6.5
+[v0.6.4]: https://github.com/tpircher/pycrc/releases/tag/v0.6.4
+[v0.6.3]: https://github.com/tpircher/pycrc/releases/tag/v0.6.3
+[v0.6.2]: https://github.com/tpircher/pycrc/releases/tag/v0.6.2
+[v0.6.1]: https://github.com/tpircher/pycrc/releases/tag/v0.6.1
+[v0.6]: https://github.com/tpircher/pycrc/releases/tag/v0.6
+[v0.5]: https://github.com/tpircher/pycrc/releases/tag/v0.5
+[v0.4]: https://github.com/tpircher/pycrc/releases/tag/v0.4
+[v0.3]: https://github.com/tpircher/pycrc/releases/tag/v0.3
+
+[pycrc home]: https://pycrc.org
+[pycrc github]: https://github.com/tpircher/pycrc
+[pycrc issues]: https://github.com/tpircher/pycrc/issues
+[crc catalogue]: http://regregex.bbcmicro.net/crc-catalogue.htm
+[universal crc]: http://mcgougan.se/universal_crc/
+[qm github]: https://github.com/tpircher/quine-mccluskey
+[CC-BY-SA-3.0]: https://creativecommons.org/licenses/by-sa/3.0/
+[CC-BY-SA-4.0]: https://creativecommons.org/licenses/by-sa/4.0/
diff --git a/third_party/pycrc/COPYING b/third_party/pycrc/COPYING
new file mode 100644
index 0000000..9f90d03
--- /dev/null
+++ b/third_party/pycrc/COPYING
@@ -0,0 +1,19 @@
+Copyright (c) 2006-2015, Thomas Pircher <tehpeh-web@tty1.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/third_party/pycrc/README.md b/third_party/pycrc/README.md
new file mode 100644
index 0000000..cab802b
--- /dev/null
+++ b/third_party/pycrc/README.md
@@ -0,0 +1,80 @@
+
+
+ _ __ _ _ ___ _ __ ___
+ | '_ \| | | |/ __| '__/ __|
+ | |_) | |_| | (__| | | (__
+ | .__/ \__, |\___|_| \___|
+ |_| |___/
+
+ https://pycrc.org
+
+
+pycrc is a free, easy to use Cyclic Redundancy Check (CRC) calculator and C
+source code generator.
+
+
+
+System Requirements
+===================
+
+pycrc requires Python 2.6 or later. Python 3.x is supported.
+The last version compatible with Python 2.4 is pycrc v0.7.10.
+
+
+Running pycrc
+=============
+
+This program doesn't need to be installed to be called. The script can be
+executed from the source directory.
+Simply call the python interpreter with the script as parameter:
+
+ python pycrc.py [options]
+
+On a UNIX-like system, you can make the pycrc.py wrapper script executable and
+call it like a normal binary:
+
+ chmod +x pycrc.py
+ ./pycrc.py [options]
+
+
+Installation
+============
+
+Install pycrc (if required) using the setup.py script:
+
+ python setup.py install
+
+
+Getting help
+============
+
+If you are new to pycrc and want to generate C code, start with
+[the tutorial](https://pycrc.org/tutorial.html).
+
+The [pycrc manual page](https://pycrc.org/pycrc.html) explains the command line
+options in some detail and also gives some more examples how to use pycrc.
+
+If you have found a bug in pycrc or want to request a feature please take the
+time and submit it to the
+[issue tracker](https://github.com/tpircher/pycrc/issues).
+Thanks for your help.
+
+Also see the [frequently asked questions](https://pycrc.org/faq.html).
+
+
+Feedback
+========
+
+If you like pycrc, let me know and drop me a note. If you don't like pycrc let
+me know what you don't like and why. In both cases, I would really appreciate
+some [feed back](https://sourceforge.net/projects/pycrc/reviews/).
+If you want some idea how to say thanks for this software, please have a look
+[here](https://www.tty1.net/say-thanks_en.html).
+
+
+Copyright of the generated source code
+======================================
+
+The code generated by pycrc is not considered a substantial portion of the
+software, therefore the licence does not cover the generated code, and the
+author of pycrc will not claim any copyright on the generated code.
diff --git a/third_party/pycrc/doc/Makefile b/third_party/pycrc/doc/Makefile
new file mode 100644
index 0000000..a0f2581
--- /dev/null
+++ b/third_party/pycrc/doc/Makefile
@@ -0,0 +1,24 @@
+HTML_PARAMS = style-html.xsl
+MAN_PARAMS = style-man.xsl
+
+source = pycrc.xml
+targets = $(source:.xml=.html) $(source:.xml=.1)
+
+all: $(targets)
+
+.PHONY: clean
+clean:
+ $(RM) $(targets)
+
+.PHONY: check
+check:
+ xmllint --valid --noout $(source)
+
+%.html: %.xml $(HTML_PARAMS)
+ saxon-xslt -o $@ $^
+
+%.1: %.xml $(MAN_PARAMS)
+ saxon-xslt -o $@ $^
+
+%.txt: %.html
+ links -dump -no-numbering -no-references $< > $@
diff --git a/third_party/pycrc/doc/pycrc.xml b/third_party/pycrc/doc/pycrc.xml
new file mode 100644
index 0000000..9f5a4a2
--- /dev/null
+++ b/third_party/pycrc/doc/pycrc.xml
@@ -0,0 +1,681 @@
+<?xml version='1.0' encoding="utf-8"?>
+
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V5.1//EN"
+ "http://www.oasis-open.org/docbook/xml/5.0b5/dtd/docbook.dtd" [
+<!ENTITY project_version "0.9.1">
+<!ENTITY project_homepage "https://pycrc.org">
+<!ENTITY project_models "https://pycrc.org/models.html">
+<!ENTITY date "2017-08-11">
+<!ENTITY author_firstname "Thomas">
+<!ENTITY author_surname "Pircher">
+<!ENTITY author_email "tehpeh-web@tty1.net">
+<!ENTITY bit-by-bit "bit-by-bit">
+<!ENTITY bbb "bbb">
+<!ENTITY bit-by-bit-fast "bit-by-bit-fast">
+<!ENTITY bbf "bbf">
+<!ENTITY table-driven "table-driven">
+<!ENTITY tbl "tbl">
+<!ENTITY slice-by "slice-by">
+<!ENTITY width "Width">
+<!ENTITY poly "Polynomial">
+<!ENTITY reflect_in "ReflectIn">
+<!ENTITY xor_in "XorIn">
+<!ENTITY reflect_out "ReflectOut">
+<!ENTITY xor_out "XorOut">
+<!ENTITY check "Check">
+]>
+
+
+<refentry xmlns='http://docbook.org/ns/docbook' version="5.0" xml:id="pycrc">
+ <info>
+ <title>pycrc</title>
+ <productname>pycrc</productname>
+ <productnumber>&project_version;</productnumber>
+ <author>
+ <personname>
+ <firstname>&author_firstname;</firstname>
+ <surname>&author_surname;</surname>
+ </personname>
+ <contrib>Author of pycrc and this manual page.</contrib>
+ <email>&author_email;</email>
+ </author>
+ <date>&date;</date>
+ </info>
+
+ <refmeta>
+ <refentrytitle>pycrc</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>pycrc</refname>
+ <refpurpose>a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator.</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>python pycrc.py</command>
+ <arg>OPTIONS</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+ <link xlink:href="&project_homepage;">pycrc</link>
+ is a CRC reference implementation in Python and a C source code generator for parametrised CRC models.
+ The generated C source code can be optimised for simplicity,
+ speed or small memory footprint, as required on small embedded systems.
+
+ The following operations are implemented:
+ <itemizedlist>
+ <listitem>
+ <para>calculate the checksum of a string (ASCII or hex)</para>
+ </listitem>
+ <listitem>
+ <para>calculate the checksum of a file</para>
+ </listitem>
+ <listitem>
+ <para>generate the header and source files for a C implementation.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ pycrc supports the following variants of the CRC algorithm:
+ <itemizedlist>
+ <listitem>
+ <para><replaceable>&bit-by-bit;</replaceable> or <replaceable>&bbb;</replaceable>:
+ the basic algorithm which operates individually on every bit of the augmented message
+ (i.e. the input data with <replaceable>&width;</replaceable> zero bits added at the end).
+ This algorithm is a straightforward implementation of the basic polynomial division and
+ is the easiest one to understand, but it is also the slowest one among all possible
+ variants.
+ </para>
+ </listitem>
+ <listitem>
+ <para><replaceable>&bit-by-bit-fast;</replaceable> or <replaceable>&bbf;</replaceable>:
+ a variation of the simple <replaceable>&bit-by-bit;</replaceable> algorithm.
+ This algorithm still iterates over every bit of the message, but does not augment
+ it (does not add <replaceable>&width;</replaceable> zero bits at the end).
+ It gives the same result as the <replaceable>&bit-by-bit;</replaceable> method by
+ carefully choosing the initial value of the algorithm.
+ This method might be a good choice for embedded platforms, where code space is more
+ important than execution speed.
+ </para>
+ </listitem>
+ <listitem>
+ <para><replaceable>&table-driven;</replaceable> or <replaceable>&tbl;</replaceable>:
+ the standard table driven algorithm.
+ This is the fastest variant because it operates on one byte at a time, as opposed to one
+ bit at the time.
+ This method uses a look-up table (usually of 256 elements), which might not be acceptable
+ for small embedded systems. The number of elements in the look-up table can be reduced
+ with the <option>--table-idx-width</option> command line switch.
+ The value of 4 bits for the table index (16 elements in the look-up table) can be a good
+ compromise between execution speed and code size.
+ </para>
+ <para>
+ The <option>--slice-by</option> option enables a variant of the <replaceable>&table-driven;</replaceable>
+ algorithm that operates on 32 bits of data or more at a time rather than 8 bits.
+ This can dramatically speed-up the calculation of the CRC, at the cost of
+ increased code and data size.
+ <emphasis>Note</emphasis>: this option is experimental and not well-tested.
+ Check your results and please raise bugs if you find problems.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>--version</option>
+ </term>
+ <listitem>
+ <para>show the program version number and exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-h</option>
+ </term>
+ <term>
+ <option>--help</option>
+ </term>
+ <listitem>
+ <para>show this help message and exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--verbose</option>
+ </term>
+ <listitem>
+ <para>be more verbose; in particular, print the value of the parameters and the chosen model to <filename>stdout</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--check-string=</option><replaceable>STRING</replaceable>
+ </term>
+ <listitem>
+ <para>calculate the checksum of a string (default: <quote><replaceable>123456789</replaceable></quote>). If the string contains non-ASCII characters then it will be UTF-8 decoded.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--check-hexstring=</option><replaceable>STRING</replaceable>
+ </term>
+ <listitem>
+ <para>calculate the checksum of a hexadecimal number string.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--check-file=</option><replaceable>FILE</replaceable>
+ </term>
+ <listitem>
+ <para>calculate the checksum of a file. If the file contains non-ASCII characters then it will be UTF-8 decoded.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--generate=</option><replaceable>CODE</replaceable>
+ </term>
+ <listitem>
+ <para>generate C source code; choose the type from {<replaceable>h</replaceable>,
+ <replaceable>c</replaceable>, <replaceable>c-main</replaceable>, <replaceable>table</replaceable>}.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--std=</option><replaceable>STD</replaceable>
+ </term>
+ <listitem>
+ <para>specify the C dialect of the generated code from {C89, ANSI, C99}.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--algorithm=</option><replaceable>ALGO</replaceable>
+ </term>
+ <listitem>
+ <para>choose an algorithm from {<replaceable>bit-by-bit</replaceable>, <replaceable>bbb</replaceable>,
+ <replaceable>bit-by-bit-fast</replaceable>, <replaceable>bbf</replaceable>,
+ <replaceable>table-driven</replaceable>, <replaceable>tbl</replaceable>,
+ <replaceable>all</replaceable>}.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--model=</option><replaceable>MODEL</replaceable>
+ </term>
+ <listitem>
+ <para>choose a parameter set from
+ {<replaceable>crc-5</replaceable>,
+ <replaceable>crc-8</replaceable>,
+ <replaceable>dallas-1-wire</replaceable>,
+ <replaceable>crc-12-3gpp</replaceable>,
+ <replaceable>crc-15</replaceable>,
+ <replaceable>crc-16</replaceable>,
+ <replaceable>crc-16-usb</replaceable>,
+ <replaceable>crc-16-modbus</replaceable>,
+ <replaceable>crc-16-genibus</replaceable>,
+ <replaceable>crc-16-ccitt</replaceable>,
+ <replaceable>r-crc-16</replaceable>,
+ <replaceable>kermit</replaceable>,
+ <replaceable>x-25</replaceable>,
+ <replaceable>xmodem</replaceable>,
+ <replaceable>zmodem</replaceable>,
+ <replaceable>crc-24</replaceable>,
+ <replaceable>crc-32</replaceable>,
+ <replaceable>crc-32c</replaceable>,
+ <replaceable>crc-32-mpeg</replaceable>,
+ <replaceable>crc-32-bzip2</replaceable>,
+ <replaceable>posix</replaceable>,
+ <replaceable>jam</replaceable>,
+ <replaceable>xfer</replaceable>,
+ <replaceable>crc-64</replaceable>,
+ <replaceable>crc-64-jones</replaceable>,
+ <replaceable>crc-64-xz</replaceable>}.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--width=</option><replaceable>NUM</replaceable>
+ </term>
+ <listitem>
+ <para>use <replaceable>NUM</replaceable> bits in the <replaceable>&poly;</replaceable>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--poly=</option><replaceable>HEX</replaceable>
+ </term>
+ <listitem>
+ <para>use <replaceable>HEX</replaceable> as <replaceable>&poly;</replaceable>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--reflect-in=</option><replaceable>BOOL</replaceable>
+ </term>
+ <listitem>
+ <para>reflect the octets in the input message.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--xor-in=</option><replaceable>HEX</replaceable>
+ </term>
+ <listitem>
+ <para>use <replaceable>HEX</replaceable> as initial value.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--reflect-out=</option><replaceable>BOOL</replaceable>
+ </term>
+ <listitem>
+ <para>reflect the resulting checksum before applying the &xor_out; value.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--xor-out=</option><replaceable>HEX</replaceable>
+ </term>
+ <listitem>
+ <para>xor the final CRC value with <replaceable>HEX</replaceable>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--slice-by=</option><replaceable>NUM</replaceable>
+ </term>
+ <listitem>
+ <para>speed-up the &table-driven; calculation by operating on
+ <replaceable>NUM</replaceable> octets of data rather than a
+ single octet at a time.
+ <replaceable>NUM</replaceable> must be one of the values
+ {<replaceable>4</replaceable>, <replaceable>8</replaceable>,
+ <replaceable>16</replaceable>}.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--table-idx-width=</option><replaceable>NUM</replaceable>
+ </term>
+ <listitem>
+ <para>use <replaceable>NUM</replaceable> bits to index the CRC table;
+ <replaceable>NUM</replaceable> must be one of the values
+ {<replaceable>1</replaceable>, <replaceable>2</replaceable>,
+ <replaceable>4</replaceable>, <replaceable>8</replaceable>}.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--force-poly</option>
+ </term>
+ <listitem>
+ <para>override any errors about possibly unsuitable
+ polynoms. pycrc does not allow even polynoms or
+ polynoms that are wider than &width;. Use this option
+ to override the error, if you know what you are
+ doing.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--symbol-prefix=</option><replaceable>STRING</replaceable>
+ </term>
+ <listitem>
+ <para>when generating source code, use <replaceable>STRING</replaceable>
+ as prefix to the exported C symbols.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--crc-type=</option><replaceable>STRING</replaceable>
+ </term>
+ <listitem>
+ <para>when generating source code, use <replaceable>STRING</replaceable> as crc_t type.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--include-file=</option><replaceable>FILE</replaceable>
+ </term>
+ <listitem>
+ <para>when generating source code, include also <replaceable>FILE</replaceable> as header file.
+ This option can be specified multiple times.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-o</option><replaceable>FILE</replaceable>
+ </term>
+ <term>
+ <option>--output=</option><replaceable>FILE</replaceable>
+ </term>
+ <listitem>
+ <para>write the generated code to <replaceable>FILE</replaceable> instead of <filename>stdout</filename>.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>The CRC Parametric Model</title>
+ <para>
+ The parametric model follows Ross N. Williams' convention described in
+ <link xlink:href="http://www.ross.net/crc/crcpaper.html">A Painless Guide to CRC Error Detection Algorithms</link>,
+ often called the Rocksoft Model.
+ Since most people are familiar with this kind of parameters, pycrc follows this convention, described as follows:
+ <glosslist>
+ <glossentry>
+ <glossterm><replaceable>&width;</replaceable></glossterm>
+ <glossdef>
+ <para>
+ The number of significant bits in the CRC <replaceable>&poly;</replaceable>,
+ excluding the most significant 1.
+ This will also be the number of bits in the final CRC result.
+ In previous versions of pycrc only multiples of 8 could be used as
+ <replaceable>&width;</replaceable> for the <replaceable>&table-driven;</replaceable> algorithm.
+ As of version 0.7.5 any value is accepted for <replaceable>&width;</replaceable> for all algorithms.
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><replaceable>&poly;</replaceable></glossterm>
+ <glossdef>
+ <para>
+ The unreflected polynomial of the CRC algorithm.
+ </para>
+ <para>
+ The <replaceable>&poly;</replaceable> may be specified in its standard form,
+ i.e. with bit <replaceable>&width;</replaceable>+1 set to 1, but the most significant
+ bit may also be omitted.
+ For example, both numbers 0x18005 and 0x8005 are accepted for a 16-bit
+ <replaceable>&poly;</replaceable>.
+ </para>
+ <para>
+ Most polynomials used in real world applications are odd (the least significant
+ bit is 1), but there are some good even ones.
+ pycrc allows the use of even polynomials with the <option>--force-poly</option>
+ option.
+ Some even polynomials may yield incorrect checksums depending on the used algorithm.
+ Use at your own risk and if at all possible use a well-known <replaceable>MODEL</replaceable> above.
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><replaceable>&reflect_in;</replaceable></glossterm>
+ <glossdef>
+ <para>
+ Reflect the octets of the message before processing them.
+ </para>
+ <para>
+ A word is reflected or reversed by <quote>flipping</quote> its bits around the
+ mid-point of the word.
+ The most significant bit of the word is moved to the least significant position,
+ the second-most significant bit is moved to the second-least significant position
+ and so on.
+ The reflected value of 0xa2 (10100010b) is 0x45 (01000101b), for example.
+ </para>
+ <para>
+ Some CRC algorithms can be implemented more efficiently in a bit reversed version,
+ that's why many of the standard CRC models use reflected input octets.
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><replaceable>&xor_in;</replaceable></glossterm>
+ <glossdef>
+ <para>
+ The initial value (usually all 0 or all 1) for algorithms which operate on the
+ non-augmented message, that is, any algorithm other than the
+ <replaceable>&bit-by-bit;</replaceable> one.
+ This value can be interpreted as a value which will be XOR-ed into the CRC register
+ after <replaceable>&width;</replaceable> iterations of the
+ <replaceable>&bit-by-bit;</replaceable> algorithm.
+ This implies that the simple <replaceable>&bit-by-bit;</replaceable> algorithm must
+ calculate the initial value using some sort of reverse CRC algorithm on the
+ <replaceable>&xor_in;</replaceable> value.
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><replaceable>&reflect_out;</replaceable></glossterm>
+ <glossdef>
+ <para>
+ Reflect the final CRC result. This operation takes place before XOR-ing the final CRC
+ value with the <replaceable>&xor_out;</replaceable> parameter.
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><replaceable>&xor_out;</replaceable></glossterm>
+ <glossdef>
+ <para>
+ A value (usually all bits 0 or all 1) which will be XOR-ed to the final CRC value.
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><replaceable>✓</replaceable></glossterm>
+ <glossdef>
+ <para>
+ This value is not exactly a parameter of a model but it is sometimes given together
+ with the Rocksoft Model parameters.
+ It is the CRC value of the parametrised model over the string
+ <quote><replaceable>123456789</replaceable></quote> and
+ can be used as a sanity check for a particular CRC implementation.
+ </para>
+ </glossdef>
+ </glossentry>
+ </glosslist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Code generation</title>
+ <para>
+ In the default configuration, the generated code is strict ISO C99.
+ A minimal set of three functions are defined for each algorithm:
+ <function>crc_init()</function>, <function>crc_update()</function> and <function>crc_finalize()</function>.
+ Depending on the number of parameters given to pycrc, a different interface will be defined.
+ A fully parametrised model has a simpler API, while the generated code for a runtime-specified
+ implementation requires a pointer to a configuration structure as first parameter to all functions.
+ </para>
+ <para>
+ The generated source code uses the type <type>crc_t</type>, which is used throughout the code
+ to hold intermediate results and also the final CRC value.
+ It is defined in the generated header file and its type may be overridden with the
+ <option>--crc-type</option> option.
+ </para>
+
+ <refsect2><title>Fully parametrised models</title>
+ <para>
+ The prototypes of the CRC functions are normally generated by pycrc using the
+ <replaceable>--generate h</replaceable> option.
+ The CRC functions for a fully parametrised model will look like:
+ </para>
+ <funcsynopsis>
+ <funcsynopsisinfo>
+#include <stdlib.h>
+typedef uint16_t crc_t; /* pycrc will use an appropriate size here */
+ </funcsynopsisinfo>
+ <funcprototype>
+ <?dbhtml funcsynopsis-style='ansi'?>
+ <funcdef>crc_t <function>crc_init</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ <funcprototype>
+ <?dbhtml funcsynopsis-style='ansi'?>
+ <funcdef>crc_t <function>crc_update</function></funcdef>
+ <paramdef>crc_t <parameter>crc</parameter></paramdef>
+ <paramdef>const unsigned char *<parameter>data</parameter></paramdef>
+ <paramdef>size_t <parameter>data_len</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <?dbhtml funcsynopsis-style='ansi'?>
+ <funcdef>crc_t <function>crc_finalize</function></funcdef>
+ <paramdef>crc_t <parameter>crc</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <para>
+ The code snippet below shows how to use the generated functions.
+ <programlisting>
+#include "pycrc_generated_crc.h"
+#include <stdio.h>
+
+int main(void)
+{
+ static const unsigned char str1[] = "1234";
+ static const unsigned char str2[] = "56789";
+ crc_t crc;
+
+ crc = crc_init();
+ crc = crc_update(crc, str1, sizeof(str1) - 1);
+ crc = crc_update(crc, str2, sizeof(str2) - 1);
+ /* more calls to crc_update... */
+ crc = crc_finalize(crc);
+
+ printf("0x%lx\n", (long)crc);
+ return 0;
+}
+ </programlisting>
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title>Models with runtime-configurable parameters</title>
+ <para>
+ When the model is not fully defined then the missing parameters are stored in a structure of
+ type <type>crc_cfg_t</type>.
+ If a CRC function requires a value from the <type>crc_cfg_t</type> structure, then the first
+ function argument is always a pointer to that structure.
+ All fields of the configuration structure must be properly initialised before the first call
+ to any CRC function.
+ </para>
+ <para>
+ If the <replaceable>&width;</replaceable> was not specified when the code was generated, then
+ the <type>crc_cfg_t</type> structure will contain three more fields:
+ <parameter>msb_mask</parameter>, <parameter>crc_mask</parameter> and <parameter>crc_shift</parameter>.
+ They are defined for performance reasons and must be initialised to the value given next to the
+ field definition.
+ </para>
+ <para>
+ For example, a completely undefined CRC implementation will generate a <type>crc_cfg_t</type>
+ structure as below:
+ <programlisting>
+typedef struct {
+ unsigned int width;
+ crc_t poly;
+ bool reflect_in;
+ crc_t xor_in;
+ bool reflect_out;
+ crc_t xor_out;
+
+ // internal parameters
+ crc_t msb_mask; // initialise as (crc_t)1u << (cfg->width - 1)
+ crc_t crc_mask; // initialise as (cfg->msb_mask - 1) | cfg->msb_mask
+ unsigned int crc_shift; // initialise as cfg->width < 8 ? 8 - cfg->width : 0
+} crc_cfg_t;
+ </programlisting>
+ </para>
+ <para>
+ <parameter>msb_mask</parameter> is a bitmask with the most significant bit of a
+ <replaceable>&width;</replaceable> bits wide data type set to 1.
+
+ <parameter>crc_mask</parameter> is a bitmask with all bits of a
+ <replaceable>&width;</replaceable> bits wide data type set to 1.
+
+ <parameter>crc_shift</parameter> is a shift counter that is used when
+ <replaceable>&width;</replaceable> is less than 8.
+ It is the number of bits to shift the CRC register to align its top bit to a byte boundary.
+ </para>
+
+ <para>
+ The file <filename>test/main.c</filename> in the source package of pycrc
+ contains a fully featured example of how to use the generated source code.
+ A shorter, more compact <code>main()</code> function can be generated with the
+ <replaceable>--generate c-main</replaceable> option.
+ This second variant is the better option as it will always output valid code when
+ some of the CRC parameters are known and some are unknown during code generation.
+ </para>
+ </refsect2>
+ </refsect1>
+
+ <refsect1><title>Examples</title>
+ <para>
+ <glosslist>
+ <glossentry>
+ <glossterm>Calculate the CRC-32 checksum of the string <quote>123456789</quote>:</glossterm>
+ <glossdef>
+ <para>
+ <userinput>python pycrc.py --model crc-32 --check-string 123456789</userinput>
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm>Generate the source code of the table-driven algorithm for an embedded application.</glossterm>
+ <glossdef>
+ <para>
+ The table index width of 4 bits ensures a moderate memory usage.
+ To be precise, the size of the resulting table will be <code>16 * sizeof(crc_t)</code>.
+ </para>
+ <para>
+ <userinput>python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate h -o crc.h</userinput>
+ </para>
+ <para>
+ <userinput>python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c -o crc.c</userinput>
+ </para>
+ <para>
+ A variant of the <replaceable>c</replaceable> target is <replaceable>c-main</replaceable>:
+ this target will generate a simple <replaceable>main()</replaceable> function in addition to
+ the CRC functions:
+ </para>
+ <para>
+ <userinput>python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c-main -o crc.c</userinput>
+ </para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm>Generate the CRC table only:</glossterm>
+ <glossdef>
+ <para>
+ <userinput>python pycrc.py --model kermit --generate table -o crc-table.txt</userinput>
+ </para>
+ </glossdef>
+ </glossentry>
+ </glosslist>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ The homepage of pycrc is <link xlink:href="&project_homepage;">&project_homepage;</link>.
+ </para>
+ <para>
+ A list of common CRC models is at <link xlink:href="&project_models;">&project_models;</link>.
+ For a long list of known CRC models, see Greg Cook's
+ <link xlink:href="http://reveng.sourceforge.net/crc-catalogue/">Catalogue of Parameterised CRC Algorithms</link>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Copyright</title>
+ <para>
+ This work is licensed under a
+ <link xlink:href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International</link>.
+ </para>
+ </refsect1>
+</refentry>
diff --git a/third_party/pycrc/doc/style-html.xsl b/third_party/pycrc/doc/style-html.xsl
new file mode 100644
index 0000000..3f9cb89
--- /dev/null
+++ b/third_party/pycrc/doc/style-html.xsl
@@ -0,0 +1,14 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl-ns/xhtml5/docbook.xsl"/>
+
+<xsl:output encoding="UTF-8" indent="no" method="html"/>
+<xsl:param name="default.table.frame" select="none"/>
+<xsl:param name="html.cleanup" select="1"/>
+<xsl:param name="make.valid.html" select="1"/>
+
+<xsl:template name="user.head.content">
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/third_party/pycrc/doc/style-man.xsl b/third_party/pycrc/doc/style-man.xsl
new file mode 100644
index 0000000..639b454
--- /dev/null
+++ b/third_party/pycrc/doc/style-man.xsl
@@ -0,0 +1,6 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl-ns/manpages/docbook.xsl"/>
+
+
+</xsl:stylesheet>
diff --git a/third_party/pycrc/pycrc.py b/third_party/pycrc/pycrc.py
new file mode 100755
index 0000000..2dd1612
--- /dev/null
+++ b/third_party/pycrc/pycrc.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+from pycrc.main import main
+import sys
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/third_party/pycrc/pycrc/__init__.py b/third_party/pycrc/pycrc/__init__.py
new file mode 100644
index 0000000..e0749e4
--- /dev/null
+++ b/third_party/pycrc/pycrc/__init__.py
@@ -0,0 +1,3 @@
+progname = "pycrc"
+version = "0.9.1"
+url = 'https://pycrc.org'
diff --git a/third_party/pycrc/pycrc/__main__.py b/third_party/pycrc/pycrc/__main__.py
new file mode 100644
index 0000000..cefc3bb
--- /dev/null
+++ b/third_party/pycrc/pycrc/__main__.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+from pycrc.main import main
+import sys
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/third_party/pycrc/pycrc/algorithms.py b/third_party/pycrc/pycrc/algorithms.py
new file mode 100644
index 0000000..3ebcebd
--- /dev/null
+++ b/third_party/pycrc/pycrc/algorithms.py
@@ -0,0 +1,235 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+CRC algorithms implemented in Python.
+If you want to study the Python implementation of the CRC routines, then this
+is a good place to start from.
+
+The algorithms Bit by Bit, Bit by Bit Fast and Table-Driven are implemented.
+
+This module can also be used as a library from within Python.
+
+Examples
+========
+
+This is an example use of the different algorithms:
+
+ from pycrc.algorithms import Crc
+
+ crc = Crc(width = 16, poly = 0x8005,
+ reflect_in = True, xor_in = 0x0000,
+ reflect_out = True, xor_out = 0x0000)
+ print("{0:#x}".format(crc.bit_by_bit("123456789")))
+ print("{0:#x}".format(crc.bit_by_bit_fast("123456789")))
+ print("{0:#x}".format(crc.table_driven("123456789")))
+"""
+
+class Crc(object):
+ """
+ A base class for CRC routines.
+ """
+ # pylint: disable=too-many-instance-attributes
+
+ def __init__(self, width, poly, reflect_in, xor_in, reflect_out, xor_out, table_idx_width=None, slice_by=1):
+ """The Crc constructor.
+
+ The parameters are as follows:
+ width
+ poly
+ reflect_in
+ xor_in
+ reflect_out
+ xor_out
+ """
+ # pylint: disable=too-many-arguments
+
+ self.width = width
+ self.poly = poly
+ self.reflect_in = reflect_in
+ self.xor_in = xor_in
+ self.reflect_out = reflect_out
+ self.xor_out = xor_out
+ self.tbl_idx_width = table_idx_width
+ self.slice_by = slice_by
+
+ self.msb_mask = 0x1 << (self.width - 1)
+ self.mask = ((self.msb_mask - 1) << 1) | 1
+ if self.tbl_idx_width != None:
+ self.tbl_width = 1 << self.tbl_idx_width
+ else:
+ self.tbl_idx_width = 8
+ self.tbl_width = 1 << self.tbl_idx_width
+
+ self.direct_init = self.xor_in
+ self.nondirect_init = self.__get_nondirect_init(self.xor_in)
+ if self.width < 8:
+ self.crc_shift = 8 - self.width
+ else:
+ self.crc_shift = 0
+
+
+ def __get_nondirect_init(self, init):
+ """
+ return the non-direct init if the direct algorithm has been selected.
+ """
+ crc = init
+ for dummy_i in range(self.width):
+ bit = crc & 0x01
+ if bit:
+ crc ^= self.poly
+ crc >>= 1
+ if bit:
+ crc |= self.msb_mask
+ return crc & self.mask
+
+
+ def reflect(self, data, width):
+ """
+ reflect a data word, i.e. reverts the bit order.
+ """
+ # pylint: disable=no-self-use
+
+ res = data & 0x01
+ for dummy_i in range(width - 1):
+ data >>= 1
+ res = (res << 1) | (data & 0x01)
+ return res
+
+
+ def bit_by_bit(self, in_data):
+ """
+ Classic simple and slow CRC implementation. This function iterates bit
+ by bit over the augmented input message and returns the calculated CRC
+ value at the end.
+ """
+ # If the input data is a string, convert to bytes.
+ if isinstance(in_data, str):
+ in_data = bytearray(in_data, 'utf-8')
+
+ reg = self.nondirect_init
+ for octet in in_data:
+ if self.reflect_in:
+ octet = self.reflect(octet, 8)
+ for i in range(8):
+ topbit = reg & self.msb_mask
+ reg = ((reg << 1) & self.mask) | ((octet >> (7 - i)) & 0x01)
+ if topbit:
+ reg ^= self.poly
+
+ for i in range(self.width):
+ topbit = reg & self.msb_mask
+ reg = ((reg << 1) & self.mask)
+ if topbit:
+ reg ^= self.poly
+
+ if self.reflect_out:
+ reg = self.reflect(reg, self.width)
+ return (reg ^ self.xor_out) & self.mask
+
+
+ def bit_by_bit_fast(self, in_data):
+ """
+ This is a slightly modified version of the bit-by-bit algorithm: it
+ does not need to loop over the augmented bits, i.e. the Width 0-bits
+ wich are appended to the input message in the bit-by-bit algorithm.
+ """
+ # If the input data is a string, convert to bytes.
+ if isinstance(in_data, str):
+ in_data = bytearray(in_data, 'utf-8')
+
+ reg = self.direct_init
+ for octet in in_data:
+ if self.reflect_in:
+ octet = self.reflect(octet, 8)
+ for i in range(8):
+ topbit = reg & self.msb_mask
+ if octet & (0x80 >> i):
+ topbit ^= self.msb_mask
+ reg <<= 1
+ if topbit:
+ reg ^= self.poly
+ reg &= self.mask
+ if self.reflect_out:
+ reg = self.reflect(reg, self.width)
+ return reg ^ self.xor_out
+
+
+ def gen_table(self):
+ """
+ This function generates the CRC table used for the table_driven CRC
+ algorithm. The Python version cannot handle tables of an index width
+ other than 8. See the generated C code for tables with different sizes
+ instead.
+ """
+ table_length = 1 << self.tbl_idx_width
+ tbl = [[0 for i in range(table_length)] for j in range(self.slice_by)]
+ for i in range(table_length):
+ reg = i
+ if self.reflect_in:
+ reg = self.reflect(reg, self.tbl_idx_width)
+ reg = reg << (self.width - self.tbl_idx_width + self.crc_shift)
+ for dummy_j in range(self.tbl_idx_width):
+ if reg & (self.msb_mask << self.crc_shift) != 0:
+ reg = (reg << 1) ^ (self.poly << self.crc_shift)
+ else:
+ reg = (reg << 1)
+ if self.reflect_in:
+ reg = self.reflect(reg >> self.crc_shift, self.width) << self.crc_shift
+ tbl[0][i] = (reg >> self.crc_shift) & self.mask
+
+ for j in range(1, self.slice_by):
+ for i in range(table_length):
+ tbl[j][i] = (tbl[j - 1][i] >> 8) ^ tbl[0][tbl[j - 1][i] & 0xff]
+ return tbl
+
+
+ def table_driven(self, in_data):
+ """
+ The Standard table_driven CRC algorithm.
+ """
+ # pylint: disable = line-too-long
+
+ # If the input data is a string, convert to bytes.
+ if isinstance(in_data, str):
+ in_data = bytearray(in_data, 'utf-8')
+
+ tbl = self.gen_table()
+
+ if not self.reflect_in:
+ reg = self.direct_init << self.crc_shift
+ for octet in in_data:
+ tblidx = ((reg >> (self.width - self.tbl_idx_width + self.crc_shift)) ^ octet) & 0xff
+ reg = ((reg << (self.tbl_idx_width - self.crc_shift)) ^ (tbl[0][tblidx] << self.crc_shift)) & (self.mask << self.crc_shift)
+ reg = reg >> self.crc_shift
+ else:
+ reg = self.reflect(self.direct_init, self.width)
+ for octet in in_data:
+ tblidx = (reg ^ octet) & 0xff
+ reg = ((reg >> self.tbl_idx_width) ^ tbl[0][tblidx]) & self.mask
+ reg = self.reflect(reg, self.width) & self.mask
+
+ if self.reflect_out:
+ reg = self.reflect(reg, self.width)
+ return reg ^ self.xor_out
+
diff --git a/third_party/pycrc/pycrc/codegen.py b/third_party/pycrc/pycrc/codegen.py
new file mode 100644
index 0000000..4079cc0
--- /dev/null
+++ b/third_party/pycrc/pycrc/codegen.py
@@ -0,0 +1,1559 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+use as follows:
+
+ import pycrc.codegen as cg
+ import pycrc.opt as opt
+ opt = opt.Options()
+ print(cg.CodeGen(opt, '', [
+ 'if (a == b) {',
+ cg.CodeGen(opt, 4*' ', ['print("a equals b\\n");']),
+ '}',
+ ]))
+"""
+
+import pycrc.symtable
+import pycrc.expr as expr
+
+
+class CodeGen(object):
+ """
+ The symbol table class.
+ """
+ def __init__(self, opt, indent, content = []):
+ """
+ The class constructor.
+ """
+ self.opt = opt
+ self.sym = pycrc.symtable.SymbolTable(opt)
+ self.indent = indent
+ self.content = content
+
+ def gen(self, indent = ''):
+ """
+ Return an array of strings.
+ """
+ out = []
+ if self.indent is None:
+ indent = ''
+ else:
+ indent += self.indent
+ for item in self.content:
+ if isinstance(item, str):
+ out.append(indent + item)
+ else:
+ out += item.gen(indent)
+ return out
+
+ def __str__(self):
+ """
+ Stringify the object.
+ """
+ return '\n'.join([l.rstrip() for l in self.gen()])
+
+
+class Conditional(CodeGen):
+ """
+ A conditional block of code.
+ """
+ def __init__(self, opt, indent, condition, content):
+ """
+ The class constructor.
+ """
+ super(Conditional, self).__init__(opt, indent)
+ if condition:
+ self.content = content
+
+
+class Conditional2(CodeGen):
+ """
+ A conditional block of code with an else block.
+ """
+ def __init__(self, opt, indent, condition, content_true, content_false):
+ """
+ The class constructor.
+ """
+ super(Conditional2, self).__init__(opt, indent)
+ if condition:
+ self.content = content_true
+ else:
+ self.content = content_false
+
+
+class Comment(CodeGen):
+ """
+ A comment wrapper.
+ """
+ def __init__(self, opt, indent, content):
+ """
+ The class constructor.
+ """
+ super(Comment, self).__init__(opt, indent)
+ self.content = [
+ '/**',
+ CodeGen(opt, indent + ' * ', content),
+ ' */'
+ ]
+
+
+class ParamBlock(CodeGen):
+ """
+ Print the parameters of the model.
+ """
+ def __init__(self, opt, indent, algorithm = False):
+ """
+ The class constructor.
+ """
+ super(ParamBlock, self).__init__(opt, indent)
+ self.content = [
+ '- {0:13s} = {1}'.format('Width', self.sym['crc_width']),
+ '- {0:13s} = {1}'.format('Poly', self.sym['crc_poly']),
+ '- {0:13s} = {1}'.format('XorIn', self.sym['crc_xor_in']),
+ '- {0:13s} = {1}'.format('ReflectIn', self.sym['crc_reflect_in']),
+ '- {0:13s} = {1}'.format('XorOut', self.sym['crc_xor_out']),
+ '- {0:13s} = {1}'.format('ReflectOut', self.sym['crc_reflect_out']),
+ Conditional(opt, '', algorithm,
+ ['- {0:13s} = {1}'.format('Algorithm', self.sym['crc_algorithm'])]),
+ Conditional(opt, '', opt.slice_by > 1,
+ ['- {0:13s} = {1}'.format('SliceBy', opt.slice_by)]),
+ ]
+
+
+
+class File(CodeGen):
+ """
+ Generate the file output.
+ """
+ def __init__(self, opt, indent):
+ """
+ The class constructor.
+ """
+ super(File, self).__init__(opt, indent)
+ self.content = [
+ Comment(opt, '', [
+ '\\file',
+ 'Functions and types for CRC checks.',
+ '',
+ 'Generated on {datetime}'.format(**self.sym),
+ 'by {program_version}, {program_url}'.format(**self.sym),
+ 'using the configuration:',
+ ParamBlock(opt, ' ', algorithm = True),
+ Conditional(opt, '', opt.action == opt.action_generate_h, [
+ '',
+ 'This file defines the functions {crc_init_function}(), ' \
+ '{crc_update_function}() and {crc_finalize_function}().'.format(**self.sym),
+ '',
+ 'The {crc_init_function}() function returns the inital \c crc value and must be called'.format(**self.sym),
+ 'before the first call to {crc_update_function}().'.format(**self.sym),
+ 'Similarly, the {crc_finalize_function}() function must be called after the last call'.format(**self.sym),
+ 'to {crc_update_function}(), before the \c crc is being used.'.format(**self.sym),
+ 'is being used.',
+ '',
+ 'The {crc_update_function}() function can be called any number of times (including zero'.format(**self.sym),
+ 'times) in between the {crc_init_function}() and {crc_finalize_function}() calls.'.format(**self.sym),
+ '',
+ 'This pseudo-code shows an example usage of the API:',
+ '\code{.c}',
+ Conditional(self.opt, '', self.opt.undefined_crc_parameters, [
+ '{cfg_t} cfg = '.format(**self.sym) + '{',
+ Conditional(self.opt, 4*' ', self.opt.width is None, [
+ '0, // width',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.poly is None, [
+ '0, // poly',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.reflect_in is None, [
+ '0, // reflect_in',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.xor_in is None, [
+ '0, // xor_in',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.reflect_out is None, [
+ '0, // reflect_out',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.xor_out is None, [
+ '0, // xor_out',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.width is None, [
+ '',
+ '0, // crc_mask',
+ '0, // msb_mask',
+ '0, // crc_shift',
+ ]),
+ '};',
+ ]),
+ '{crc_t} crc;'.format(**self.sym),
+ 'unsigned char data[MAX_DATA_LEN];',
+ 'size_t data_len;',
+ '',
+ Conditional(self.opt, '', _use_crc_table_gen(self.opt), [
+ '{crc_table_gen_function}(&cfg);'.format(**self.sym),
+ ]),
+ 'crc = {0}({1});'.format(self.sym['crc_init_function'], '' if _use_constant_crc_init(self.sym) else '&cfg'),
+ 'while ((data_len = read_data(data, MAX_DATA_LEN)) > 0) {',
+ CodeGen(self.opt, 4*' ', [
+ 'crc = {0}({1}crc, data, data_len);'.format(self.sym['crc_update_function'], '' if _use_cfg_in_crc_update(self.opt) else '&cfg, '),
+ ]),
+ '}',
+ 'crc = {0}({1}crc);'.format(self.sym['crc_finalize_function'], '' if _use_cfg_in_finalize(self.opt) else '&cfg, '),
+ '\endcode',
+ ]),
+ ]),
+ ]
+ if opt.action == opt.action_generate_h:
+ self.content += self._header_file()
+ elif opt.action == opt.action_generate_c:
+ self.content += self._c_file()
+ elif opt.action == opt.action_generate_c_main:
+ self.content += self._c_file() + self._main_file()
+
+ def _header_file(self):
+ """
+ Add header content.
+ """
+ out = [
+ '#ifndef {header_protection}'.format(**self.sym),
+ '#define {header_protection}'.format(**self.sym),
+ '',
+ CodeGen(self.opt, '', _includes(self.opt)),
+ '#include <stdlib.h>',
+ Conditional(self.opt, '', self.opt.c_std != 'C89',
+ ['#include <stdint.h>']),
+ Conditional(self.opt, '', _use_cfg(self.opt) and self.opt.c_std != 'C89',
+ ['#include <stdbool.h>']),
+ '',
+ '#ifdef __cplusplus',
+ 'extern "C" {',
+ '#endif',
+ '', '',
+ Comment(self.opt, '', [
+ 'The definition of the used algorithm.',
+ '',
+ 'This is not used anywhere in the generated code, but it may be used by the',
+ 'application code to call algorithm-specific code, if desired.',
+ ]),
+ '#define {0} 1'.format(_crc_algo_define(self.opt, self.sym)),
+ '', '',
+ Comment(self.opt, self.indent, [
+ 'The type of the CRC values.',
+ '',
+ 'This type must be big enough to contain at least {cfg_width} bits.'.format(**self.sym),
+ ]),
+ 'typedef {underlying_crc_t} {crc_t};'.format(**self.sym),
+ Conditional(self.opt, '', _use_cfg(self.opt), [
+ '', '',
+ Comment(self.opt, self.indent, ['The configuration type of the CRC algorithm.']),
+ 'typedef struct {',
+ Conditional(self.opt, 4*' ', self.opt.width is None,
+ ['{0:24s} {1}'.format('unsigned int width;',
+ '/*!< The width of the polynomial */')]),
+ Conditional(self.opt, 4*' ', self.opt.poly is None,
+ ['{0:24s} {1}'.format(self.sym['crc_t'] + ' poly;',
+ '/*!< The CRC polynomial */')]),
+ Conditional(self.opt, 4*' ', self.opt.reflect_in is None,
+ ['{0:24s} {1}'.format(self.sym['c_bool'] + ' reflect_in;',
+ '/*!< Whether the input shall be reflected or not */')]),
+ Conditional(self.opt, 4*' ', self.opt.xor_in is None,
+ ['{0:24s} {1}'.format(self.sym['crc_t'] + ' xor_in;',
+ '/*!< The initial value of the register */')]),
+ Conditional(self.opt, 4*' ', self.opt.reflect_out is None,
+ ['{0:24s} {1}'.format(self.sym['c_bool'] + ' reflect_out;',
+ '/*!< Whether the output shall be reflected or not */')]),
+ Conditional(self.opt, 4*' ', self.opt.xor_out is None,
+ ['{0:24s} {1}'.format(self.sym['crc_t'] + ' xor_out;',
+ '/*!< The value which shall be XOR-ed to the final CRC value */')]),
+ Conditional(self.opt, 4*' ', self.opt.width is None, [
+ '',
+ '/* internal parameters */',
+ '{0:24s} {1}'.format(self.sym['crc_t'] + ' msb_mask;', '/*!< a bitmask with the Most Significant Bit set to 1'),
+ 33*' ' + 'initialise as (crc_t)1u << (width - 1) */',
+ '{0:24s} {1}'.format(self.sym['crc_t'] + ' crc_mask;', '/*!< a bitmask with all width bits set to 1'),
+ 33*' ' + 'initialise as (cfg->msb_mask - 1) | cfg->msb_mask */',
+ '{0:24s} {1}'.format('unsigned int crc_shift;', '/*!< a shift count that is used when width < 8'),
+ 33*' ' + 'initialise as cfg->width < 8 ? 8 - cfg->width : 0 */',
+ ]),
+ '}} {cfg_t};'.format(**self.sym),
+ ]),
+ Conditional(self.opt, '', _use_reflect_func(self.opt) and not _use_static_reflect_func(self.opt), [
+ '', '',
+ Comment(self.opt, '', [
+ 'Reflect all bits of a \\a data word of \\a data_len bytes.',
+ '',
+ '\\param[in] data The data word to be reflected.',
+ '\\param[in] data_len The width of \\a data expressed in number of bits.',
+ '\\return The reflected data.'
+ ]),
+ '{crc_t} {crc_reflect_function}({crc_t} data, size_t data_len);'.format(**self.sym),
+ ]),
+ Conditional(self.opt, '', _use_crc_table_gen(self.opt), [
+ '', '',
+ Comment(self.opt, '', [
+ 'Populate the private static crc table.',
+ '',
+ '\\param[in] cfg A pointer to an initialised {cfg_t} structure.'.format(**self.sym),
+ ]),
+ 'void {crc_table_gen_function}(const {cfg_t} *cfg);'.format(**self.sym),
+ ]),
+ '', '',
+ Comment(self.opt, '', [
+ 'Calculate the initial crc value.',
+ '',
+ Conditional(self.opt, '', _use_cfg(self.opt), [
+ '\\param[in] cfg A pointer to an initialised {cfg_t} structure.'.format(**self.sym),
+ ]),
+ '\\return The initial crc value.',
+ ]),
+ Conditional2(self.opt, '', _use_constant_crc_init(self.sym), [
+ Conditional2(self.opt, '', self.opt.c_std == 'C89', [
+ '#define {crc_init_function}() ({crc_init_value})'.format(**self.sym),
+ ], [
+ 'static inline {0}'.format(_crc_init_function_def(self.opt, self.sym)),
+ '{',
+ ' return {crc_init_value};'.format(**self.sym),
+ '}',
+ ]),
+ ], [
+ '{0};'.format(_crc_init_function_def(self.opt, self.sym)),
+ ]),
+ '', '',
+ Comment(self.opt, '', [
+ 'Update the crc value with new data.',
+ '',
+ '\\param[in] crc The current crc value.',
+ Conditional(self.opt, '', not _use_cfg_in_crc_update(self.opt), [
+ '\\param[in] cfg A pointer to an initialised {cfg_t} structure.'.format(**self.sym),
+ ]),
+ '\\param[in] data Pointer to a buffer of \\a data_len bytes.',
+ '\\param[in] data_len Number of bytes in the \\a data buffer.',
+ '\\return The updated crc value.',
+ ]),
+ '{0};'.format(_crc_update_function_def(self.opt, self.sym)),
+ '', '',
+ Comment(self.opt, '', [
+ 'Calculate the final crc value.',
+ '',
+ Conditional(self.opt, '', not _use_cfg_in_finalize(self.opt), [
+ '\\param[in] cfg A pointer to an initialised {cfg_t} structure.'.format(**self.sym),
+ ]),
+ '\\param[in] crc The current crc value.',
+ '\\return The final crc value.',
+ ]),
+ Conditional2(self.opt, '', _use_inline_crc_finalize(self.opt), [
+ Conditional2(self.opt, '', self.opt.c_std == 'C89', [
+ '#define {0}(crc) ({1})'.format(self.sym['crc_finalize_function'], _crc_final_value(self.opt, self.sym)),
+ ], [
+ 'static inline {0}'.format(_crc_finalize_function_def(self.opt, self.sym)),
+ '{',
+ ' return {0};'.format(_crc_final_value(self.opt, self.sym)),
+ '}',
+ ]),
+ ], [
+ '{0};'.format(_crc_finalize_function_def(self.opt, self.sym)),
+ ]),
+ '', '',
+ '#ifdef __cplusplus',
+ '} /* closing brace for extern "C" */',
+ '#endif',
+ '',
+ '#endif /* {header_protection} */'.format(**self.sym),
+ '',
+ ]
+ return out
+
+ def _c_file(self):
+ """
+ Add C file content.
+ """
+ out = [
+ CodeGen(self.opt, '', _includes(self.opt)),
+ '#include "{header_filename}" /* include the header file generated with pycrc */'.format(**self.sym),
+ '#include <stdlib.h>',
+ Conditional(self.opt, '', self.opt.c_std != 'C89', [
+ '#include <stdint.h>',
+ Conditional(self.opt, '', self.opt.undefined_crc_parameters or \
+ self.opt.algorithm == self.opt.algo_bit_by_bit or \
+ self.opt.algorithm == self.opt.algo_bit_by_bit_fast, [
+ '#include <stdbool.h>',
+ ]),
+ ]),
+ Conditional(self.opt, '', self.opt.slice_by > 1, [
+ '#include <endian.h>',
+ ]),
+ Conditional(self.opt, '', _use_reflect_func(self.opt) and _use_static_reflect_func(self.opt), [
+ '',
+ 'static {crc_t} {crc_reflect_function}({crc_t} data, size_t data_len);'.format(**self.sym),
+ ]),
+ '',
+ CodeGen(self.opt, '', _crc_table(self.opt, self.sym)),
+ CodeGen(self.opt, '', _crc_reflect_function_gen(self.opt, self.sym)),
+ CodeGen(self.opt, '', _crc_init_function_gen(self.opt, self.sym)),
+ CodeGen(self.opt, '', _crc_table_gen(self.opt, self.sym)),
+ CodeGen(self.opt, '', _crc_update_function_gen(self.opt, self.sym)),
+ CodeGen(self.opt, '', _crc_finalize_function_gen(self.opt, self.sym)),
+ '',
+ ]
+ return out
+
+ def _main_file(self):
+ """
+ Add main file content.
+ """
+ out = [
+ '',
+ '',
+ CodeGen(self.opt, '', _includes(self.opt)),
+ '#include <stdio.h>',
+ '#include <getopt.h>',
+ Conditional(self.opt, '', self.opt.undefined_crc_parameters, [
+ '#include <stdlib.h>',
+ '#include <stdio.h>',
+ '#include <ctype.h>',
+ ]),
+ Conditional(self.opt, '', self.opt.c_std != 'C89', [
+ '#include <stdbool.h>',
+ ]),
+ '#include <string.h>',
+ '',
+ 'static char str[256] = "123456789";',
+ 'static {c_bool} verbose = {c_false};'.format(**self.sym),
+ self._getopt_template(),
+ '',
+ '',
+ Conditional2(self.opt, '', self.opt.undefined_crc_parameters, [
+ 'static void print_params(const {cfg_t} *cfg)'.format(**self.sym),
+ ], [
+ 'static void print_params(void)',
+ ]),
+ '{',
+ CodeGen(self.opt, 4*' ', [
+ 'char format[20];',
+ '',
+ Conditional2(self.opt, '', self.opt.c_std == 'C89', [
+ 'sprintf(format, "%%-16s = 0x%%0%dlx\\n", (unsigned int)({cfg_width} + 3) / 4);'.format(**self.sym),
+ 'printf("%-16s = %d\\n", "width", (unsigned int){cfg_width});'.format(**self.sym),
+ 'printf(format, "poly", (unsigned long int){cfg_poly});'.format(**self.sym),
+ 'printf("%-16s = %s\\n", "reflect_in", {0});'.format(self.sym['cfg_reflect_in'] + ' ? "true": "false"' if self.opt.reflect_in is None else ('"true"' if self.opt.reflect_in else '"false"')),
+ 'printf(format, "xor_in", (unsigned long int){cfg_xor_in});'.format(**self.sym),
+ 'printf("%-16s = %s\\n", "reflect_out", {0});'.format(self.sym['cfg_reflect_out'] + ' ? "true": "false"' if self.opt.reflect_out is None else ('"true"' if self.opt.reflect_out else '"false"')),
+ 'printf(format, "xor_out", (unsigned long int){cfg_xor_out});'.format(**self.sym),
+ 'printf(format, "crc_mask", (unsigned long int){cfg_mask});'.format(**self.sym),
+ 'printf(format, "msb_mask", (unsigned long int){cfg_msb_mask});'.format(**self.sym),
+ ], [
+ 'snprintf(format, sizeof(format), "%%-16s = 0x%%0%dllx\\n", (unsigned int)({cfg_width} + 3) / 4);'.format(**self.sym),
+ 'printf("%-16s = %d\\n", "width", (unsigned int){cfg_width});'.format(**self.sym),
+ 'printf(format, "poly", (unsigned long long int){cfg_poly});'.format(**self.sym),
+ 'printf("%-16s = %s\\n", "reflect_in", {0});'.format(self.sym['cfg_reflect_in'] + ' ? "true": "false"' if self.opt.reflect_in is None else ('"true"' if self.opt.reflect_in else '"false"')),
+ 'printf(format, "xor_in", (unsigned long long int){cfg_xor_in});'.format(**self.sym),
+ 'printf("%-16s = %s\\n", "reflect_out", {0});'.format(self.sym['cfg_reflect_out'] + ' ? "true": "false"' if self.opt.reflect_out is None else ('"true"' if self.opt.reflect_out else '"false"')),
+ 'printf(format, "xor_out", (unsigned long long int){cfg_xor_out});'.format(**self.sym),
+ 'printf(format, "crc_mask", (unsigned long long int){cfg_mask});'.format(**self.sym),
+ 'printf(format, "msb_mask", (unsigned long long int){cfg_msb_mask});'.format(**self.sym),
+ ]),
+ ]),
+ '}',
+ '',
+ '',
+ Comment(self.opt, '', [
+ 'C main function.',
+ '\\param[in] argc the number of arguments in \\a argv.',
+ '\\param[in] argv a NULL-terminated array of pointers to the argument strings.',
+ '\\retval 0 on success.',
+ '\\retval >0 on error.',
+ ]),
+ 'int main(int argc, char *argv[])',
+ '{',
+ CodeGen(self.opt, 4*' ', [
+ Conditional(self.opt, '', self.opt.undefined_crc_parameters, [
+ '{cfg_t} cfg = '.format(**self.sym) + '{',
+ Conditional(self.opt, 4*' ', self.opt.width is None, [
+ '0, /* width */',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.poly is None, [
+ '0, /* poly */',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.reflect_in is None, [
+ '0, /* reflect_in */',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.xor_in is None, [
+ '0, /* xor_in */',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.reflect_out is None, [
+ '0, /* reflect_out */',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.xor_out is None, [
+ '0, /* xor_out */',
+ ]),
+ Conditional(self.opt, 4*' ', self.opt.width is None, [
+ '',
+ '0, /* crc_mask */',
+ '0, /* msb_mask */',
+ '0, /* crc_shift */',
+ ]),
+ '};',
+ ]),
+ '{crc_t} crc;'.format(**self.sym),
+ '',
+ Conditional2(self.opt, '', self.opt.undefined_crc_parameters, [
+ 'get_config(argc, argv, &cfg);',
+ ], [
+ 'get_config(argc, argv);',
+ ]),
+ Conditional(self.opt, '', _use_crc_table_gen(self.opt), [
+ '{crc_table_gen_function}(&cfg);'.format(**self.sym),
+ ]),
+ 'crc = {0}({1});'.format(self.sym['crc_init_function'], '' if _use_constant_crc_init(self.sym) else '&cfg'),
+ 'crc = {0}({1}crc, (void *)str, strlen(str));'.format(self.sym['crc_update_function'], '' if _use_cfg_in_crc_update(self.opt) else '&cfg, '),
+ 'crc = {0}({1}crc);'.format(self.sym['crc_finalize_function'], '' if _use_cfg_in_finalize(self.opt) else '&cfg, '),
+ '',
+ 'if (verbose) {',
+ CodeGen(self.opt, 4*' ', [
+ 'print_params({0});'.format('&cfg' if self.opt.undefined_crc_parameters else ''),
+ ]),
+ '}',
+ Conditional2(self.opt, '', self.opt.c_std == 'C89', [
+ 'printf("0x%lx\\n", (unsigned long int)crc);',
+ ], [
+ 'printf("0x%llx\\n", (unsigned long long int)crc);',
+ ]),
+ 'return 0;',
+ ]),
+ '}',
+ ]
+ return out
+
+ def _getopt_template(self):
+ """
+ Add getopt functions.
+ """
+ out = [
+ Conditional(self.opt, '', self.opt.reflect_in is None or self.opt.reflect_out is None, [
+ '',
+ '',
+ 'static {c_bool} atob(const char *str)'.format(**self.sym),
+ '{',
+ CodeGen(self.opt, 4*' ', [
+ 'if (!str) {',
+ CodeGen(self.opt, 4*' ', [
+ 'return 0;',
+ ]),
+ '}',
+ 'if (isdigit(str[0])) {',
+ CodeGen(self.opt, 4*' ', [
+ 'return ({c_bool})atoi(str);'.format(**self.sym),
+ ]),
+ '}',
+ 'if (tolower(str[0]) == \'t\') {',
+ CodeGen(self.opt, 4*' ', [
+ 'return {c_true};'.format(**self.sym),
+ ]),
+ '}',
+ 'return {c_false};'.format(**self.sym),
+ ]),
+ '}',
+ ]),
+ Conditional(self.opt, '', self.opt.poly is None or self.opt.xor_in is None or self.opt.xor_out is None, [
+ '',
+ '',
+ 'static crc_t xtoi(const char *str)',
+ '{',
+ CodeGen(self.opt, 4*' ', [
+ 'crc_t ret = 0;',
+ '',
+ 'if (!str) {',
+ CodeGen(self.opt, 4*' ', [
+ 'return 0;',
+ ]),
+ '}',
+ 'if (str[0] == \'0\' && tolower(str[1]) == \'x\') {',
+ CodeGen(self.opt, 4*' ', [
+ 'str += 2;',
+ 'while (*str) {',
+ CodeGen(self.opt, 4*' ', [
+ 'if (isdigit(*str))',
+ CodeGen(self.opt, 4*' ', [
+ 'ret = 16 * ret + *str - \'0\';',
+ ]),
+ 'else if (isxdigit(*str))',
+ CodeGen(self.opt, 4*' ', [
+ 'ret = 16 * ret + tolower(*str) - \'a\' + 10;',
+ ]),
+ 'else',
+ CodeGen(self.opt, 4*' ', [
+ 'return ret;',
+ ]),
+ 'str++;',
+ ]),
+ '}',
+ ]),
+ '} else if (isdigit(*str)) {',
+ CodeGen(self.opt, 4*' ', [
+ 'while (*str) {',
+ CodeGen(self.opt, 4*' ', [
+ 'if (isdigit(*str))',
+ CodeGen(self.opt, 4*' ', [
+ 'ret = 10 * ret + *str - \'0\';',
+ ]),
+ 'else',
+ CodeGen(self.opt, 4*' ', [
+ 'return ret;',
+ ]),
+ 'str++;',
+ ]),
+ '}',
+ ]),
+ '}',
+ 'return ret;',
+ ]),
+ '}',
+ ]),
+ '',
+ '',
+ Conditional2(self.opt, '', self.opt.undefined_crc_parameters, [
+ 'static int get_config(int argc, char *argv[], {cfg_t} *cfg)'.format(**self.sym),
+ ], [
+ 'static int get_config(int argc, char *argv[])',
+ ]),
+ '{',
+ CodeGen(self.opt, 4*' ', [
+ 'int c;',
+ 'int option_index;',
+ 'static struct option long_options[] = {',
+ CodeGen(self.opt, 4*' ', [
+ Conditional(self.opt, '', self.opt.width is None, [
+ '{"width", 1, 0, \'w\'},',
+ ]),
+ Conditional(self.opt, '', self.opt.poly is None, [
+ '{"poly", 1, 0, \'p\'},',
+ ]),
+ Conditional(self.opt, '', self.opt.reflect_in is None, [
+ '{"reflect-in", 1, 0, \'n\'},',
+ ]),
+ Conditional(self.opt, '', self.opt.xor_in is None, [
+ '{"xor-in", 1, 0, \'i\'},',
+ ]),
+ Conditional(self.opt, '', self.opt.reflect_out is None, [
+ '{"reflect-out", 1, 0, \'u\'},',
+ ]),
+ Conditional(self.opt, '', self.opt.xor_out is None, [
+ '{"xor-out", 1, 0, \'o\'},',
+ ]),
+ '{"verbose", 0, 0, \'v\'},',
+ '{"check-string", 1, 0, \'s\'},',
+ Conditional(self.opt, '', self.opt.width is None, [
+ '{"table-idx-with", 1, 0, \'t\'},',
+ ]),
+ '{0, 0, 0, 0}',
+ ]),
+ '};',
+ '',
+ 'while (1) {',
+ CodeGen(self.opt, 4*' ', [
+ 'option_index = 0;',
+ '',
+ 'c = getopt_long(argc, argv, "w:p:n:i:u:o:s:vt", long_options, &option_index);',
+ 'if (c == -1)',
+ CodeGen(self.opt, 4*' ', [
+ 'break;',
+ ]),
+ '',
+ 'switch (c) {',
+ CodeGen(self.opt, 4*' ', [
+ 'case 0:',
+ CodeGen(self.opt, 4*' ', [
+ 'printf("option %s", long_options[option_index].name);',
+ 'if (optarg)',
+ CodeGen(self.opt, 4*' ', [
+ 'printf(" with arg %s", optarg);',
+ ]),
+ 'printf("\\n");',
+ 'break;',
+ ]),
+ Conditional(self.opt, '', self.opt.width is None, [
+ 'case \'w\':',
+ CodeGen(self.opt, 4*' ', [
+ 'cfg->width = atoi(optarg);',
+ 'break;',
+ ]),
+ ]),
+ Conditional(self.opt, '', self.opt.poly is None, [
+ 'case \'p\':',
+ CodeGen(self.opt, 4*' ', [
+ 'cfg->poly = xtoi(optarg);',
+ 'break;',
+ ]),
+ ]),
+ Conditional(self.opt, '', self.opt.reflect_in is None, [
+ 'case \'n\':',
+ CodeGen(self.opt, 4*' ', [
+ 'cfg->reflect_in = atob(optarg);',
+ 'break;',
+ ]),
+ ]),
+ Conditional(self.opt, '', self.opt.xor_in is None, [
+ 'case \'i\':',
+ CodeGen(self.opt, 4*' ', [
+ 'cfg->xor_in = xtoi(optarg);',
+ 'break;',
+ ]),
+ ]),
+ Conditional(self.opt, '', self.opt.reflect_out is None, [
+ 'case \'u\':',
+ CodeGen(self.opt, 4*' ', [
+ 'cfg->reflect_out = atob(optarg);',
+ 'break;',
+ ]),
+ ]),
+ Conditional(self.opt, '', self.opt.xor_out is None, [
+ 'case \'o\':',
+ CodeGen(self.opt, 4*' ', [
+ 'cfg->xor_out = xtoi(optarg);',
+ 'break;',
+ ]),
+ ]),
+ 'case \'s\':',
+ CodeGen(self.opt, 4*' ', [
+ 'memcpy(str, optarg, strlen(optarg) < sizeof(str) ? strlen(optarg) + 1 : sizeof(str));',
+ 'str[sizeof(str) - 1] = \'\\0\';',
+ 'break;',
+ ]),
+ 'case \'v\':',
+ CodeGen(self.opt, 4*' ', [
+ 'verbose = {c_true};'.format(**self.sym),
+ 'break;',
+ ]),
+ Conditional(self.opt, '', self.opt.width is None, [
+ 'case \'t\':',
+ CodeGen(self.opt, 4*' ', [
+ '/* ignore --table_idx_width option */',
+ 'break;',
+ ]),
+ ]),
+ 'case \'?\':',
+ CodeGen(self.opt, 4*' ', [
+ 'return -1;',
+ ]),
+ 'case \':\':',
+ CodeGen(self.opt, 4*' ', [
+ 'fprintf(stderr, "missing argument to option %c\\n", c);',
+ 'return -1;',
+ ]),
+ 'default:',
+ CodeGen(self.opt, 4*' ', [
+ 'fprintf(stderr, "unhandled option %c\\n", c);',
+ 'return -1;',
+ ]),
+ ]),
+ '}',
+ ]),
+ '}',
+ Conditional(self.opt, '', self.opt.width is None, [
+ 'cfg->msb_mask = (crc_t)1u << (cfg->width - 1);',
+ 'cfg->crc_mask = (cfg->msb_mask - 1) | cfg->msb_mask;',
+ 'cfg->crc_shift = cfg->width < 8 ? 8 - cfg->width : 0;',
+ ]),
+ '',
+ Conditional(self.opt, '', self.opt.poly is None, [
+ 'cfg->poly &= {cfg_mask};'.format(**self.sym),
+ ]),
+ Conditional(self.opt, '', self.opt.xor_in is None, [
+ 'cfg->xor_in &= {cfg_mask};'.format(**self.sym),
+ ]),
+ Conditional(self.opt, '', self.opt.xor_out is None, [
+ 'cfg->xor_out &= {cfg_mask};'.format(**self.sym),
+ ]),
+ 'return 0;',
+ ]),
+ '}',
+ ]
+ return CodeGen(self.opt, '', out)
+
+
+def _includes(opt):
+ """
+ Return the #include directives for the user-defined list of include files.
+ """
+ includes = []
+ if opt.include_files is not None and len(opt.include_files) > 0:
+ for include_file in opt.include_files:
+ if include_file[0] == '"' or include_file[0] == '<':
+ includes.append('#include {0}'.format(include_file))
+ else:
+ includes.append('#include "{0}"'.format(include_file))
+ return includes
+
+
+def _crc_algo_define(opt, sym):
+ """
+ Get the the identifier for header files.
+ """
+ name = sym['crc_algorithm'].upper().replace('-', '_')
+ return 'CRC_ALGO_' + name
+
+
+def _use_cfg(opt):
+ """
+ Return True if a cfg_t structure is to be used.
+ """
+ return opt.undefined_crc_parameters
+
+
+def _use_constant_crc_init(sym):
+ """
+ Return True if the inintial value is constant.
+ """
+ return sym['crc_init_value'] is not None
+
+
+def _use_reflect_func(opt):
+ """
+ Return True if the reflect function is to be used.
+ """
+ if opt.reflect_out == None or opt.reflect_in == None:
+ return True
+ elif opt.algorithm == opt.algo_table_driven:
+ if opt.reflect_in != opt.reflect_out:
+ return True
+ elif opt.algorithm == opt.algo_bit_by_bit:
+ if opt.reflect_in:
+ return True
+ if opt.reflect_out:
+ return True
+ elif opt.algorithm == opt.algo_bit_by_bit_fast:
+ if opt.reflect_in:
+ return True
+ if opt.reflect_out:
+ return True
+ return False
+
+
+def _use_static_reflect_func(opt):
+ """
+ Whether a static reflect function is to be used.
+ """
+ if opt.algorithm == opt.algo_table_driven:
+ return False
+ elif opt.reflect_out is not None and opt.algorithm == opt.algo_bit_by_bit_fast:
+ return False
+ else:
+ return True
+
+
+def _use_crc_table_gen(opt):
+ """
+ Return True if the table generator function is to be generated.
+ """
+ if opt.algorithm == opt.algo_table_driven:
+ return opt.width is None or opt.poly is None or opt.reflect_in is None
+ else:
+ return False
+
+
+def _crc_init_function_def(opt, sym):
+ """
+ The definition for the init function.
+ """
+ if _use_constant_crc_init(sym):
+ return '{crc_t} {crc_init_function}(void)'.format(**sym)
+ else:
+ return '{crc_t} {crc_init_function}(const {cfg_t} *cfg)'.format(**sym)
+
+
+def _use_cfg_in_crc_update(opt):
+ """
+ Return True if the update function uses the cfg_t parameter.
+ """
+ if opt.algorithm in set([opt.algo_bit_by_bit, opt.algo_bit_by_bit_fast]):
+ if opt.width is not None and opt.poly is not None and opt.reflect_in is not None:
+ return True
+ elif opt.algorithm == opt.algo_table_driven:
+ if opt.width is not None and opt.reflect_in is not None:
+ return True
+ return False
+
+
+def _crc_update_function_def(opt, sym):
+ """
+ The definition of the update function.
+ """
+ if _use_cfg_in_crc_update(opt):
+ return '{crc_t} {crc_update_function}({crc_t} crc, const void *data, size_t data_len)'.format(**sym)
+ else:
+ return '{crc_t} {crc_update_function}(const {cfg_t} *cfg, {crc_t} crc, const void *data, size_t data_len)'.format(**sym)
+
+
+def _use_cfg_in_finalize(opt):
+ """
+ Return True if the cfg_t parameter is used in the finalize function.
+ """
+ if opt.algorithm == opt.algo_bit_by_bit:
+ if opt.width is not None and opt.poly is not None and opt.reflect_out is not None and opt.xor_out is not None:
+ return True
+ elif opt.algorithm == opt.algo_bit_by_bit_fast:
+ if opt.width is not None and opt.reflect_out is not None and opt.xor_out is not None:
+ return True
+ elif opt.algorithm == opt.algo_table_driven:
+ if opt.width is not None and opt.reflect_in is not None and opt.reflect_out is not None and opt.xor_out is not None:
+ return True
+ return False
+
+
+def _use_inline_crc_finalize(opt):
+ """
+ Return True if the init function can be inlined.
+ """
+ if opt.algorithm in set([opt.algo_bit_by_bit_fast, opt.algo_table_driven]) and \
+ (opt.width is not None and opt.reflect_in is not None and opt.reflect_out is not None and opt.xor_out is not None):
+ return True
+ else:
+ return False
+
+
+def _use_constant_crc_table(opt):
+ """
+ Return True is the CRC table is constant.
+ """
+ if opt.width is not None and opt.poly is not None and opt.reflect_in is not None:
+ return True
+ else:
+ return False
+
+
+def _crc_finalize_function_def(opt, sym):
+ """
+ The definition of the finalize function.
+ """
+ if _use_cfg_in_finalize(opt):
+ return '{crc_t} {crc_finalize_function}({crc_t} crc)'.format(**sym)
+ else:
+ return '{crc_t} {crc_finalize_function}(const {cfg_t} *cfg, {crc_t} crc)'.format(**sym)
+
+
+def _crc_final_value(opt, sym):
+ """
+ The return value for the finalize function.
+ """
+ if opt.algorithm == opt.algo_table_driven:
+ if opt.reflect_in == opt.reflect_out:
+ return expr.Xor('crc', sym['crc_xor_out']).simplify()
+ else:
+ reflect_fun = expr.FunctionCall(sym['crc_reflect_function'], ['crc', sym['crc_width']])
+ return expr.Xor(reflect_fun, sym['crc_xor_out']).simplify()
+ elif opt.reflect_out:
+ reflect_fun = expr.FunctionCall(sym['crc_reflect_function'], ['crc', sym['crc_width']])
+ return expr.Xor(reflect_fun, sym['crc_xor_out']).simplify()
+ else:
+ return expr.Xor('crc', sym['crc_xor_out']).simplify()
+
+
+def _crc_table(opt, sym):
+ """
+ Return the code for the CRC table or the generator function.
+ """
+ if opt.algorithm != opt.algo_table_driven:
+ return []
+ return [
+ '', '',
+ Comment(opt, '', [
+ 'Static table used for the table_driven implementation.',
+ Conditional(opt, '', opt.undefined_crc_parameters, [
+ 'Must be initialised with the {crc_table_gen_function} function.'.format(**sym),
+ ]),
+ ]),
+ Conditional2(opt, '', _use_constant_crc_table(opt), [
+ Conditional2(opt, '', opt.slice_by > 1, [
+ 'static const {crc_t} crc_table[{crc_slice_by}][{crc_table_width}] = {crc_table_init};'.format(**sym),
+ ], [
+ 'static const {crc_t} crc_table[{crc_table_width}] = {crc_table_init};'.format(**sym),
+ ]),
+ ], [
+ 'static {crc_t} crc_table[{crc_table_width}];'.format(**sym),
+ ]),
+ ]
+
+
+def _crc_table_gen(opt, sym):
+ """
+ Return the code for the CRC table or the generator function.
+ """
+ if opt.algorithm != opt.algo_table_driven or _use_constant_crc_table(opt):
+ return []
+ return [
+ '', '',
+ 'void {crc_table_gen_function}(const {cfg_t} *cfg)'.format(**sym),
+ '{',
+ CodeGen(opt, 4*' ', [
+ '{crc_t} crc;'.format(**sym),
+ 'unsigned int i, j;',
+ '',
+ 'for (i = 0; i < {cfg_table_width}; i++) '.format(**sym) + '{',
+ CodeGen(opt, 4*' ', [
+ Conditional2(opt, '', opt.reflect_in is None, [
+ 'if (cfg->reflect_in) {',
+ CodeGen(opt, 4*' ', [
+ 'crc = {crc_reflect_function}(i, {cfg_table_idx_width});'.format(**sym),
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'crc = i;',
+ ]),
+ '}',
+ ], [
+ Conditional2(opt, '', opt.reflect_in, [
+ 'crc = {crc_reflect_function}(i, {cfg_table_idx_width});'.format(**sym),
+ ], [
+ 'crc = i;',
+ ]),
+ ]),
+ 'crc <<= {0};'.format(expr.Parenthesis(expr.Add(expr.Sub(sym['cfg_width'], sym['cfg_table_idx_width']), sym['cfg_shift'])).simplify()),
+ 'for (j = 0; j < {cfg_table_idx_width}; j++) '.format(**sym) + '{',
+ CodeGen(opt, 4*' ', [
+ 'if (crc & {cfg_msb_mask_shifted}) '.format(**sym) + '{',
+ CodeGen(opt, 4*' ', [
+ 'crc = {0};'.format(expr.Xor(expr.Parenthesis(expr.Shl('crc', 1)), sym['cfg_poly_shifted']).simplify()),
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'crc = crc << 1;',
+ ]),
+ '}',
+ ]),
+ '}',
+ Conditional(opt, '', opt.reflect_in is None, [
+ 'if (cfg->reflect_in) {',
+ Conditional2(opt, 4*' ', sym.tbl_shift is None or sym.tbl_shift > 0, [
+ 'crc = {0};'.format(expr.Shl(expr.FunctionCall(sym['crc_reflect_function'], [expr.Shr('crc', sym['cfg_shift']), sym['cfg_width']]), sym['cfg_shift']).simplify()),
+ ], [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ '}',
+ ]),
+ Conditional(opt, '', opt.reflect_in, [
+ Conditional2(opt, 4*' ', sym.tbl_shift is None or sym.tbl_shift > 0, [
+ 'crc = {0};'.format(expr.Shl(expr.FunctionCall(sym['crc_reflect_function'], [expr.Shr('crc', sym['cfg_shift']), sym['cfg_width']]), sym['cfg_shift']).simplify()),
+ ], [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ ]),
+ 'crc_table[i] = {0};'.format(expr.Shr(expr.Parenthesis(expr.And('crc', sym['cfg_mask_shifted'])), sym['cfg_shift'])),
+ ]),
+ '}',
+ ]),
+ '}',
+ ]
+
+
+def _crc_reflect_function_gen(opt, sym):
+ """
+ Return the code for the reflect functon.
+ """
+ if not _use_reflect_func(opt):
+ return []
+ if not (opt.reflect_in is None or opt.reflect_in or \
+ opt.reflect_out is None or opt.reflect_out):
+ return []
+ return [
+ '', '',
+ '{crc_t} {crc_reflect_function}({crc_t} data, size_t data_len)'.format(**sym),
+ '{',
+ CodeGen(opt, 4*' ', [
+ 'unsigned int i;',
+ '{crc_t} ret;'.format(**sym),
+ '',
+ 'ret = data & 0x01;',
+ 'for (i = 1; i < data_len; i++) {',
+ CodeGen(opt, 4*' ', [
+ 'data >>= 1;',
+ 'ret = (ret << 1) | (data & 0x01);',
+ ]),
+ '}',
+ 'return ret;',
+ ]),
+ '}',
+ ]
+
+
+def _crc_init_function_gen(opt, sym):
+ """
+ Return the code for the init function.
+ """
+ if _use_constant_crc_init(sym):
+ return []
+ out = [
+ '', '',
+ _crc_init_function_def(opt, sym),
+ '{',
+ CodeGen(opt, 4*' ', [
+ Conditional(opt, '', opt.algorithm == opt.algo_bit_by_bit, [
+ 'unsigned int i;',
+ '{c_bool} bit;'.format(**sym),
+ '{crc_t} crc = {cfg_xor_in};'.format(**sym),
+ 'for (i = 0; i < {cfg_width}; i++) '.format(**sym) + '{',
+ CodeGen(opt, 4*' ', [
+ 'bit = crc & 0x01;',
+ 'if (bit) {',
+ CodeGen(opt, 4*' ', [
+ 'crc = ((crc ^ {cfg_poly}) >> 1) | {cfg_msb_mask};'.format(**sym),
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'crc >>= 1;',
+ ]),
+ '}',
+ ]),
+ '}',
+ 'return crc & {cfg_mask};'.format(**sym),
+ ]),
+ Conditional(opt, '', opt.algorithm == opt.algo_bit_by_bit_fast, [
+ 'return {cfg_xor_in} & {cfg_mask};'.format(**sym),
+ ]),
+ Conditional(opt, '', opt.algorithm == opt.algo_table_driven, [
+ Conditional2(opt, '', opt.reflect_in is None, [
+ 'if ({cfg_reflect_in}) '.format(**sym) + '{',
+ CodeGen(opt, 4*' ', [
+ 'return {crc_reflect_function}({cfg_xor_in} & {cfg_mask}, {cfg_width});'.format(**sym),
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'return {cfg_xor_in} & {cfg_mask};'.format(**sym),
+ ]),
+ '}',
+ ], [
+ Conditional2(opt, '', opt.algorithm == opt.reflect_in, [
+ 'return {crc_reflect_function}({cfg_xor_in} & {cfg_mask}, {cfg_width});'.format(**sym),
+ ], [
+ 'return {cfg_xor_in} & {cfg_mask};'.format(**sym),
+ ]),
+ ]),
+ ]),
+ ]),
+ '}',
+ ]
+ return out
+
+
+def _crc_update_function_gen(opt, sym):
+ """
+ Return the code for the update function.
+ """
+ out = [
+ '', '',
+ _crc_update_function_def(opt, sym),
+ '{',
+ CodeGen(opt, 4*' ', [ 'const unsigned char *d = (const unsigned char *)data;' ]),
+ ]
+ if opt.algorithm == opt.algo_bit_by_bit:
+ out += [
+ CodeGen(opt, 4*' ', [
+ 'unsigned int i;',
+ '{c_bool} bit;'.format(**sym),
+ 'unsigned char c;',
+ '',
+ 'while (data_len--) {',
+ Conditional2(opt, 4*' ', opt.reflect_in is None, [
+ 'if (' + sym['cfg_reflect_in'] + ') {',
+ CodeGen(opt, 4*' ', [
+ 'c = {crc_reflect_function}(*d++, 8);'.format(**sym),
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'c = *d++;',
+ ]),
+ '}',
+ ], [
+ Conditional2(opt, '', opt.reflect_in, [
+ 'c = {crc_reflect_function}(*d++, 8);'.format(**sym),
+ ], [
+ 'c = *d++;',
+ ]),
+ ]),
+
+ CodeGen(opt, 4*' ', [
+ 'for (i = 0; i < 8; i++) {',
+ CodeGen(opt, 4*' ', [
+ Conditional2(opt, '', opt.c_std == 'C89', [
+ 'bit = !!(crc & {cfg_msb_mask});'.format(**sym),
+ ], [
+ 'bit = crc & {cfg_msb_mask};'.format(**sym),
+ ]),
+ 'crc = (crc << 1) | ((c >> (7 - i)) & 0x01);',
+ 'if (bit) {',
+ CodeGen(opt, 4*' ', [
+ 'crc ^= {cfg_poly};'.format(**sym),
+ ]),
+ '}',
+ ]),
+ '}',
+ 'crc &= {cfg_mask};'.format(**sym),
+ ]),
+ '}',
+ 'return crc & {cfg_mask};'.format(**sym),
+ ]),
+ ]
+
+ if opt.algorithm == opt.algo_bit_by_bit_fast:
+ out += [
+ CodeGen(opt, 4*' ', [
+ 'unsigned int i;',
+ '{c_bool} bit;'.format(**sym),
+ 'unsigned char c;',
+ '',
+ 'while (data_len--) {',
+ CodeGen(opt, 4*' ', [
+ Conditional2(opt, '', opt.reflect_in == None, [
+ 'if (' + sym['cfg_reflect_in'] + ') {',
+ CodeGen(opt, 4*' ', [
+ 'c = {crc_reflect_function}(*d++, 8);'.format(**sym),
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'c = *d++;',
+ ]),
+ '}',
+ ], [
+ 'c = *d++;',
+ ]),
+ Conditional2(opt, '', opt.reflect_in, [
+ 'for (i = 0x01; i & 0xff; i <<= 1) {',
+ ], [
+ 'for (i = 0x80; i > 0; i >>= 1) {',
+ ]),
+ CodeGen(opt, 4*' ', [
+ Conditional2(opt, '', opt.c_std == 'C89', [
+ 'bit = !!({0});'.format(expr.And('crc', sym['cfg_msb_mask']).simplify()),
+ ], [
+ 'bit = {0};'.format(expr.And('crc', sym['cfg_msb_mask']).simplify()),
+ ]),
+ 'if (c & i) {',
+ CodeGen(opt, 4*' ', [
+ 'bit = !bit;',
+ ]),
+ '}',
+ 'crc <<= 1;',
+ 'if (bit) {',
+ CodeGen(opt, 4*' ', [
+ 'crc ^= {cfg_poly};'.format(**sym),
+ ]),
+ '}',
+ ]),
+ '}',
+ 'crc &= {cfg_mask};'.format(**sym)
+ ]),
+ '}',
+ 'return {0};'.format(expr.And('crc', sym['cfg_mask']).simplify()),
+ ]),
+ ]
+
+ if opt.algorithm == opt.algo_table_driven:
+ out += [
+ CodeGen(opt, 4*' ', [
+ 'unsigned int tbl_idx;',
+ '',
+ Conditional2(opt, '', opt.reflect_in == None, [
+ 'if (cfg->reflect_in) {',
+ CodeGen(opt, 4*' ', [
+ 'while (data_len--) {',
+ CodeGen(opt, 4*' ', [
+ _crc_table_core_algorithm_reflected(opt, sym),
+ 'd++;',
+ ]),
+ '}',
+ ]),
+ '} else {',
+ CodeGen(opt, 4*' ', [
+ 'while (data_len--) {',
+ CodeGen(opt, 4*' ', [
+ _crc_table_core_algorithm_nonreflected(opt, sym),
+ 'd++;',
+ ]),
+ '}',
+ ]),
+ '}',
+ ], [
+ Conditional(opt, '', opt.slice_by > 1, [
+ '/* Align to a multiple of {crc_slice_by} bytes */'.format(**sym),
+ 'while (data_len && (((uintptr_t)(const void *)d) % {crc_slice_by} != 0))'.format(**sym) + ' {',
+ CodeGen(opt, 4*' ', [
+ _crc_table_core_algorithm(opt, sym),
+ 'data_len--;',
+ ]),
+ '}',
+ '',
+ _crc_table_slice_by_algorithm(opt, sym),
+ '/* Remaining bytes with the standard algorithm */',
+ 'd = (const unsigned char *)d32;',
+ ]),
+ 'while (data_len--) {',
+ CodeGen(opt, 4*' ', [
+ _crc_table_core_algorithm(opt, sym),
+ ]),
+ '}',
+ ]),
+ 'return {0};'.format(expr.And('crc', sym['cfg_mask']).simplify()),
+ ]),
+ ]
+ out += [
+ '}',
+ ]
+ return out
+
+
+
+def _crc_finalize_function_gen(opt, sym):
+ """
+ Return the code for the finalize function.
+ """
+ if _use_inline_crc_finalize(opt):
+ return []
+ out = [
+ '', '',
+ _crc_finalize_function_def(opt, sym),
+ '{',
+ ]
+ if opt.algorithm in set([opt.algo_bit_by_bit, opt.algo_bit_by_bit_fast]):
+ out += [
+ Conditional(opt, 4*' ', opt.algorithm == opt.algo_bit_by_bit, [
+ 'unsigned int i;',
+ '{c_bool} bit;'.format(**sym),
+ '',
+ 'for (i = 0; i < ' + sym['cfg_width'] + '; i++) {',
+ CodeGen(opt, 4*' ', [
+ Conditional2(opt, '', opt.c_std == 'C89', [
+ 'bit = !!(crc & {cfg_msb_mask});'.format(**sym)
+ ], [
+ 'bit = crc & {cfg_msb_mask};'.format(**sym),
+ ]),
+ 'crc <<= 1;',
+ 'if (bit) {',
+ CodeGen(opt, 4*' ', [
+ 'crc ^= {cfg_poly};'.format(**sym),
+ ]),
+ '}',
+ ]),
+ '}',
+ Conditional(opt, '', opt.reflect_out is None, [
+ 'if (' + sym['cfg_reflect_out'] + ') {',
+ CodeGen(opt, 4*' ', [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ '}',
+ ]),
+ Conditional(opt, '', opt.reflect_out, [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ ]),
+
+ Conditional(opt, 4*' ', opt.algorithm == opt.algo_bit_by_bit_fast, [
+ Conditional(opt, '', opt.reflect_out is None, [
+ 'if (' + sym['cfg_reflect_out'] + ') {',
+ CodeGen(opt, 4*' ', [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ '}',
+ ]),
+ Conditional(opt, '', opt.reflect_out, [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ ]),
+ ]
+
+ if opt.algorithm == opt.algo_table_driven:
+ if opt.reflect_in is None or opt.reflect_out is None:
+ if opt.reflect_in is None and opt.reflect_out is None:
+ cond = 'cfg->reflect_in != cfg->reflect_out'
+ elif opt.reflect_out is None:
+ cond = '!' if opt.reflect_in else '' + 'cfg->reflect_out'
+ else:
+ cond = '!' if opt.reflect_out else '' + 'cfg->reflect_in'
+ out += [
+ CodeGen(opt, 4*' ', [
+ 'if (' + cond + ') {',
+ CodeGen(opt, 4*' ', [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]),
+ '}',
+ ]),
+ ]
+ elif opt.reflect_in != opt.reflect_out:
+ out += [
+ 'crc = {crc_reflect_function}(crc, {cfg_width});'.format(**sym),
+ ]
+ out += [
+ CodeGen(opt, 4*' ', [
+ 'return {0};'.format(expr.And(expr.Parenthesis(expr.Xor('crc', sym['cfg_xor_out'])), sym['cfg_mask']).simplify()),
+ ]),
+ '}',
+ ]
+ return out
+
+def _crc_table_core_algorithm(opt, sym):
+ """
+ Return the core of the table-driven algorithm.
+ """
+ out = []
+ out += [
+ Conditional2(opt, '', opt.reflect_in, [
+ _crc_table_core_algorithm_reflected(opt, sym),
+ ], [
+ _crc_table_core_algorithm_nonreflected(opt, sym),
+ ]),
+ 'd++;',
+ ]
+ return CodeGen(opt, '', out)
+
+def _crc_table_core_algorithm_reflected(opt, sym):
+ """
+ Return the core loop of the table-driven algorithm, reflected variant.
+ """
+ out = []
+ if opt.width is not None and opt.tbl_idx_width is not None and opt.width <= opt.tbl_idx_width:
+ crc_xor_expr = '0'
+ else:
+ crc_xor_expr = '(crc >> {cfg_table_idx_width})'.format(**sym)
+
+ if opt.tbl_idx_width == 8:
+ if opt.slice_by > 1:
+ crc_lookup = 'crc_table[0][tbl_idx]'
+ else:
+ crc_lookup = 'crc_table[tbl_idx]'
+ out += [
+ Conditional2(opt, '', opt.width is None or opt.width > 8, [
+ 'tbl_idx = (crc ^ *d) & {crc_table_mask};'.format(**sym),
+ ], [
+ 'tbl_idx = crc ^ *d;',
+ ]),
+ 'crc = {0};'.format(expr.And(expr.Parenthesis(expr.Xor(crc_lookup, expr.Parenthesis(expr.Shr('crc', sym['cfg_table_idx_width'])))), sym['cfg_mask']).simplify()),
+ ]
+ else:
+ crc_lookup = 'crc_table[tbl_idx & {crc_table_mask}]'.format(**sym)
+ for i in range(8 // opt.tbl_idx_width):
+ out += [
+ 'tbl_idx = {0};'.format(expr.Xor('crc', expr.Parenthesis(expr.Shr('*d', expr.Parenthesis(expr.Mul(i, sym['cfg_table_idx_width']))))).simplify()),
+ 'crc = {0};'.format(expr.Xor(crc_lookup, crc_xor_expr).simplify())
+ ]
+ return CodeGen(opt, '', out)
+
+def _crc_table_core_algorithm_nonreflected(opt, sym):
+ """
+ Return the core loop of the table-driven algorithm, non-reflected variant.
+ """
+ out = []
+ if opt.width == None:
+ crc_shifted_right = expr.Parenthesis(expr.Shr('crc', expr.Parenthesis(expr.Sub(sym['cfg_width'], sym['cfg_table_idx_width'])))).simplify()
+ elif opt.width < 8:
+ shift_val = opt.width - opt.tbl_idx_width
+ if shift_val < 0:
+ crc_shifted_right = expr.Parenthesis(expr.Shl('crc', -shift_val)).simplify()
+ else:
+ crc_shifted_right = expr.Parenthesis(expr.Shr('crc', shift_val)).simplify()
+ else:
+ shift_val = opt.width - opt.tbl_idx_width
+ crc_shifted_right = expr.Parenthesis(expr.Shr('crc', shift_val)).simplify()
+
+ if opt.width is not None and opt.tbl_idx_width is not None and opt.width <= opt.tbl_idx_width:
+ crc_xor_expr = '0'
+ else:
+ crc_xor_expr = '(crc << {cfg_table_idx_width})'.format(**sym)
+
+ if opt.tbl_idx_width == 8:
+ if opt.slice_by > 1:
+ crc_lookup = 'crc_table[0][tbl_idx]'
+ else:
+ crc_lookup = 'crc_table[tbl_idx]'
+ out += [
+ Conditional2(opt, '', opt.width is None or opt.width > 8, [
+ 'tbl_idx = {0};'.format(expr.And(expr.Parenthesis(expr.Xor(crc_shifted_right, '*d')), sym['crc_table_mask']).simplify())
+ ], [
+ 'tbl_idx = {0};'.format(expr.Xor(crc_shifted_right, '*d').simplify())
+ ]),
+ 'crc = {0};'.format(expr.And(expr.Parenthesis(expr.Xor(crc_lookup, crc_xor_expr)), sym['cfg_mask']).simplify())
+ ]
+ else:
+ crc_lookup = 'crc_table[tbl_idx & {crc_table_mask}]'.format(**sym)
+ for i in range(8 // opt.tbl_idx_width):
+ str_idx = '{0:d}'.format(8 - (i + 1) * opt.tbl_idx_width)
+ out += [
+ 'tbl_idx = {0};'.format(expr.Xor(crc_shifted_right, expr.Parenthesis(expr.Shr('*d', str_idx)))),
+ 'crc = {0};'.format(expr.Xor(crc_lookup, crc_xor_expr).simplify()),
+ ]
+ return CodeGen(opt, '', out)
+
+def _crc_table_slice_by_algorithm(opt, sym):
+ update_be = []
+ for i in range(opt.slice_by // 4):
+ vard = 'd{0}'.format(opt.slice_by // 4 - i)
+ for j in range(4):
+ idx1 = i * 4 + j
+ idx2 = expr.And(expr.Parenthesis(expr.Shr(vard, j*8)), expr.Terminal(255, '0xffu')).simplify()
+ update_be.append('crc_table[{0}][{1}]{2}'.format(idx1, idx2, ' ^' if idx1 < opt.slice_by - 1 else ';'))
+
+ update_le = []
+ for i in range(opt.slice_by // 4):
+ vard = 'd{0}'.format(opt.slice_by // 4 - i)
+ for j in range(4):
+ idx1 = i * 4 + j
+ idx2 = expr.And(expr.Parenthesis(expr.Shr(vard, 24 - j*8)), expr.Terminal(255, '0xffu')).simplify()
+ update_le.append('crc_table[{0}][{1}]{2}'.format(idx1, idx2, ' ^' if idx1 < opt.slice_by - 1 else ';'))
+
+ out = [
+ 'const uint32_t *d32 = (const uint32_t *)d;',
+ 'while (data_len >= {crc_slice_by})'.format(**sym),
+ '{',
+ CodeGen(opt, 4*' ', [
+ CodeGen(opt, None, [
+ '#if __BYTE_ORDER == __BIG_ENDIAN',
+ ]),
+ '{crc_t} d1 = *d32++ ^ le16toh(crc);'.format(**sym),
+ Conditional(opt, '', opt.slice_by >= 8, [
+ '{crc_t} d2 = *d32++;'.format(**sym),
+ ]),
+ Conditional(opt, '', opt.slice_by >= 16, [
+ '{crc_t} d3 = *d32++;'.format(**sym),
+ '{crc_t} d4 = *d32++;'.format(**sym),
+ ]),
+ 'crc =',
+ CodeGen(opt, 4*' ', update_be),
+ CodeGen(opt, None, [
+ '#else',
+ ]),
+ '{crc_t} d1 = *d32++ ^ crc;'.format(**sym),
+ Conditional(opt, '', opt.slice_by >= 8, [
+ '{crc_t} d2 = *d32++;'.format(**sym),
+ ]),
+ Conditional(opt, '', opt.slice_by >= 16, [
+ '{crc_t} d3 = *d32++;'.format(**sym),
+ '{crc_t} d4 = *d32++;'.format(**sym),
+ ]),
+ 'crc =',
+ CodeGen(opt, 4*' ', update_le),
+ CodeGen(opt, None, [
+ '#endif',
+ ]),
+ '',
+ 'data_len -= {crc_slice_by};'.format(**sym),
+ ]),
+ '}',
+ '',
+ ]
+ return CodeGen(opt, '', out)
diff --git a/third_party/pycrc/pycrc/expr.py b/third_party/pycrc/pycrc/expr.py
new file mode 100644
index 0000000..a15742c
--- /dev/null
+++ b/third_party/pycrc/pycrc/expr.py
@@ -0,0 +1,398 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+This modules simplifies an expression.
+
+ import pycrc.expr as exp
+
+ my_expr = exp.Xor('var', exp.Parenthesis(exp.And('0x700', 4)))
+ print('"{}" -> "{}"'.format(my_expr, my_expr.simplify()))
+"""
+
+
+def _classify(val):
+ """
+ Creates a Terminal object if the parameter is a string or an integer.
+ """
+ if type(val) is int:
+ return Terminal(val)
+ if type(val) is str:
+ if val.isdigit():
+ return Terminal(int(val), val)
+ if val[:2].lower() == '0x':
+ return Terminal(int(val, 16), val)
+ return Terminal(val)
+ return val
+
+
+class Expression(object):
+ """
+ Base class for all expressions.
+ """
+ def is_int(self, val = None):
+ return False
+
+
+class Terminal(Expression):
+ """
+ A terminal object.
+ """
+ def __init__(self, val, pretty = None):
+ """
+ Construct a Terminal.
+ The val variable is usually a string or an integer. Integers may also
+ be passed as strings. The pretty-printer will use the string when
+ formatting the expression.
+ """
+ self.val = val
+ self.pretty = pretty
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ if self.pretty is None:
+ return str(self.val)
+ return self.pretty
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ return self
+
+ def is_int(self, val = None):
+ """
+ Return True if the value of this Terminal is an integer.
+ """
+ if type(self.val) is int:
+ return val is None or self.val == val
+ return False
+
+
+class FunctionCall(Expression):
+ """
+ Represent a function call
+ """
+ def __init__(self, name, args):
+ """
+ Construct a function call object.
+ """
+ self.name = _classify(name)
+ self.args = [_classify(arg) for arg in args]
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.name) + '(' + ', '.join([str(arg) for arg in self.args]) + ')'
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ args = [arg.simplify() for arg in self.args]
+ return FunctionCall(self.name, args)
+
+
+class Parenthesis(Expression):
+ """
+ Represent a pair of round brackets.
+ """
+ def __init__(self, val):
+ """
+ Construct a parenthesis object.
+ """
+ self.val = _classify(val)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ val = self.val.simplify()
+ if type(val) is Terminal:
+ return val
+ return Parenthesis(val)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return '(' + str(self.val) + ')'
+
+
+class Add(Expression):
+ """
+ Represent an addition of operands.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct an addition object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val + rhs.val)
+ if lhs.is_int(0):
+ return rhs
+ if rhs.is_int(0):
+ return lhs
+ return Add(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' + ' + str(self.rhs)
+
+
+class Sub(Expression):
+ """
+ Represent a subtraction of operands.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct subtraction object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val - rhs.val)
+ if lhs.is_int(0):
+ return rhs
+ if rhs.is_int(0):
+ return lhs
+ return Sub(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' - ' + str(self.rhs)
+
+
+class Mul(Expression):
+ """
+ Represent the multiplication of operands.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct a multiplication object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val * rhs.val)
+ if lhs.is_int(0) or rhs.is_int(0):
+ return Terminal(0)
+ if lhs.is_int(1):
+ return rhs
+ if rhs.is_int(1):
+ return lhs
+ return Mul(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' * ' + str(self.rhs)
+
+
+class Shl(Expression):
+ """
+ Shift left operation.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct a shift left object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val << rhs.val)
+ if lhs.is_int(0):
+ return Terminal(0)
+ if rhs.is_int(0):
+ return lhs
+ return Shl(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' << ' + str(self.rhs)
+
+
+class Shr(Expression):
+ """
+ Shift right operation.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct a shift right object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val >> rhs.val)
+ if lhs.is_int(0):
+ return Terminal(0)
+ if rhs.is_int(0):
+ return lhs
+ return Shr(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' >> ' + str(self.rhs)
+
+
+class Or(Expression):
+ """
+ Logical or operation.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct a logical and object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val | rhs.val)
+ if lhs.is_int(0):
+ return rhs
+ if rhs.is_int(0):
+ return lhs
+ return Or(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' | ' + str(self.rhs)
+
+
+class And(Expression):
+ """
+ Logical and operation.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct a logical and object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val & rhs.val)
+ if lhs.is_int(0) or rhs.is_int(0):
+ return Terminal(0)
+ return And(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' & ' + str(self.rhs)
+
+
+class Xor(Expression):
+ """
+ Logical xor operation.
+ """
+ def __init__(self, lhs, rhs):
+ """
+ Construct a logical xor object.
+ """
+ self.lhs = _classify(lhs)
+ self.rhs = _classify(rhs)
+
+ def simplify(self):
+ """
+ Return a simplified version of this sub-expression.
+ """
+ lhs = self.lhs.simplify()
+ rhs = self.rhs.simplify()
+ if lhs.is_int() and rhs.is_int():
+ return Terminal(lhs.val ^ rhs.val)
+ if lhs.is_int(0):
+ return rhs
+ if rhs.is_int(0):
+ return lhs
+ return Xor(lhs, rhs)
+
+ def __str__(self):
+ """
+ Return the string expression of this object.
+ """
+ return str(self.lhs) + ' ^ ' + str(self.rhs)
diff --git a/third_party/pycrc/pycrc/main.py b/third_party/pycrc/pycrc/main.py
new file mode 100644
index 0000000..d446362
--- /dev/null
+++ b/third_party/pycrc/pycrc/main.py
@@ -0,0 +1,241 @@
+#!/usr/bin/env python
+
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+pycrc is a fully parameterisable Cyclic Redundancy Check (CRC) calculation
+utility and C source code generator written in Python.
+
+It can:
+ - generate the checksum of a string
+ - generate the checksum of a file
+ - generate the C header file and source of any of the algorithms below
+
+It supports the following CRC algorithms:
+ - bit-by-bit the basic algorithm which operates bit by bit on the
+ augmented message
+ - bit-by-bit-fast a variation of the simple bit-by-bit algorithm
+ - table-driven the standard table driven algorithm
+"""
+
+from __future__ import print_function
+from pycrc import progname, version, url
+from pycrc.opt import Options
+from pycrc.algorithms import Crc
+import pycrc.codegen as cg
+import binascii
+import sys
+
+
+
+def print_parameters(opt):
+ """
+ Generate a string with the options pretty-printed (used in the --verbose mode).
+ """
+ return str(cg.ParamBlock(opt, ''))
+
+
+def check_string(opt):
+ """
+ Return the calculated CRC sum of a string.
+ """
+ error = False
+ if opt.undefined_crc_parameters:
+ sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname))
+ sys.exit(1)
+ if opt.algorithm == 0:
+ opt.algorithm = opt.algo_bit_by_bit | opt.algo_bit_by_bit_fast | opt.algo_table_driven
+
+ alg = Crc(
+ width=opt.width, poly=opt.poly,
+ reflect_in=opt.reflect_in, xor_in=opt.xor_in,
+ reflect_out=opt.reflect_out, xor_out=opt.xor_out,
+ table_idx_width=opt.tbl_idx_width)
+
+ crc = None
+ if opt.algorithm & opt.algo_bit_by_bit:
+ bbb_crc = alg.bit_by_bit(opt.check_string)
+ if crc != None and bbb_crc != crc:
+ error = True
+ crc = bbb_crc
+ if opt.algorithm & opt.algo_bit_by_bit_fast:
+ bbf_crc = alg.bit_by_bit_fast(opt.check_string)
+ if crc != None and bbf_crc != crc:
+ error = True
+ crc = bbf_crc
+ if opt.algorithm & opt.algo_table_driven:
+ # no point making the python implementation slower by using less than 8 bits as index.
+ opt.tbl_idx_width = 8
+ tbl_crc = alg.table_driven(opt.check_string)
+ if crc != None and tbl_crc != crc:
+ error = True
+ crc = tbl_crc
+
+ if error:
+ sys.stderr.write("{0:s}: error: different checksums!\n".format(progname))
+ if opt.algorithm & opt.algo_bit_by_bit:
+ sys.stderr.write(" bit-by-bit: {0:#x}\n".format(bbb_crc))
+ if opt.algorithm & opt.algo_bit_by_bit_fast:
+ sys.stderr.write(" bit-by-bit-fast: {0:#x}\n".format(bbf_crc))
+ if opt.algorithm & opt.algo_table_driven:
+ sys.stderr.write(" table_driven: {0:#x}\n".format(tbl_crc))
+ sys.exit(1)
+ return crc
+
+
+def check_hexstring(opt):
+ """
+ Return the calculated CRC sum of a hex string.
+ """
+ if opt.undefined_crc_parameters:
+ sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname))
+ sys.exit(1)
+ if len(opt.check_string) % 2 != 0:
+ opt.check_string = "0" + opt.check_string
+ if sys.version_info >= (3, 0):
+ opt.check_string = bytes(opt.check_string, 'utf_8')
+ try:
+ check_str = bytearray(binascii.unhexlify(opt.check_string))
+ except TypeError:
+ sys.stderr.write(
+ "{0:s}: error: invalid hex string {1:s}\n".format(progname, opt.check_string))
+ sys.exit(1)
+
+ opt.check_string = check_str
+ return check_string(opt)
+
+
+def crc_file_update(alg, register, check_bytes):
+ """
+ Update the CRC using the bit-by-bit-fast CRC algorithm.
+ """
+ # If the input data is a string, convert to bytes.
+ if isinstance(check_bytes, str):
+ check_bytes = bytearray(check_bytes, 'utf_8')
+
+ for octet in check_bytes:
+ if alg.reflect_in:
+ octet = alg.reflect(octet, 8)
+ for j in range(8):
+ bit = register & alg.msb_mask
+ register <<= 1
+ if octet & (0x80 >> j):
+ bit ^= alg.msb_mask
+ if bit:
+ register ^= alg.poly
+ register &= alg.mask
+ return register
+
+
+def check_file(opt):
+ """
+ Calculate the CRC of a file.
+ This algorithm uses the table_driven CRC algorithm.
+ """
+ if opt.undefined_crc_parameters:
+ sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname))
+ sys.exit(1)
+ alg = Crc(
+ width=opt.width, poly=opt.poly,
+ reflect_in=opt.reflect_in, xor_in=opt.xor_in,
+ reflect_out=opt.reflect_out, xor_out=opt.xor_out,
+ table_idx_width=opt.tbl_idx_width)
+
+ if not opt.reflect_in:
+ register = opt.xor_in
+ else:
+ register = alg.reflect(opt.xor_in, opt.width)
+
+ try:
+ with open(opt.check_file, 'rb') as f:
+ check_bytes = bytearray(f.read(1024))
+ while check_bytes != b"":
+ register = crc_file_update(alg, register, check_bytes)
+ check_bytes = bytearray(f.read(1024))
+ except IOError:
+ sys.stderr.write(
+ "{0:s}: error: can't open file {1:s}\n".format(progname, opt.check_file))
+ sys.exit(1)
+
+ if opt.reflect_out:
+ register = alg.reflect(register, opt.width)
+ register = register ^ opt.xor_out
+ return register
+
+
+def write_file(filename, out_str):
+ """
+ Write the content of out_str to filename.
+ """
+ try:
+ out_file = open(filename, "w")
+ out_file.write(out_str)
+ out_file.close()
+ except IOError:
+ sys.stderr.write("{0:s}: error: cannot write to file {1:s}\n".format(progname, filename))
+ sys.exit(1)
+
+
+def main():
+ """
+ Main function.
+ """
+ opt = Options(progname, version, url)
+ opt.parse(sys.argv[1:])
+ if opt.verbose:
+ print(print_parameters(opt))
+ if opt.action == opt.action_check_str:
+ crc = check_string(opt)
+ print("{0:#x}".format(crc))
+ if opt.action == opt.action_check_hex_str:
+ crc = check_hexstring(opt)
+ print("{0:#x}".format(crc))
+ if opt.action == opt.action_check_file:
+ crc = check_file(opt)
+ print("{0:#x}".format(crc))
+ if opt.action in set([
+ opt.action_generate_h, opt.action_generate_c, opt.action_generate_c_main,
+ opt.action_generate_table]):
+ if opt.action == opt.action_generate_h:
+ in_str = "$h_template"
+ elif opt.action == opt.action_generate_c:
+ in_str = "$c_template"
+ elif opt.action == opt.action_generate_c_main:
+ in_str = "$c_template\n\n$main_template"
+ elif opt.action == opt.action_generate_table:
+ in_str = "$crc_table_init"
+ else:
+ sys.stderr.write(
+ "{0:s}: error: unknown action. Please file a bug report!\n".format(progname))
+ sys.exit(1)
+ out = str(cg.File(opt, ''))
+ if opt.output_file == None:
+ print(out)
+ else:
+ write_file(opt.output_file, out)
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/third_party/pycrc/pycrc/models.py b/third_party/pycrc/pycrc/models.py
new file mode 100644
index 0000000..d12c91c
--- /dev/null
+++ b/third_party/pycrc/pycrc/models.py
@@ -0,0 +1,334 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+Collection of CRC models. This module contains the CRC models known to pycrc.
+
+To print the parameters of a particular model:
+
+ from pycrc.models import CrcModels
+
+ models = CrcModels()
+ print(", ".join(models.names()))
+ m = models.get_params("crc-32")
+ if m != None:
+ print("Width: {width:d}".format(**m))
+ print("Poly: {poly:#x}".format(**m))
+ print("ReflectIn: {reflect_in}".format(**m))
+ print("XorIn: {xor_in:#x}".format(**m))
+ print("ReflectOut: {reflect_out}".format(**m))
+ print("XorOut: {xor_out:#x}".format(**m))
+ print("Check: {check:#x}".format(**m))
+ else:
+ print("model not found.")
+"""
+
+
+
+class CrcModels(object):
+ """
+ CRC Models.
+
+ All models are defined as constant class variables.
+ """
+
+ models = []
+
+ models.append({
+ 'name': 'crc-5',
+ 'width': 5,
+ 'poly': 0x05,
+ 'reflect_in': True,
+ 'xor_in': 0x1f,
+ 'reflect_out': True,
+ 'xor_out': 0x1f,
+ 'check': 0x19,
+ })
+ models.append({
+ 'name': 'crc-8',
+ 'width': 8,
+ 'poly': 0x07,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0xf4,
+ })
+ models.append({
+ 'name': 'dallas-1-wire',
+ 'width': 8,
+ 'poly': 0x31,
+ 'reflect_in': True,
+ 'xor_in': 0x0,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0xa1,
+ })
+ models.append({
+ 'name': 'crc-12-3gpp',
+ 'width': 12,
+ 'poly': 0x80f,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0xdaf,
+ })
+ models.append({
+ 'name': 'crc-15',
+ 'width': 15,
+ 'poly': 0x4599,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0x59e,
+ })
+ models.append({
+ 'name': 'crc-16',
+ 'width': 16,
+ 'poly': 0x8005,
+ 'reflect_in': True,
+ 'xor_in': 0x0,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0xbb3d,
+ })
+ models.append({
+ 'name': 'crc-16-usb',
+ 'width': 16,
+ 'poly': 0x8005,
+ 'reflect_in': True,
+ 'xor_in': 0xffff,
+ 'reflect_out': True,
+ 'xor_out': 0xffff,
+ 'check': 0xb4c8,
+ })
+ models.append({
+ 'name': 'crc-16-modbus',
+ 'width': 16,
+ 'poly': 0x8005,
+ 'reflect_in': True,
+ 'xor_in': 0xffff,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0x4b37,
+ })
+ models.append({
+ 'name': 'crc-16-genibus',
+ 'width': 16,
+ 'poly': 0x1021,
+ 'reflect_in': False,
+ 'xor_in': 0xffff,
+ 'reflect_out': False,
+ 'xor_out': 0xffff,
+ 'check': 0xd64e,
+ })
+ models.append({
+ 'name': 'crc-16-ccitt',
+ 'width': 16,
+ 'poly': 0x1021,
+ 'reflect_in': False,
+ 'xor_in': 0x1d0f,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0xe5cc,
+ })
+ models.append({
+ 'name': 'r-crc-16',
+ 'width': 16,
+ 'poly': 0x0589,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0x0001,
+ 'check': 0x007e,
+ })
+ models.append({
+ 'name': 'kermit',
+ 'width': 16,
+ 'poly': 0x1021,
+ 'reflect_in': True,
+ 'xor_in': 0x0,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0x2189,
+ })
+ models.append({
+ 'name': 'x-25',
+ 'width': 16,
+ 'poly': 0x1021,
+ 'reflect_in': True,
+ 'xor_in': 0xffff,
+ 'reflect_out': True,
+ 'xor_out': 0xffff,
+ 'check': 0x906e,
+ })
+ models.append({
+ 'name': 'xmodem',
+ 'width': 16,
+ 'poly': 0x1021,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0x31c3,
+ })
+ models.append({
+ 'name': 'zmodem',
+ 'width': 16,
+ 'poly': 0x1021,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0x31c3,
+ })
+ models.append({
+ 'name': 'crc-24',
+ 'width': 24,
+ 'poly': 0x864cfb,
+ 'reflect_in': False,
+ 'xor_in': 0xb704ce,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0x21cf02,
+ })
+ models.append({
+ 'name': 'crc-32',
+ 'width': 32,
+ 'poly': 0x4c11db7,
+ 'reflect_in': True,
+ 'xor_in': 0xffffffff,
+ 'reflect_out': True,
+ 'xor_out': 0xffffffff,
+ 'check': 0xcbf43926,
+ })
+ models.append({
+ 'name': 'crc-32c',
+ 'width': 32,
+ 'poly': 0x1edc6f41,
+ 'reflect_in': True,
+ 'xor_in': 0xffffffff,
+ 'reflect_out': True,
+ 'xor_out': 0xffffffff,
+ 'check': 0xe3069283,
+ })
+ models.append({
+ 'name': 'crc-32-mpeg',
+ 'width': 32,
+ 'poly': 0x4c11db7,
+ 'reflect_in': False,
+ 'xor_in': 0xffffffff,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0x0376e6e7,
+ })
+ models.append({
+ 'name': 'crc-32-bzip2',
+ 'width': 32,
+ 'poly': 0x04c11db7,
+ 'reflect_in': False,
+ 'xor_in': 0xffffffff,
+ 'reflect_out': False,
+ 'xor_out': 0xffffffff,
+ 'check': 0xfc891918,
+ })
+ models.append({
+ 'name': 'posix',
+ 'width': 32,
+ 'poly': 0x4c11db7,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0xffffffff,
+ 'check': 0x765e7680,
+ })
+ models.append({
+ 'name': 'jam',
+ 'width': 32,
+ 'poly': 0x4c11db7,
+ 'reflect_in': True,
+ 'xor_in': 0xffffffff,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0x340bc6d9,
+ })
+ models.append({
+ 'name': 'xfer',
+ 'width': 32,
+ 'poly': 0x000000af,
+ 'reflect_in': False,
+ 'xor_in': 0x0,
+ 'reflect_out': False,
+ 'xor_out': 0x0,
+ 'check': 0xbd0be338,
+ })
+ models.append({
+ 'name': 'crc-64',
+ 'width': 64,
+ 'poly': 0x000000000000001b,
+ 'reflect_in': True,
+ 'xor_in': 0x0,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0x46a5a9388a5beffe,
+ })
+ models.append({
+ 'name': 'crc-64-jones',
+ 'width': 64,
+ 'poly': 0xad93d23594c935a9,
+ 'reflect_in': True,
+ 'xor_in': 0xffffffffffffffff,
+ 'reflect_out': True,
+ 'xor_out': 0x0,
+ 'check': 0xcaa717168609f281,
+ })
+ models.append({
+ 'name': 'crc-64-xz',
+ 'width': 64,
+ 'poly': 0x42f0e1eba9ea3693,
+ 'reflect_in': True,
+ 'xor_in': 0xffffffffffffffff,
+ 'reflect_out': True,
+ 'xor_out': 0xffffffffffffffff,
+ 'check': 0x995dc9bbdf1939fa,
+ })
+
+
+ def names(self):
+ """
+ This function returns the list of supported CRC models.
+ """
+ return [model['name'] for model in self.models]
+
+
+ def get_params(self, model):
+ """
+ This function returns the parameters of a given model.
+ """
+ model = model.lower()
+ for i in self.models:
+ if i['name'] == model:
+ return i
+ return None
diff --git a/third_party/pycrc/pycrc/opt.py b/third_party/pycrc/pycrc/opt.py
new file mode 100644
index 0000000..7868b2e
--- /dev/null
+++ b/third_party/pycrc/pycrc/opt.py
@@ -0,0 +1,484 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+Option parsing library for pycrc.
+use as follows:
+
+ from pycrc.opt import Options
+
+ opt = Options()
+ opt.parse(sys.argv[1:])
+"""
+
+from optparse import OptionParser, Option, OptionValueError
+from copy import copy
+import sys
+from pycrc.models import CrcModels
+
+
+class Options(object):
+ """
+ The options parsing and validating class.
+ """
+ # pylint: disable=too-many-instance-attributes, too-few-public-methods
+
+ # Bitmap of the algorithms
+ algo_none = 0x00
+ algo_bit_by_bit = 0x01
+ algo_bit_by_bit_fast = 0x02
+ algo_table_driven = 0x08
+
+ action_check_str = 0x01
+ action_check_hex_str = 0x02
+ action_check_file = 0x03
+ action_generate_h = 0x04
+ action_generate_c = 0x05
+ action_generate_c_main = 0x06
+ action_generate_table = 0x07
+
+
+ def __init__(self, progname='pycrc', version=None, url=None):
+ self.program_name = progname
+ self.version = version
+ self.version_str = "{0:s} v{1:s}".format(progname, version)
+ self.web_address = url
+
+ self.width = None
+ self.poly = None
+ self.reflect_in = None
+ self.xor_in = None
+ self.reflect_out = None
+ self.xor_out = None
+ self.tbl_idx_width = 8
+ self.tbl_width = 1 << self.tbl_idx_width
+ self.slice_by = 1
+ self.verbose = False
+ self.check_string = "123456789"
+ self.msb_mask = None
+ self.mask = None
+
+ self.algorithm = self.algo_none
+ self.symbol_prefix = "crc_"
+ self.crc_type = None
+ self.include_files = []
+ self.output_file = None
+ self.action = self.action_check_str
+ self.check_file = None
+ self.c_std = None
+ self.undefined_crc_parameters = False
+
+
+ def parse(self, argv=None):
+ """
+ Parses and validates the options given as arguments
+ """
+ # pylint: disable=too-many-branches, too-many-statements
+
+ usage = """python %prog [OPTIONS]
+
+To calculate the checksum of a string or hexadecimal data:
+ python %prog [model] --check-string "123456789"
+ python %prog [model] --check-hexstring "313233343536373839"
+
+To calculate the checksum of a file:
+ python %prog [model] --check-file filename
+
+To generate the C source code and write it to filename:
+ python %prog [model] --generate c -o filename
+
+The model can be defined either with the --model switch or by specifying each
+of the following parameters:
+ --width --poly --reflect-in --xor-in --reflect-out --xor-out"""
+
+ models = CrcModels()
+ model_list = ", ".join(models.names())
+ parser = OptionParser(option_class=MyOption, usage=usage, version=self.version_str)
+ parser.add_option(
+ "-v", "--verbose",
+ action="store_true", dest="verbose", default=False,
+ help="be more verbose; print the value of the parameters "
+ "and the chosen model to stdout")
+ parser.add_option(
+ "--check-string",
+ action="store", type="string", dest="check_string",
+ help="calculate the checksum of a string (default: '123456789')",
+ metavar="STRING")
+ parser.add_option(
+ "--check-hexstring",
+ action="store", type="string", dest="check_hexstring",
+ help="calculate the checksum of a hexadecimal number string",
+ metavar="STRING")
+ parser.add_option(
+ "--check-file",
+ action="store", type="string", dest="check_file",
+ help="calculate the checksum of a file",
+ metavar="FILE")
+ parser.add_option(
+ "--generate",
+ action="store", type="string", dest="generate", default=None,
+ help="generate C source code; choose the type from {h, c, c-main, table}",
+ metavar="CODE")
+ parser.add_option(
+ "--std",
+ action="store", type="string", dest="c_std", default="C99",
+ help="choose the C dialect of the generated code from {C89, ANSI, C99}",
+ metavar="STD")
+ parser.add_option(
+ "--algorithm",
+ action="store", type="string", dest="algorithm", default="all",
+ help="choose an algorithm from "
+ "{bit-by-bit, bbb, bit-by-bit-fast, bbf, table-driven, tbl, all}",
+ metavar="ALGO")
+ parser.add_option(
+ "--model",
+ action="callback", callback=_model_cb, type="string", dest="model", default=None,
+ help="choose a parameter set from {{{0:s}}}".format(model_list),
+ metavar="MODEL")
+ parser.add_option(
+ "--width",
+ action="store", type="hex", dest="width",
+ help="use NUM bits in the polynomial",
+ metavar="NUM")
+ parser.add_option(
+ "--poly",
+ action="store", type="hex", dest="poly",
+ help="use HEX as polynomial",
+ metavar="HEX")
+ parser.add_option(
+ "--reflect-in",
+ action="store", type="bool", dest="reflect_in",
+ help="reflect the octets in the input message",
+ metavar="BOOL")
+ parser.add_option(
+ "--xor-in",
+ action="store", type="hex", dest="xor_in",
+ help="use HEX as initial value",
+ metavar="HEX")
+ parser.add_option(
+ "--reflect-out",
+ action="store", type="bool", dest="reflect_out",
+ help="reflect the resulting checksum before applying the --xor-out value",
+ metavar="BOOL")
+ parser.add_option(
+ "--xor-out",
+ action="store", type="hex", dest="xor_out",
+ help="xor the final CRC value with HEX",
+ metavar="HEX")
+ parser.add_option(
+ "--slice-by",
+ action="store", type="int", dest="slice_by",
+ help="read NUM bytes at a time from the input. NUM must be one of the values {4, 8, 16}",
+ metavar="NUM")
+ parser.add_option(
+ "--table-idx-width",
+ action="store", type="int", dest="table_idx_width",
+ help="use NUM bits to index the CRC table; NUM must be one of the values {1, 2, 4, 8}",
+ metavar="NUM")
+ parser.add_option(
+ "--force-poly",
+ action="store_true", dest="force_poly", default=False,
+ help="override any errors about possibly unsuitable polynoms")
+ parser.add_option(
+ "--symbol-prefix",
+ action="store", type="string", dest="symbol_prefix",
+ help="when generating source code, use STRING as prefix to the exported C symbols",
+ metavar="STRING")
+ parser.add_option(
+ "--crc-type",
+ action="store", type="string", dest="crc_type",
+ help="when generating source code, use STRING as crc_t type",
+ metavar="STRING")
+ parser.add_option(
+ "--include-file",
+ action="append", type="string", dest="include_files",
+ help="when generating source code, include also FILE as header file; "
+ "can be specified multiple times",
+ metavar="FILE")
+ parser.add_option(
+ "-o", "--output",
+ action="store", type="string", dest="output_file",
+ help="write the generated code to file instead to stdout",
+ metavar="FILE")
+
+ (options, args) = parser.parse_args(argv)
+
+ if options.c_std != None:
+ std = options.c_std.upper()
+ if std == "ANSI" or std == "C89":
+ self.c_std = "C89"
+ elif std == "C99":
+ self.c_std = std
+ else:
+ self.__error("unknown C standard {0:s}".format(options.c_std))
+
+ undefined_params = []
+ if options.width != None:
+ self.width = options.width
+ else:
+ undefined_params.append("--width")
+ if options.poly != None:
+ self.poly = options.poly
+ else:
+ undefined_params.append("--poly")
+ if options.reflect_in != None:
+ self.reflect_in = options.reflect_in
+ else:
+ undefined_params.append("--reflect-in")
+ if options.xor_in != None:
+ self.xor_in = options.xor_in
+ else:
+ undefined_params.append("--xor-in")
+ if options.reflect_out != None:
+ self.reflect_out = options.reflect_out
+ else:
+ undefined_params.append("--reflect-out")
+ if options.xor_out != None:
+ self.xor_out = options.xor_out
+ else:
+ undefined_params.append("--xor-out")
+
+ if options.table_idx_width != None:
+ if options.table_idx_width in set((1, 2, 4, 8)):
+ self.tbl_idx_width = options.table_idx_width
+ self.tbl_width = 1 << options.table_idx_width
+ else:
+ self.__error("unsupported table-idx-width {0:d}".format(options.table_idx_width))
+
+ if self.poly != None and self.poly % 2 == 0 and not options.force_poly:
+ self.__error("even polinomials are not allowed by default. Use --force-poly to override this.")
+
+ if self.width != None:
+ if self.width <= 0:
+ self.__error("Width must be strictly positive")
+ self.msb_mask = 0x1 << (self.width - 1)
+ self.mask = ((self.msb_mask - 1) << 1) | 1
+ if self.poly != None and self.poly >> (self.width + 1) != 0 and not options.force_poly:
+ self.__error("the polynomial is wider than the supplied Width. Use --force-poly to override this.")
+ if self.poly != None:
+ self.poly = self.poly & self.mask
+ if self.xor_in != None:
+ self.xor_in = self.xor_in & self.mask
+ if self.xor_out != None:
+ self.xor_out = self.xor_out & self.mask
+ else:
+ self.msb_mask = None
+ self.mask = None
+
+ if self.width == None or \
+ self.poly == None or \
+ self.reflect_in == None or \
+ self.xor_in == None or \
+ self.reflect_out == None or \
+ self.xor_out == None:
+ self.undefined_crc_parameters = True
+ else:
+ self.undefined_crc_parameters = False
+
+ if options.slice_by != None:
+ if options.slice_by in set((4, 8, 16)):
+ self.slice_by = options.slice_by
+ else:
+ self.__error("unsupported slice-by {0:d}".format(options.slice_by))
+ if self.undefined_crc_parameters:
+ self.__error("slice-by is only implemented for fully defined models")
+ if self.tbl_idx_width != 8:
+ self.__error("slice-by is only implemented for table-idx-width=8")
+ # FIXME tp: Fix corner cases and disable the following tests
+ if self.width < 8:
+ self.__warning("disabling slice-by for width {0}".format(self.width))
+ self.slice_by = 1
+ if self.width < 16:
+ self.__warning("disabling slice-by for width {0}".format(self.width))
+ self.slice_by = 1
+ if self.width > 32:
+ self.__warning("disabling slice-by for width {0}".format(self.width))
+ self.slice_by = 1
+ if not self.reflect_in:
+ self.__warning("disabling slice-by for non-reflected algorithm")
+ self.slice_by = 1
+# FIXME tp: reintroduce this?
+# if self.width % 8 != 0:
+# self.__error("slice-by is only implemented for width multiples of 8")
+# if options.slice_by < self.width / 8:
+# self.__error("slice-by must be greater or equal width / 8")
+ if self.c_std == "C89":
+ self.__error("--slice-by not supported for C89")
+
+ if options.algorithm != None:
+ alg = options.algorithm.lower()
+ if alg in set(["bit-by-bit", "bbb", "all"]):
+ self.algorithm |= self.algo_bit_by_bit
+ if alg in set(["bit-by-bit-fast", "bbf", "all"]):
+ self.algorithm |= self.algo_bit_by_bit_fast
+ if alg in set(["table-driven", "tbl", "all"]):
+ self.algorithm |= self.algo_table_driven
+ if self.algorithm == 0:
+ self.__error("unknown algorithm {0:s}".format(options.algorithm))
+
+ if options.symbol_prefix != None:
+ self.symbol_prefix = options.symbol_prefix
+ if options.include_files != None:
+ self.include_files = options.include_files
+ if options.crc_type != None:
+ self.crc_type = options.crc_type
+ if options.output_file != None:
+ self.output_file = options.output_file
+ op_count = 0
+ if options.check_string != None:
+ self.action = self.action_check_str
+ self.check_string = options.check_string
+ op_count += 1
+ if options.check_hexstring != None:
+ self.action = self.action_check_hex_str
+ self.check_string = options.check_hexstring
+ op_count += 1
+ if options.check_file != None:
+ self.action = self.action_check_file
+ self.check_file = options.check_file
+ op_count += 1
+ if options.generate != None:
+ arg = options.generate.lower()
+ if arg == 'h':
+ self.action = self.action_generate_h
+ elif arg == 'c':
+ self.action = self.action_generate_c
+ elif arg == 'c-main':
+ self.action = self.action_generate_c_main
+ elif arg == 'table':
+ self.action = self.action_generate_table
+ else:
+ self.__error("don't know how to generate {0:s}".format(options.generate))
+ op_count += 1
+
+ if self.action == self.action_generate_table:
+ if self.algorithm & self.algo_table_driven == 0:
+ self.__error("the --generate table option is incompatible "
+ "with the --algorithm option")
+ self.algorithm = self.algo_table_driven
+ elif self.algorithm not in set(
+ [self.algo_bit_by_bit, self.algo_bit_by_bit_fast, self.algo_table_driven]):
+ self.__error("select an algorithm to be used in the generated file")
+ else:
+ if self.tbl_idx_width != 8:
+ self.__warning("reverting to Table Index Width = 8 "
+ "for internal CRC calculation")
+ self.tbl_idx_width = 8
+ self.tbl_width = 1 << options.table_idx_width
+ if op_count == 0:
+ self.action = self.action_check_str
+ if op_count > 1:
+ self.__error("too many actions specified")
+
+ if len(args) != 0:
+ self.__error("unrecognized argument(s): {0:s}".format(" ".join(args)))
+
+ def_params_acts = (self.action_check_str, self.action_check_hex_str,
+ self.action_check_file, self.action_generate_table)
+ if self.undefined_crc_parameters and self.action in set(def_params_acts):
+ self.__error("undefined parameters: Add {0:s} or use --model"
+ .format(", ".join(undefined_params)))
+ self.verbose = options.verbose
+
+
+
+ def __warning(self, message):
+ """
+ Print a warning message to stderr.
+ """
+ sys.stderr.write(
+ "{0:s}: warning: {1:s}\n".format(self.program_name, message))
+
+
+
+ def __error(self, message):
+ """
+ Print a error message to stderr and terminate the program.
+ """
+ sys.stderr.write(
+ "{0:s}: error: {1:s}\n".format(self.program_name, message))
+ sys.exit(1)
+
+
+def _model_cb(option, opt_str, value, parser):
+ """
+ This function sets up the single parameters if the 'model' option has been selected
+ by the user.
+ """
+ model_name = value.lower()
+ models = CrcModels()
+ model = models.get_params(model_name)
+ if model != None:
+ setattr(parser.values, 'width', model['width'])
+ setattr(parser.values, 'poly', model['poly'])
+ setattr(parser.values, 'reflect_in', model['reflect_in'])
+ setattr(parser.values, 'xor_in', model['xor_in'])
+ setattr(parser.values, 'reflect_out', model['reflect_out'])
+ setattr(parser.values, 'xor_out', model['xor_out'])
+ else:
+ models = CrcModels()
+ model_list = ", ".join(models.names())
+ raise OptionValueError(
+ "unsupported model {0:s}. Supported models are: {1:s}."
+ .format(value, model_list))
+
+
+def _check_hex(dummy_option, opt, value):
+ """
+ Checks if a value is given in a decimal integer of hexadecimal reppresentation.
+ Returns the converted value or rises an exception on error.
+ """
+ try:
+ if value.lower().startswith("0x"):
+ return int(value, 16)
+ else:
+ return int(value)
+ except ValueError:
+ raise OptionValueError(
+ "option {0:s}: invalid integer or hexadecimal value: {1:s}.".format(opt, value))
+
+
+def _check_bool(dummy_option, opt, value):
+ """
+ Checks if a value is given as a boolean value (either 0 or 1 or "true" or "false")
+ Returns the converted value or rises an exception on error.
+ """
+ if value.isdigit():
+ return int(value, 10) != 0
+ elif value.lower() == "false":
+ return False
+ elif value.lower() == "true":
+ return True
+ else:
+ raise OptionValueError("option {0:s}: invalid boolean value: {1:s}.".format(opt, value))
+
+
+class MyOption(Option):
+ """
+ New option parsing class extends the Option class
+ """
+ TYPES = Option.TYPES + ("hex", "bool")
+ TYPE_CHECKER = copy(Option.TYPE_CHECKER)
+ TYPE_CHECKER["hex"] = _check_hex
+ TYPE_CHECKER["bool"] = _check_bool
+
diff --git a/third_party/pycrc/pycrc/symtable.py b/third_party/pycrc/pycrc/symtable.py
new file mode 100644
index 0000000..e46ab9c
--- /dev/null
+++ b/third_party/pycrc/pycrc/symtable.py
@@ -0,0 +1,352 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+
+"""
+Symbol table for the macro processor used by pycrc.
+use as follows:
+
+ import pycrc.opt as opt
+ import pycrc.symtable as sym
+
+ opt = opt.Options()
+ sym = sym.SymbolTable(opt)
+
+ print(sym['crc_width'])
+ print('width: {crc_width}, poly: {crc_poly}'.format(**sym))
+"""
+
+from pycrc.algorithms import Crc
+import collections
+import time
+import os
+
+
+class SymbolTable(object):
+ def __init__(self, opt):
+ self.opt = opt
+
+
+class SymbolTable(collections.MutableMapping):
+ """A dictionary that applies an arbitrary key-altering
+ function before accessing the keys"""
+
+ def __init__(self, opt):
+ self.opt = opt
+ self.tbl_shift = _tbl_shift(opt)
+ self.cache = dict()
+ self.generator = dict({
+ 'datetime': lambda: time.asctime(),
+ 'program_version': lambda: self.opt.version_str,
+ 'program_url': lambda: self.opt.web_address,
+ 'filename': lambda: 'pycrc_stdout' if self.opt.output_file is None else os.path.basename(self.opt.output_file),
+ 'header_filename': lambda: _pretty_header_filename(self.opt.output_file),
+ 'header_protection': lambda: _pretty_hdrprotection(self.opt),
+
+ 'crc_algorithm': lambda: _pretty_algorithm(self.opt),
+ 'crc_width': lambda: _pretty_str(self.opt.width),
+ 'crc_poly': lambda: _pretty_hex(self.opt.poly, self.opt.width),
+ 'crc_reflect_in': lambda: _pretty_bool(self.opt.reflect_in),
+ 'crc_xor_in': lambda: _pretty_hex(self.opt.xor_in, self.opt.width),
+ 'crc_reflect_out': lambda: _pretty_bool(self.opt.reflect_out),
+ 'crc_xor_out': lambda: _pretty_hex(self.opt.xor_out, self.opt.width),
+ 'crc_slice_by': lambda: _pretty_str(self.opt.slice_by),
+ 'crc_table_idx_width': lambda: str(self.opt.tbl_idx_width),
+ 'crc_table_width': lambda: _pretty_str(1 << self.opt.tbl_idx_width),
+ 'crc_table_mask': lambda: _pretty_hex(self.opt.tbl_width - 1, 8),
+ 'crc_mask': lambda: _pretty_hex(self.opt.mask, self.opt.width),
+ 'crc_msb_mask': lambda: _pretty_hex(self.opt.msb_mask, self.opt.width),
+ 'crc_shift': lambda: _pretty_str(self.tbl_shift),
+
+ 'cfg_width': lambda: self.__getitem__('crc_width') if self.opt.width is not None else 'cfg->width',
+ 'cfg_poly': lambda: self.__getitem__('crc_poly') if self.opt.poly is not None else 'cfg->poly',
+ 'cfg_reflect_in': lambda: self.__getitem__('crc_reflect_in') if self.opt.reflect_in is not None else 'cfg->reflect_in',
+ 'cfg_xor_in': lambda: self.__getitem__('crc_xor_in') if self.opt.xor_in is not None else 'cfg->xor_in',
+ 'cfg_reflect_out': lambda: self.__getitem__('crc_reflect_out') if self.opt.reflect_out is not None else 'cfg->reflect_out',
+ 'cfg_xor_out': lambda: self.__getitem__('crc_xor_out') if self.opt.xor_out is not None else 'cfg->xor_out',
+ 'cfg_table_idx_width': lambda: self.__getitem__('crc_table_idx_width') if self.opt.tbl_idx_width is not None else 'cfg->table_idx_width',
+ 'cfg_table_width': lambda: self.__getitem__('crc_table_width') if self.opt.tbl_width is not None else 'cfg->table_width',
+ 'cfg_mask': lambda: self.__getitem__('crc_mask') if self.opt.mask is not None else 'cfg->crc_mask',
+ 'cfg_msb_mask': lambda: self.__getitem__('crc_msb_mask') if self.opt.msb_mask is not None else 'cfg->msb_mask',
+ 'cfg_shift': lambda: self.__getitem__('crc_shift') if self.tbl_shift is not None else 'cfg->crc_shift',
+ 'cfg_poly_shifted': lambda: '('+self.__getitem__('cfg_poly')+' << '+self.__getitem__('cfg_shift')+')' if self.tbl_shift is None or self.tbl_shift > 0 else self.__getitem__('cfg_poly'),
+ 'cfg_mask_shifted': lambda: '('+self.__getitem__('cfg_mask')+' << '+self.__getitem__('cfg_shift')+')' if self.tbl_shift is None or self.tbl_shift > 0 else self.__getitem__('cfg_mask'),
+ 'cfg_msb_mask_shifted': lambda: '('+self.__getitem__('cfg_msb_mask')+' << '+self.__getitem__('cfg_shift')+')' if self.tbl_shift is None or self.tbl_shift > 0 else self.__getitem__('cfg_msb_mask'),
+
+ 'c_bool': lambda: 'int' if self.opt.c_std == 'C89' else 'bool',
+ 'c_true': lambda: '1' if self.opt.c_std == 'C89' else 'true',
+ 'c_false': lambda: '0' if self.opt.c_std == 'C89' else 'false',
+
+ 'underlying_crc_t': lambda: _get_underlying_crc_t(self.opt),
+ 'crc_t': lambda: self.opt.symbol_prefix + 't',
+ 'cfg_t': lambda: self.opt.symbol_prefix + 'cfg_t',
+ 'crc_reflect_function': lambda: self.opt.symbol_prefix + 'reflect',
+ 'crc_table_gen_function': lambda: self.opt.symbol_prefix + 'table_gen',
+ 'crc_init_function': lambda: self.opt.symbol_prefix + 'init',
+ 'crc_update_function': lambda: self.opt.symbol_prefix + 'update',
+ 'crc_finalize_function': lambda: self.opt.symbol_prefix + 'finalize',
+
+ 'crc_init_value': lambda: _get_init_value(self.opt),
+ 'crc_table_init': lambda: _get_table_init(self.opt),
+ })
+
+ def __getitem__(self, key):
+ """
+ Return the value for the requested key
+ """
+ if key in self.cache:
+ return self.cache[key]
+ if key not in self.generator:
+ raise KeyError(key)
+ generator = self.generator[key]
+ val = self.generator[key]()
+ self.cache[key] = val
+ return val
+
+ def __setitem__(self, key, value):
+ self.generator[key] = value
+
+ def __delitem__(self, key):
+ del self.generator[key]
+
+ def __iter__(self):
+ return iter(self.generator)
+
+ def __len__(self):
+ return len(self.generator)
+
+
+
+def _pretty_str(value):
+ """
+ Return a value of width bits as a pretty string.
+ """
+ if value is None:
+ return 'Undefined'
+ return str(value)
+
+
+def _pretty_hex(value, width=None):
+ """
+ Return a value of width bits as a pretty hexadecimal formatted string.
+ """
+ if value is None:
+ return 'Undefined'
+ if width is None:
+ return '{0:#x}'.format(value)
+ width = (width + 3) // 4
+ hex_str = "{{0:#0{0:d}x}}".format(width + 2)
+ return hex_str.format(value)
+
+
+def _pretty_bool(value):
+ """
+ Return a boolen value of width bits as a pretty formatted string.
+ """
+ if value is None:
+ return 'Undefined'
+ return 'True' if value else 'False'
+
+
+def _pretty_algorithm(opt):
+ """
+ Return the algorithm name.
+ """
+ if opt.algorithm == opt.algo_bit_by_bit:
+ return 'bit-by-bit'
+ elif opt.algorithm == opt.algo_bit_by_bit_fast:
+ return 'bit-by-bit-fast'
+ elif opt.algorithm == opt.algo_table_driven:
+ return 'table-driven'
+ else:
+ return 'UNDEFINED'
+
+def _pretty_header_filename(filename):
+ """
+ Return the sanitized filename of a header file.
+ """
+ if filename is None:
+ return 'pycrc_stdout.h'
+ filename = os.path.basename(filename)
+ if filename[-2:] == '.c':
+ return filename[0:-1] + 'h'
+ else:
+ return filename + '.h'
+
+
+def _pretty_hdrprotection(opt):
+ """
+ Return the name of a C header protection (e.g. CRC_IMPLEMENTATION_H).
+ """
+ if opt.output_file is None:
+ filename = 'pycrc_stdout'
+ else:
+ filename = os.path.basename(opt.output_file)
+ out_str = ''.join([s.upper() if s.isalnum() else '_' for s in filename])
+ return out_str
+
+
+def _get_underlying_crc_t(opt):
+ """
+ Return the C type of the crc_t typedef.
+ """
+ # pylint: disable=too-many-return-statements, too-many-branches
+
+ if opt.crc_type is not None:
+ return opt.crc_type
+ if opt.c_std == 'C89':
+ if opt.width is None:
+ return 'unsigned long int'
+ if opt.width <= 8:
+ return 'unsigned char'
+ elif opt.width <= 16:
+ return 'unsigned int'
+ else:
+ return 'unsigned long int'
+ else: # C99
+ if opt.width is None:
+ return 'unsigned long long int'
+ if opt.width <= 8:
+ return 'uint_fast8_t'
+ elif opt.width <= 16:
+ return 'uint_fast16_t'
+ elif opt.width <= 32:
+ return 'uint_fast32_t'
+ elif opt.width <= 64:
+ return 'uint_fast64_t'
+ elif opt.width <= 128:
+ return 'uint_fast128_t'
+ else:
+ return 'uintmax_t'
+
+
+def _get_init_value(opt):
+ """
+ Return the init value of a C implementation, according to the selected
+ algorithm and to the given options.
+ If no default option is given for a given parameter, value in the cfg_t
+ structure must be used.
+ """
+ if opt.algorithm == opt.algo_bit_by_bit:
+ if opt.xor_in is None or opt.width is None or opt.poly is None:
+ return None
+ crc = Crc(
+ width=opt.width, poly=opt.poly,
+ reflect_in=opt.reflect_in, xor_in=opt.xor_in,
+ reflect_out=opt.reflect_out, xor_out=opt.xor_out,
+ table_idx_width=opt.tbl_idx_width)
+ init = crc.nondirect_init
+ elif opt.algorithm == opt.algo_bit_by_bit_fast:
+ if opt.xor_in is None:
+ return None
+ init = opt.xor_in
+ elif opt.algorithm == opt.algo_table_driven:
+ if opt.reflect_in is None or opt.xor_in is None or opt.width is None:
+ return None
+ if opt.poly is None:
+ poly = 0
+ else:
+ poly = opt.poly
+ crc = Crc(
+ width=opt.width, poly=poly,
+ reflect_in=opt.reflect_in, xor_in=opt.xor_in,
+ reflect_out=opt.reflect_out, xor_out=opt.xor_out,
+ table_idx_width=opt.tbl_idx_width)
+ if opt.reflect_in:
+ init = crc.reflect(crc.direct_init, opt.width)
+ else:
+ init = crc.direct_init
+ else:
+ init = 0
+ return _pretty_hex(init, opt.width)
+
+
+def _get_simple_table(opt, crc_tbl, values_per_line, format_width, indent):
+ """
+ Get one CRC table, formatted as string with appropriate indenting and
+ line breaks.
+ """
+ out = ""
+ for i in range(opt.tbl_width):
+ if i % values_per_line == 0:
+ out += " " * indent
+ tbl_val = _pretty_hex(crc_tbl[i], format_width)
+ if i == (opt.tbl_width - 1):
+ out += "{0:s}".format(tbl_val)
+ elif i % values_per_line == (values_per_line - 1):
+ out += "{0:s},\n".format(tbl_val)
+ else:
+ out += "{0:s}, ".format(tbl_val)
+ return out
+
+
+def _get_table_init(opt): # TODO: change to return a list
+ """
+ Return the precalculated CRC table for the table_driven implementation.
+ """
+ if opt.algorithm != opt.algo_table_driven:
+ return "0"
+ if opt.width is None or opt.poly is None or opt.reflect_in is None:
+ return "0"
+ crc = Crc(
+ width=opt.width, poly=opt.poly,
+ reflect_in=opt.reflect_in,
+ xor_in=0, reflect_out=False, xor_out=0, # set unimportant variables to known values
+ table_idx_width=opt.tbl_idx_width,
+ slice_by=opt.slice_by)
+ crc_tbl = crc.gen_table()
+ if opt.width > 32:
+ values_per_line = 4
+ elif opt.width >= 16:
+ values_per_line = 8
+ else:
+ values_per_line = 16
+ format_width = max(opt.width, 8)
+ if opt.slice_by == 1:
+ indent = 4
+ else:
+ indent = 8
+
+ out = [''] * opt.slice_by
+ for i in range(opt.slice_by):
+ out[i] = _get_simple_table(opt, crc_tbl[i], values_per_line, format_width, indent)
+ fixed_indent = ' ' * (indent - 4)
+ out = '{0:s}{{\n'.format(fixed_indent) + \
+ '\n{0:s}}},\n{0:s}{{\n'.format(fixed_indent).join(out) + \
+ '\n{0:s}}}'.format(fixed_indent)
+ if opt.slice_by == 1:
+ return out
+ return '{\n' + out + '\n}'
+
+
+def _tbl_shift(opt):
+ """
+ Return the table shift value
+ """
+ if opt.algorithm == opt.algo_table_driven and (opt.width is None or opt.width < 8):
+ if opt.width is None:
+ return None
+ else:
+ return 8 - opt.width
+ else:
+ return 0
+
diff --git a/third_party/pycrc/setup.py b/third_party/pycrc/setup.py
new file mode 100644
index 0000000..7d270ae
--- /dev/null
+++ b/third_party/pycrc/setup.py
@@ -0,0 +1,12 @@
+from distutils.core import setup
+
+from pycrc import progname, version, url
+
+setup(name = 'pycrc',
+ version = version,
+ description = 'Free, easy to use Cyclic Redundancy Check source code generator for C/C++',
+ author = 'Thomas Pircher',
+ author_email = 'tehpeh-web@tty1.net',
+ url = url,
+ packages = ['pycrc'],
+ )
diff --git a/third_party/pycrc/test/check_files.sh b/third_party/pycrc/test/check_files.sh
new file mode 100755
index 0000000..4cc874f
--- /dev/null
+++ b/third_party/pycrc/test/check_files.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+set -e
+
+PYCRC=`dirname $0`/../pycrc.py
+outdir_old="/tmp/pycrc_out"
+outdir_new="/tmp/pycrc_new"
+tarfile="pycrc_files.tar.gz"
+
+usage() {
+ echo >&2 "usage: $0 [OPTIONS]"
+ echo >&2 ""
+ echo >&2 "with OPTIONS in"
+ echo >&2 " -c check the generated output"
+ echo >&2 " -g generate the database"
+ echo >&2 " -n no cleanup: don't delete the directories with the generated code"
+ echo >&2 " -h this help message"
+}
+
+
+opt_check=off
+opt_no_cleanup=off
+opt_generate=off
+
+while getopts cgnh opt; do
+ case "$opt" in
+ c) opt_check=on;;
+ g) opt_generate=on;;
+ n) opt_no_cleanup=on;;
+ h) usage
+ exit 0
+ ;;
+ \?) usage # unknown flag
+ exit 1
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+if [ -e "$outdir_old" ]; then
+ echo >&2 "Output directory $outdir_old exists!"
+ exit 1
+fi
+if [ -e "$outdir_new" ]; then
+ echo >&2 "Output directory $outdir_new exists!"
+ exit 1
+fi
+
+
+cleanup() {
+ if [ "$opt_no_cleanup" = "on" ]; then
+ echo "No cleanup. Please delete $outdir_old and $outdir_new when you're done"
+ else
+ rm -rf "$outdir_old" "$outdir_new"
+ fi
+}
+
+trap cleanup 0 1 2 3 15
+
+
+generate() {
+ outfile="$1"
+ shift
+ $PYCRC "$@" -o "${outfile}"
+ sed -i -e 's/Generated on ... ... .. ..:..:.. ..../Generated on XXX XXX XX XX:XX:XX XXXX/; s/by pycrc v[0-9.]*/by pycrc vXXX/;' "${outfile}"
+}
+
+populate() {
+ outdir=$1
+ mkdir -p "$outdir"
+ models=`PYTHONPATH=.. python -c 'import pycrc.models as m; print(" ".join(m.CrcModels().names()))'`
+ for model in "undefined" $models; do
+ for algo in "bbb" "bbf" "tbl"; do
+ for cstd in c98 c99; do
+ if [ "$model" = "undefined" ]; then
+ mod_opt=
+ else
+ mod_opt="--model=${model}"
+ fi
+ generate "${outdir}/${model}_${algo}_${cstd}.h" --generate=h --algorithm=${algo} $mod_opt
+ generate "${outdir}/${model}_${algo}_${cstd}.c" --generate=c --algorithm=${algo} $mod_opt
+ done
+ done
+ done
+
+ algo=tbl
+ for model in crc-32; do
+ for slice in 4 8 16; do
+ for cstd in c98 c99; do
+ generate "${outdir}/${model}_${algo}_sb${slice}_${cstd}.h" --generate=h --algorithm=${algo} --model=${model} --slice-by ${slice}
+ generate "${outdir}/${model}_${algo}_sb${slice}_${cstd}.c" --generate=c --algorithm=${algo} --model=${model} --slice-by ${slice}
+ done
+ done
+ done
+}
+
+do_check() {
+ tar xzf "$tarfile" -C "`dirname $outdir_new`"
+ populate "$outdir_new"
+ diff -ru "$outdir_old" "$outdir_new"
+}
+
+
+if [ "$opt_check" = "on" ]; then
+ if [ ! -f "$tarfile" ]; then
+ echo >&2 "Can't find tarfile $tarfile"
+ exit 1
+ fi
+ do_check
+fi
+
+if [ "$opt_generate" = "on" ]; then
+ populate "$outdir_old"
+ dirname="`dirname $outdir_old`"
+ basename="`basename $outdir_old`"
+ tar czf "$tarfile" -C "$dirname" "$basename"
+fi
diff --git a/third_party/pycrc/test/main.c b/third_party/pycrc/test/main.c
new file mode 100644
index 0000000..252d4fd
--- /dev/null
+++ b/third_party/pycrc/test/main.c
@@ -0,0 +1,283 @@
+// Copyright (c) 2006-2013 Thomas Pircher <tehpeh-web@tty1.net>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include "crc.h"
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <string.h>
+
+static bool atob(const char *str);
+static crc_t xtoi(const char *str);
+static int get_config(int argc, char *argv[], crc_cfg_t *cfg);
+#if CRC_ALGO_BIT_BY_BIT
+static crc_t crc_verify(const crc_cfg_t *cfg, crc_t crc_pre_final, crc_t crc);
+static crc_t crc_reflect(crc_t data, size_t data_len);
+#endif
+
+
+
+static bool verbose = false;
+static unsigned char str[256] = "123456789";
+
+bool atob(const char *str)
+{
+ if (!str) {
+ return 0;
+ }
+ if (isdigit(str[0])) {
+ return (bool)atoi(str);
+ }
+ if (tolower(str[0]) == 't') {
+ return true;
+ }
+ return false;
+}
+
+crc_t xtoi(const char *str)
+{
+ crc_t ret = 0;
+
+ if (!str) {
+ return 0;
+ }
+ if (str[0] == '0' && tolower(str[1]) == 'x') {
+ str += 2;
+ while (*str) {
+ if (isdigit(*str))
+ ret = 16 * ret + *str - '0';
+ else if (isxdigit(*str))
+ ret = 16 * ret + tolower(*str) - 'a' + 10;
+ else
+ return ret;
+ str++;
+ }
+ } else if (isdigit(*str)) {
+ while (*str) {
+ if (isdigit(*str))
+ ret = 10 * ret + *str - '0';
+ else
+ return ret;
+ str++;
+ }
+ }
+ return ret;
+}
+
+
+int get_config(int argc, char *argv[], crc_cfg_t *cfg)
+{
+ int c;
+ int option_index;
+ static struct option long_options[] = {
+ {"width", 1, 0, 'w'},
+ {"poly", 1, 0, 'p'},
+ {"reflect-in", 1, 0, 'n'},
+ {"xor-in", 1, 0, 'i'},
+ {"reflect-out", 1, 0, 'u'},
+ {"xor-out", 1, 0, 'o'},
+ {"verbose", 0, 0, 'v'},
+ {"check-string", 1, 0, 's'},
+ {"table-idx-with", 1, 0, 't'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ option_index = 0;
+
+ c = getopt_long(argc, argv, "w:p:ni:uo:v", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ printf("option %s", long_options[option_index].name);
+ if (optarg)
+ printf(" with arg %s", optarg);
+ printf ("\n");
+ case 'w':
+ cfg->width = atoi(optarg);
+ break;
+ case 'p':
+ cfg->poly = xtoi(optarg);
+ break;
+ case 'n':
+ cfg->reflect_in = atob(optarg);
+ break;
+ case 'i':
+ cfg->xor_in = xtoi(optarg);
+ break;
+ case 'u':
+ cfg->reflect_out = atob(optarg);
+ break;
+ case 'o':
+ cfg->xor_out = xtoi(optarg);
+ break;
+ case 's':
+ memcpy(str, optarg, strlen(optarg) < sizeof(str) ? strlen(optarg) + 1 : sizeof(str));
+ str[sizeof(str) - 1] = '\0';
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 't':
+ // ignore --table_idx_width option
+ break;
+ case '?':
+ return -1;
+ case ':':
+ fprintf(stderr, "missing argument to option %c\n", c);
+ return -1;
+ default:
+ fprintf(stderr, "unhandled option %c\n", c);
+ return -1;
+ }
+ }
+ cfg->msb_mask = (crc_t)1u << (cfg->width - 1);
+ cfg->crc_mask = (cfg->msb_mask - 1) | cfg->msb_mask;
+ cfg->crc_shift = cfg->width < 8 ? 8 - cfg->width : 0;
+
+ cfg->poly &= cfg->crc_mask;
+ cfg->xor_in &= cfg->crc_mask;
+ cfg->xor_out &= cfg->crc_mask;
+ return 0;
+}
+
+
+#if CRC_ALGO_BIT_BY_BIT
+crc_t crc_verify(const crc_cfg_t *cfg, crc_t crc_pre_final, crc_t crc)
+{
+ crc_t result;
+ unsigned char data;
+ unsigned int i;
+
+ // don't verify if the width is not a multiple of 8
+ if (cfg->width % 8) {
+ return 0;
+ }
+ if (cfg->xor_out) {
+ crc ^= cfg->xor_out;
+ }
+ if (cfg->reflect_out) {
+ crc = crc_reflect(crc, cfg->width);
+ }
+ result = crc_pre_final;
+ for (i = 0; i < cfg->width / 8; i++) {
+ data = (crc >> (cfg->width - 8 * i - 8)) & 0xff;
+ if (cfg->reflect_in) {
+ data = crc_reflect(data, 8);
+ }
+ result = crc_update(cfg, result, &data, 1);
+ }
+ // no crc_finalize, because if the CRC calculation is correct, result == 0.
+ // A crc_finalize would XOR-in again some ones into the solution.
+ // In theory the finalize function of the bit-by-bit algorithm
+ // would also loop over cfg->width zero bits, but since
+ // a) result == 0, and
+ // b) input data == 0
+ // the output would always be zero
+ return result;
+}
+
+crc_t crc_reflect(crc_t data, size_t data_len)
+{
+ unsigned int i;
+ crc_t ret;
+
+ ret = 0;
+ for (i = 0; i < data_len; i++)
+ {
+ if (data & 0x01) {
+ ret = (ret << 1) | 1;
+ } else {
+ ret = ret << 1;
+ }
+ data >>= 1;
+ }
+ return ret;
+}
+#endif // CRC_ALGO_BIT_BY_BIT
+
+
+int main(int argc, char *argv[])
+{
+ crc_cfg_t cfg = {
+ 0, // width
+ 0, // poly
+ 0, // xor_in
+ 0, // reflect_in
+ 0, // xor_out
+ 0, // reflect_out
+
+ 0, // crc_mask
+ 0, // msb_mask
+ 0, // crc_shift
+ };
+ crc_t crc;
+ crc_t crc_test, crc_pre_final;
+ char format[20];
+ int ret, i;
+
+ ret = get_config(argc, argv, &cfg);
+ if (ret == 0) {
+# if CRC_ALGO_TABLE_DRIVEN
+ crc_table_gen(&cfg);
+# endif // CRC_ALGO_TABLE_DRIVEN
+ crc = crc_init(&cfg);
+ crc = crc_pre_final = crc_update(&cfg, crc, str, strlen((char *)str));
+ crc = crc_finalize(&cfg, crc);
+
+# if CRC_ALGO_BIT_BY_BIT
+ if (crc_verify(&cfg, crc_pre_final, crc) != 0) {
+ fprintf(stderr, "error: crc verification failed\n");
+ return 1;
+ }
+# endif
+
+ // calculate the checksum again, but this time loop over the input
+ // bytes one-by-one.
+ crc_test = crc_init(&cfg);
+ for (i = 0; str[i]; i++)
+ {
+ crc_test = crc_update(&cfg, crc_test, str + i, 1);
+ }
+ crc_test = crc_finalize(&cfg, crc_test);
+ if (crc_test != crc) {
+ fprintf(stderr, "error: crc loop verification failed\n");
+ return 1;
+ }
+
+ if (verbose) {
+ snprintf(format, sizeof(format), "%%-16s = 0x%%0%dx\n", (unsigned int)(cfg.width + 3) / 4);
+ printf("%-16s = %d\n", "width", (unsigned int)cfg.width);
+ printf(format, "poly", (unsigned int)cfg.poly);
+ printf("%-16s = %s\n", "reflect_in", cfg.reflect_in ? "true": "false");
+ printf(format, "xor_in", cfg.xor_in);
+ printf("%-16s = %s\n", "reflect_out", cfg.reflect_out ? "true": "false");
+ printf(format, "xor_out", (unsigned int)cfg.xor_out);
+ printf(format, "crc_mask", (unsigned int)cfg.crc_mask);
+ printf(format, "msb_mask", (unsigned int)cfg.msb_mask);
+ }
+ printf("0x%llx\n", (unsigned long long int)crc);
+ }
+ return !ret;
+}
diff --git a/third_party/pycrc/test/performance.sh b/third_party/pycrc/test/performance.sh
new file mode 100755
index 0000000..7b6b239
--- /dev/null
+++ b/third_party/pycrc/test/performance.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+set -e
+
+PYCRC=`dirname $0`/../pycrc.py
+
+cleanup() {
+ rm -f a.out performance.c crc_bbb.[ch] crc_bbf.[ch] crc_tb[l4].[ch] crc_sb4.[ch]
+}
+
+trap cleanup 0 1 2 3 15
+
+model=crc-32
+
+prefix=bbb
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o crc_$prefix.c --algo bit-by-bit
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o crc_$prefix.h --algo bit-by-bit
+prefix=bbf
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o crc_$prefix.h --algo bit-by-bit-fast
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o crc_$prefix.c --algo bit-by-bit-fast
+prefix=tbl
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o crc_$prefix.h --algo table-driven
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o crc_$prefix.c --algo table-driven
+prefix=tb4
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o crc_$prefix.h --algo table-driven --table-idx-width 4
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o crc_$prefix.c --algo table-driven --table-idx-width 4
+prefix=sb4
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o crc_$prefix.h --algo table-driven --slice-by 4
+$PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o crc_$prefix.c --algo table-driven --slice-by 4
+
+
+print_main() {
+cat <<EOF
+#include "crc_bbb.h"
+#include "crc_bbf.h"
+#include "crc_tbl.h"
+#include "crc_tb4.h"
+#include "crc_sb4.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/times.h>
+#include <unistd.h>
+
+#define NUM_RUNS (128*256*256)
+
+unsigned char buf[1024];
+
+void test_bbb(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec);
+void test_bbf(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec);
+void test_tbl(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec);
+void test_tb4(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec);
+void test_sb4(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec);
+
+/**
+ * Print results.
+ *
+ * \param dsc Description of the test
+ * \param buflen Length of one buffer
+ * \param num_runs Number of runs over that buffer
+ * \param t_user user time
+ * \param t_sys system time
+ * \return void
+ *****************************************************************************/
+void show_times(const char *dsc, size_t buflen, size_t num_runs, double t_user)
+{
+ double mbps = (((double)buflen) * num_runs)/(1024*1024*t_user);
+ printf("%s of %ld bytes (%ld * %ld)\n", dsc, (long)buflen*num_runs, (long)buflen, (long)num_runs);
+ printf("%13s: %7.3f s %13s: %7.3f MiB/s\n", "user time", t_user, "throughput", mbps);
+ printf("\n");
+}
+
+
+/**
+ * C main function.
+ *
+ * \retval 0 on success
+ * \retval 1 on failure
+ *****************************************************************************/
+int main(void)
+{
+ unsigned int i;
+ long int clock_per_sec;
+
+ for (i = 0; i < sizeof(buf); i++) {
+ buf[i] = (unsigned char)rand();
+ }
+ clock_per_sec = sysconf(_SC_CLK_TCK);
+
+ // bit-by-bit
+ test_bbb(buf, sizeof(buf), NUM_RUNS / 8, clock_per_sec);
+
+ // bit-by-bit-fast
+ test_bbf(buf, sizeof(buf), NUM_RUNS / 8, clock_per_sec);
+
+ // table-driven
+ test_tbl(buf, sizeof(buf), NUM_RUNS, clock_per_sec);
+
+ // table-driven idx4
+ test_tb4(buf, sizeof(buf), NUM_RUNS / 2, clock_per_sec);
+
+ // table-driven slice-by 4
+ test_sb4(buf, sizeof(buf), NUM_RUNS, clock_per_sec);
+
+ return 0;
+}
+EOF
+}
+
+print_routine() {
+ algo=$1
+ prefix=$2
+ cat <<EOF
+/**
+ * Test $algo Algorithm.
+ *
+ * \return void
+ *****************************************************************************/
+void test_${prefix}(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec)
+{
+ crc_${prefix}_t crc;
+ unsigned int i;
+ struct tms tm1, tm2;
+
+ times(&tm1);
+ crc = crc_${prefix}_init();
+ for (i = 0; i < num_runs; i++) {
+ crc = crc_${prefix}_update(crc, buf, buf_len);
+ }
+ crc = crc_${prefix}_finalize(crc);
+ times(&tm2);
+ show_times("$model, $algo, block-wise", buf_len, num_runs,
+ ((double)(tm2.tms_utime - tm1.tms_utime) / (double)clock_per_sec));
+}
+
+EOF
+}
+
+print_main > performance.c
+print_routine "bit-by-bit" bbb >> performance.c
+print_routine "bit-by-bit-fast" bbf >> performance.c
+print_routine "table-driven" tbl >> performance.c
+print_routine "table-driven idx4" tb4 >> performance.c
+print_routine "table-driven sb4" sb4 >> performance.c
+
+gcc -W -Wall -O3 crc_bbb.c crc_bbf.c crc_tbl.c crc_tb4.c crc_sb4.c performance.c
+./a.out
diff --git a/third_party/pycrc/test/test.py b/third_party/pycrc/test/test.py
new file mode 100644
index 0000000..1731416
--- /dev/null
+++ b/third_party/pycrc/test/test.py
@@ -0,0 +1,679 @@
+#!/usr/bin/env python
+# -*- coding: Latin-1 -*-
+
+# pycrc test application.
+
+from optparse import OptionParser, Option, OptionValueError
+from copy import copy
+import os, sys
+import tempfile
+sys.path.append('..')
+sys.path.append('.')
+from pycrc.models import CrcModels
+from pycrc.algorithms import Crc
+
+
+class Options(object):
+ """
+ The options parsing and validating class
+ """
+
+ def __init__(self):
+ self.AllAlgorithms = set(['bit-by-bit', 'bbb', 'bit-by-bit-fast', 'bbf', 'table-driven', 'tbl'])
+ self.Compile = False
+ self.RandomParameters = False
+ self.CompileMixedArgs = False
+ self.VariableWidth = False
+ self.verbose = False
+ self.algorithm = copy(self.AllAlgorithms)
+
+ def parse(self, argv = None):
+ """
+ Parses and validates the options given as arguments
+ """
+ usage = """%prog [OPTIONS]"""
+
+ algorithms = ', '.join(sorted(list(self.AllAlgorithms)) + ['all'])
+ parser = OptionParser(usage=usage)
+ parser.add_option('-v', '--verbose',
+ action='store_true', dest='verbose', default=self.verbose,
+ help='print information about the model')
+ parser.add_option('-c', '--compile',
+ action='store_true', dest='compile', default=self.Compile,
+ help='test compiled version')
+ parser.add_option('-r', '--random-parameters',
+ action='store_true', dest='random_parameters', default=self.RandomParameters,
+ help='test random parameters')
+ parser.add_option('-m', '--compile-mixed-arguments',
+ action='store_true', dest='compile_mixed_args', default=self.CompileMixedArgs,
+ help='test compiled C program with some arguments given at compile time some arguments given at runtime')
+ parser.add_option('-w', '--variable-width',
+ action='store_true', dest='variable_width', default=self.VariableWidth,
+ help='test variable width from 1 to 64')
+ parser.add_option('-a', '--all',
+ action='store_true', dest='all', default=False,
+ help='do all tests')
+ parser.add_option('--algorithm',
+ action='store', type='string', dest='algorithm', default='all',
+ help='choose an algorithm from {{{0:s}}}'.format(algorithms, metavar='ALGO'))
+
+ (options, args) = parser.parse_args(argv)
+
+ self.verbose = options.verbose
+ self.Compile = options.all or options.compile or options.random_parameters
+ self.RandomParameters = options.all or options.random_parameters
+ self.CompileMixedArgs = options.all or options.compile_mixed_args
+ self.VariableWidth = options.all or options.variable_width
+
+ if options.algorithm is not None:
+ alg = options.algorithm.lower()
+ if alg in self.AllAlgorithms:
+ self.algorithm = set([alg])
+ elif alg == 'all':
+ self.algorithm = copy(self.AllAlgorithms)
+ else:
+ sys.stderr.write('unknown algorithm: {0:s}\n'.format(alg))
+ sys.exit(1)
+
+
+class CrcTests(object):
+ """
+ The CRC test class.
+ """
+
+ def __init__(self):
+ """
+ The class constructor.
+ """
+ self.pycrc_bin = '/bin/false'
+ self.use_algo_bit_by_bit = True
+ self.use_algo_bit_by_bit_fast = True
+ self.use_algo_table_driven = True
+ self.verbose = False
+ self.python3 = sys.version_info[0] >= 3
+ self.tmpdir = tempfile.mkdtemp(prefix='pycrc.')
+ self.check_file = None
+ self.crc_bin_bbb_c89 = None
+ self.crc_bin_bbb_c99 = None
+ self.crc_bin_bbf_c89 = None
+ self.crc_bin_bbf_c99 = None
+ self.crc_bin_bwe_c89 = None
+ self.crc_bin_bwe_c99 = None
+ self.crc_bin_tbl_c89 = None
+ self.crc_bin_tbl_c99 = None
+ self.crc_bin_tbl_sb4 = None
+ self.crc_bin_tbl_sb8 = None
+ self.crc_bin_tbl_sb16 = None
+ self.crc_bin_tbl_idx2 = None
+ self.crc_bin_tbl_idx4 = None
+
+ def __del__(self):
+ """
+ The class destructor. Delete all generated files.
+ """
+ if self.check_file is not None:
+ os.remove(self.check_file)
+ if self.crc_bin_bbb_c89 is not None:
+ self.__del_files([self.crc_bin_bbb_c89, self.crc_bin_bbb_c89+'.h', self.crc_bin_bbb_c89+'.c'])
+ if self.crc_bin_bbb_c99 is not None:
+ self.__del_files([self.crc_bin_bbb_c99, self.crc_bin_bbb_c99+'.h', self.crc_bin_bbb_c99+'.c'])
+ if self.crc_bin_bbf_c89 is not None:
+ self.__del_files([self.crc_bin_bbf_c89, self.crc_bin_bbf_c89+'.h', self.crc_bin_bbf_c89+'.c'])
+ if self.crc_bin_bbf_c99 is not None:
+ self.__del_files([self.crc_bin_bbf_c99, self.crc_bin_bbf_c99+'.h', self.crc_bin_bbf_c99+'.c'])
+ if self.crc_bin_bwe_c89 is not None:
+ self.__del_files([self.crc_bin_bwe_c89, self.crc_bin_bwe_c89+'.h', self.crc_bin_bwe_c89+'.c'])
+ if self.crc_bin_bwe_c99 is not None:
+ self.__del_files([self.crc_bin_bwe_c99, self.crc_bin_bwe_c99+'.h', self.crc_bin_bwe_c99+'.c'])
+ if self.crc_bin_tbl_c89 is not None:
+ self.__del_files([self.crc_bin_tbl_c89, self.crc_bin_tbl_c89+'.h', self.crc_bin_tbl_c89+'.c'])
+ if self.crc_bin_tbl_c99 is not None:
+ self.__del_files([self.crc_bin_tbl_c99, self.crc_bin_tbl_c99+'.h', self.crc_bin_tbl_c99+'.c'])
+ if self.crc_bin_tbl_sb4 is not None:
+ self.__del_files([self.crc_bin_tbl_sb4, self.crc_bin_tbl_sb4+'.h', self.crc_bin_tbl_sb4+'.c'])
+ if self.crc_bin_tbl_sb8 is not None:
+ self.__del_files([self.crc_bin_tbl_sb8, self.crc_bin_tbl_sb8+'.h', self.crc_bin_tbl_sb8+'.c'])
+ if self.crc_bin_tbl_sb16 is not None:
+ self.__del_files([self.crc_bin_tbl_sb16, self.crc_bin_tbl_sb16+'.h', self.crc_bin_tbl_sb16+'.c'])
+ if self.crc_bin_tbl_idx2 is not None:
+ self.__del_files([self.crc_bin_tbl_idx2, self.crc_bin_tbl_idx2+'.h', self.crc_bin_tbl_idx2+'.c'])
+ if self.crc_bin_tbl_idx4 is not None:
+ self.__del_files([self.crc_bin_tbl_idx4, self.crc_bin_tbl_idx4+'.h', self.crc_bin_tbl_idx4+'.c'])
+ os.removedirs(self.tmpdir)
+
+ def __del_files(delf, files):
+ """
+ Helper function to delete files.
+ """
+ for f in files:
+ try:
+ os.remove(f)
+ except:
+ print("error: can't delete {0:s}".format(f))
+ pass
+
+ def __get_status_output(self, cmd_str):
+ if self.python3:
+ import subprocess
+ return subprocess.getstatusoutput(cmd_str)
+ else:
+ import commands
+ return commands.getstatusoutput(cmd_str)
+
+ def __make_src(self, args, basename, cstd):
+ """
+ Generate the *.h and *.c source files for a test.
+ """
+ gen_src = '{0:s}/{1:s}'.format(self.tmpdir, basename)
+ cmd_str = self.pycrc_bin + ' {0:s} --std {1:s} --generate h -o {2:s}.h'.format(args, cstd, gen_src)
+ if self.verbose:
+ print(cmd_str)
+ ret = self.__get_status_output(cmd_str)
+ if ret[0] != 0:
+ print('error: the following command returned error: {0:s}'.format(cmd_str))
+ print(ret[1])
+ print(ret[2])
+ return None
+
+ cmd_str = self.pycrc_bin + ' {0:s} --std {1:s} --generate c-main -o {2:s}.c'.format(args, cstd, gen_src)
+ if self.verbose:
+ print(cmd_str)
+ ret = self.__get_status_output(cmd_str)
+ if ret[0] != 0:
+ print('error: the following command returned error: {0:s}'.format(cmd_str))
+ print(ret[1])
+ print(ret[2])
+ return None
+ return gen_src
+
+ def __compile(self, args, binfile, cstd):
+ """
+ Compile a generated source file.
+ """
+ cmd_str = 'gcc -W -Wall -pedantic -Werror -std={0:s} -o {1:s} {2:s}.c'.format(cstd, binfile, binfile)
+ if self.verbose:
+ print(cmd_str)
+ ret = self.__get_status_output(cmd_str)
+ if len(ret) > 1 and len(ret[1]) > 0:
+ print(ret[1])
+ if ret[0] != 0:
+ print('error: {0:d} with command error: {1:s}'.format(ret[0], cmd_str))
+ return None
+ return binfile
+
+ def __make_bin(self, args, basename, cstd='c99'):
+ """
+ Generate the source and compile to a binary.
+ """
+ filename = self.__make_src(args, basename, cstd)
+ if filename is None:
+ return None
+ if not self.__compile(args, filename, cstd):
+ self.__del_files([filename, filename+'.h', filename+'.c'])
+ return None
+ return filename
+
+ def __setup_files(self, opt):
+ """
+ Set up files needed during the test.
+ """
+ if self.verbose:
+ print('Setting up files...')
+ self.check_file = '{0:s}/check.txt'.format(self.tmpdir)
+ f = open(self.check_file, 'wb')
+ if self.python3:
+ f.write(bytes('123456789', 'utf-8'))
+ else:
+ f.write('123456789')
+ f.close()
+
+ if opt.Compile:
+ if self.use_algo_bit_by_bit:
+ filename = self.__make_bin('--algorithm bit-by-bit', 'crc_bbb_c89', 'c89')
+ if filename is None:
+ return False
+ self.crc_bin_bbb_c89 = filename
+
+ filename = self.__make_bin('--algorithm bit-by-bit', 'crc_bbb_c99', 'c99')
+ if filename is None:
+ return False
+ self.crc_bin_bbb_c99 = filename
+
+ if self.use_algo_bit_by_bit_fast:
+ filename = self.__make_bin('--algorithm bit-by-bit-fast', 'crc_bbf_c89', 'c89')
+ if filename is None:
+ return False
+ self.crc_bin_bbf_c89 = filename
+
+ filename = self.__make_bin('--algorithm bit-by-bit-fast', 'crc_bbf_c99', 'c99')
+ if filename is None:
+ return False
+ self.crc_bin_bbf_c99 = filename
+
+ if self.use_algo_table_driven:
+ filename = self.__make_bin('--algorithm table-driven', 'crc_tbl_c89', 'c89')
+ if filename is None:
+ return False
+ self.crc_bin_tbl_c89 = filename
+
+ filename = self.__make_bin('--algorithm table-driven', 'crc_tbl_c99', 'c99')
+ if filename is None:
+ return False
+ self.crc_bin_tbl_c99 = filename
+
+# FIXME don't test undefined params
+# filename = self.__make_bin('--algorithm table-driven --slice-by 4', 'crc_tbl_sb4')
+# if filename is None:
+# return False
+# self.crc_bin_tbl_sb4 = filename
+#
+# filename = self.__make_bin('--algorithm table-driven --slice-by 8', 'crc_tbl_sb8')
+# if filename is None:
+# return False
+# self.crc_bin_tbl_sb8 = filename
+#
+# filename = self.__make_bin('--algorithm table-driven --slice-by 16', 'crc_tbl_sb16')
+# if filename is None:
+# return False
+# self.crc_bin_tbl_sb16 = filename
+
+ filename = self.__make_bin('--algorithm table-driven --table-idx-width 2', 'crc_tbl_idx2')
+ if filename is None:
+ return False
+ self.crc_bin_tbl_idx2 = filename
+
+ filename = self.__make_bin('--algorithm table-driven --table-idx-width 4', 'crc_tbl_idx4')
+ if filename is None:
+ return False
+ self.crc_bin_tbl_idx4 = filename
+
+ return True
+
+
+ def __run_command(self, cmd_str):
+ """
+ Run a command and return its stdout.
+ """
+ if self.verbose:
+ print(cmd_str)
+ ret = self.__get_status_output(cmd_str)
+ if ret[0] != 0:
+ print('error: the following command returned error: {0:s}'.format(cmd_str))
+ print(ret[1])
+ return None
+ return ret[1]
+
+ def __check_command(self, cmd_str, expected_result):
+ """
+ Run a command and check if the stdout matches the expected result.
+ """
+ ret = self.__run_command(cmd_str)
+ if int(ret, 16) != expected_result:
+ print('error: different checksums!')
+ print('{0:s}: expected {1:#x}, got {2:s}'.format(cmd_str, expected_result, ret))
+ return False
+ return True
+
+ def __check_bin(self, args, expected_result, long_data_type = True):
+ """
+ Check all precompiled binaries.
+ """
+ for binary in [
+ self.crc_bin_bbb_c89, self.crc_bin_bbb_c99,
+ self.crc_bin_bbf_c89, self.crc_bin_bbf_c99,
+ self.crc_bin_tbl_c89, self.crc_bin_tbl_c99,
+ self.crc_bin_tbl_sb4, self.crc_bin_tbl_sb8, self.crc_bin_tbl_sb16,
+ self.crc_bin_tbl_idx2, self.crc_bin_tbl_idx4]:
+ if binary is not None:
+ # Don't test width > 32 for C89, as I don't know how to ask for an data type > 32 bits.
+ if binary[-3:] == 'c89' and long_data_type:
+ continue
+ cmd_str = binary + ' ' + args
+ if not self.__check_command(cmd_str, expected_result):
+ return False
+ return True
+
+ def __get_crc(self, model, check_str = '123456789', expected_crc = None):
+ """
+ Get the CRC for a set of parameters from the Python reference implementation.
+ """
+ if self.verbose:
+ out_str = 'Crc(width = {width:d}, poly = {poly:#x}, reflect_in = {reflect_in}, xor_in = {xor_in:#x}, reflect_out = {reflect_out}, xor_out = {xor_out:#x})'.format(**model)
+ if expected_crc is not None:
+ out_str += ' [check = {0:#x}]'.format(expected_crc)
+ print(out_str)
+ alg = Crc(width = model['width'], poly = model['poly'],
+ reflect_in = model['reflect_in'], xor_in = model['xor_in'],
+ reflect_out = model['reflect_out'], xor_out = model['xor_out'])
+ error = False
+ crc = expected_crc
+
+ if self.use_algo_bit_by_bit:
+ bbb_crc = alg.bit_by_bit(check_str)
+ if crc is None:
+ crc = bbb_crc
+ error = error or bbb_crc != crc
+ if self.use_algo_bit_by_bit_fast:
+ bbf_crc = alg.bit_by_bit_fast(check_str)
+ if crc is None:
+ crc = bbf_crc
+ error = error or bbf_crc != crc
+ if self.use_algo_table_driven:
+ tbl_crc = alg.table_driven(check_str)
+ if crc is None:
+ crc = tbl_crc
+ error = error or tbl_crc != crc
+
+ if error:
+ print('error: different checksums!')
+ if expected_crc is not None:
+ print(' check: {0:#x}'.format(expected_crc))
+ if self.use_algo_bit_by_bit:
+ print(' bit-by-bit: {0:#x}'.format(bbb_crc))
+ if self.use_algo_bit_by_bit_fast:
+ print(' bit-by-bit-fast: {0:#x}'.format(bbf_crc))
+ if self.use_algo_table_driven:
+ print(' table_driven: {0:#x}'.format(tbl_crc))
+ return None
+ return crc
+
+ def __compile_and_check_res(self, cmp_opt, run_opt, name, expected_crc):
+ """
+ Compile a model and run it.
+ """
+ filename = self.__make_bin(cmp_opt, name)
+ if filename is None:
+ return False
+ if run_opt is None:
+ cmd = filename
+ else:
+ cmd = filename + ' ' + run_opt
+ ret = self.__check_command(cmd, expected_crc)
+ self.__del_files([filename, filename+'.h', filename+'.c'])
+ if not ret:
+ return False
+ return True
+
+
+ def __test_models(self):
+ """
+ Standard Tests.
+ Test all known models.
+ """
+ if self.verbose:
+ print('Running __test_models()...')
+ check_str = '123456789'
+ check_bytes = bytearray(check_str, 'utf-8')
+ models = CrcModels()
+ for m in models.models:
+ expected_crc = m['check']
+ if self.__get_crc(m, check_str, expected_crc) != expected_crc:
+ return False
+
+ ext_args = '--width {width:d} --poly {poly:#x} --xor-in {xor_in:#x} --reflect-in {reflect_in} --xor-out {xor_out:#x} --reflect-out {reflect_out}'.format(**m)
+
+ cmd_str = '{0:s} --model {1:s}'.format(self.pycrc_bin, m['name'])
+ if not self.__check_command(cmd_str, expected_crc):
+ return False
+
+ cmd_str = '{0:s} {1:s}'.format(self.pycrc_bin, ext_args)
+ if not self.__check_command(cmd_str, expected_crc):
+ return False
+
+ cmd_str = '{0:s} {1:s} --check-hexstring {2:s}'.format(self.pycrc_bin, ext_args, ''.join(['{0:02x}'.format(c) for c in check_bytes]))
+ if not self.__check_command(cmd_str, expected_crc):
+ return False
+
+ cmd_str = '{0:s} --model {1:s} --check-file {2:s}'.format(self.pycrc_bin, m['name'], self.check_file)
+ if not self.__check_command(cmd_str, expected_crc):
+ return False
+
+ if not self.__check_bin(ext_args, expected_crc, m['width'] > 32):
+ return False
+
+ if self.verbose:
+ print("")
+ return True
+
+
+ def __test_compiled_models(self):
+ """
+ Standard Tests.
+ Test all known models with the compiled code
+ """
+ if self.verbose:
+ print('Running __test_compiled_models()...')
+ models = CrcModels()
+ for m in models.models:
+ expected_crc = m['check']
+ cmp_opt = '--model {name}'.format(**m)
+
+ if self.use_algo_bit_by_bit:
+ if not self.__compile_and_check_res('--algorithm bit-by-bit' + ' ' + cmp_opt, None, 'crc_bbb_mod', expected_crc):
+ return False
+
+ if self.use_algo_bit_by_bit_fast:
+ if not self.__compile_and_check_res('--algorithm bit-by-bit-fast' + ' ' + cmp_opt, None, 'crc_bbf_mod', expected_crc):
+ return False
+
+ if self.use_algo_table_driven:
+ if not self.__compile_and_check_res('--algorithm table-driven' + ' ' + cmp_opt, None, 'crc_tbl_mod', expected_crc):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm table-driven --slice-by=4' + ' ' + cmp_opt, None, 'crc_tsb4_mod', expected_crc):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm table-driven --slice-by=8' + ' ' + cmp_opt, None, 'crc_tsb8_mod', expected_crc):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm table-driven --slice-by=16' + ' ' + cmp_opt, None, 'crc_tsb16_mod', expected_crc):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm table-driven --table-idx-width=2' + ' ' + cmp_opt, None, 'crc_tix2_mod', expected_crc):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm table-driven --table-idx-width=4' + ' ' + cmp_opt, None, 'crc_tix4_mod', expected_crc):
+ return False
+ return True
+
+
+ def __test_compiled_special_cases(self):
+ """
+ Standard Tests.
+ Test some special cases.
+ """
+ if self.verbose:
+ print('Running __test_compiled_special_cases()...')
+ if self.use_algo_table_driven:
+ if not self.__compile_and_check_res('--model=crc-5 --reflect-in=0 --algorithm table-driven --table-idx-width=8', None, 'crc_tbl_special', 0x01):
+ return False
+ if not self.__compile_and_check_res('--model=crc-5 --reflect-in=0 --algorithm table-driven --table-idx-width=4', None, 'crc_tbl_special', 0x01):
+ return False
+ if not self.__compile_and_check_res('--model=crc-5 --reflect-in=0 --algorithm table-driven --table-idx-width=2', None, 'crc_tbl_special', 0x01):
+ return False
+ return True
+
+
+ def __test_variable_width(self):
+ """
+ Test variable width.
+ """
+ if self.verbose:
+ print('Running __test_variable_width()...')
+ models = CrcModels()
+ m = models.get_params('crc-64-jones')
+
+ for width in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 23, 24, 25, 31, 32, 33, 63, 64]:
+ mask = (1 << width) - 1
+ mw = {
+ 'width': width,
+ 'poly': m['poly'] & mask,
+ 'reflect_in': m['reflect_in'],
+ 'xor_in': m['xor_in'] & mask,
+ 'reflect_out': m['reflect_out'],
+ 'xor_out': m['xor_out'] & mask,
+ }
+ args = '--width {width:d} --poly {poly:#x} --xor-in {xor_in:#x} --reflect-in {reflect_in} --xor-out {xor_out:#x} --reflect-out {reflect_out}'.format(**mw)
+
+ check = self.__get_crc(mw)
+ if check is None:
+ return False
+
+ if self.use_algo_bit_by_bit:
+ if self.crc_bin_bbb_c99 is not None:
+ if not self.__check_command(self.crc_bin_bbb_c99 + ' ' + args, check):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm bit-by-bit' + ' ' + args, None, 'crc_bbb_arg', check):
+ return False
+
+ if self.use_algo_bit_by_bit_fast:
+ if self.crc_bin_bbf_c99 is not None:
+ if not self.__check_command(self.crc_bin_bbf_c99 + ' ' + args, check):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm bit-by-bit-fast' + ' ' + args, None, 'crc_bbf_arg', check):
+ return False
+
+ if self.use_algo_table_driven:
+ if self.crc_bin_tbl_c99 is not None:
+ if not self.__check_command(self.crc_bin_tbl_c99 + ' ' + args, check):
+ return False
+
+ if not self.__compile_and_check_res('--algorithm table-driven' + ' ' + args, None, 'crc_tbl_arg', check):
+ return False
+ return True
+
+
+ def __test_compiled_mixed_args(self):
+ """
+ Test compiled arguments.
+ """
+ if self.verbose:
+ print('Running __test_compiled_mixed_args()...')
+ m = {
+ 'name': 'zmodem',
+ 'width': ['', '--width 16'],
+ 'poly': ['', '--poly 0x1021'],
+ 'reflect_in': ['', '--reflect-in False'],
+ 'xor_in': ['', '--xor-in 0x0'],
+ 'reflect_out': ['', '--reflect-out False'],
+ 'xor_out': ['', '--xor-out 0x0'],
+ 'check': 0x31c3,
+ }
+ cmp_args = {}
+ run_args = {}
+ for b_width in range(2):
+ cmp_args['width'] = m['width'][b_width]
+ run_args['width'] = m['width'][1 - b_width]
+ for b_poly in range(2):
+ cmp_args['poly'] = m['poly'][b_poly]
+ run_args['poly'] = m['poly'][1 - b_poly]
+ for b_ref_in in range(2):
+ cmp_args['reflect_in'] = m['reflect_in'][b_ref_in]
+ run_args['reflect_in'] = m['reflect_in'][1 - b_ref_in]
+ for b_xor_in in range(2):
+ cmp_args['xor_in'] = m['xor_in'][b_xor_in]
+ run_args['xor_in'] = m['xor_in'][1 - b_xor_in]
+ for b_ref_out in range(2):
+ cmp_args['reflect_out'] = m['reflect_out'][b_ref_out]
+ run_args['reflect_out'] = m['reflect_out'][1 - b_ref_out]
+ for b_xor_out in range(2):
+ cmp_args['xor_out'] = m['xor_out'][b_xor_out]
+ run_args['xor_out'] = m['xor_out'][1 - b_xor_out]
+
+ cmp_opt = '{width:s} {poly:s} {reflect_in} {xor_in:s} {reflect_out} {xor_out:s}'.format(**cmp_args)
+ run_opt = '{width:s} {poly:s} {reflect_in} {xor_in:s} {reflect_out} {xor_out:s}'.format(**run_args)
+
+ if self.use_algo_bit_by_bit:
+ if not self.__compile_and_check_res('--algorithm bit-by-bit' + ' ' + cmp_opt, run_opt, 'crc_bbb_arg', m['check']):
+ return False
+
+ if self.use_algo_bit_by_bit_fast:
+ if not self.__compile_and_check_res('--algorithm bit-by-bit-fast' + ' ' + cmp_opt, run_opt, 'crc_bbf_arg', m['check']):
+ return False
+
+ if self.use_algo_table_driven:
+ if not self.__compile_and_check_res('--algorithm table-driven' + ' ' + cmp_opt, run_opt, 'crc_tbl_arg', m['check']):
+ return False
+ return True
+
+
+ def __test_random_params(self):
+ """
+ Test random parameters.
+ """
+ if self.verbose:
+ print('Running __test_random_params()...')
+ for width in [8, 16, 32]:
+ for poly in [0x8005, 0x4c11db7, 0xa5a5a5a5]:
+ poly = poly & ((1 << width) - 1)
+ for refin in [0, 1]:
+ for refout in [0, 1]:
+ for init in [0x0, 0x1, 0x5a5a5a5a]:
+ args='--width {0:d} --poly {1:#x} --reflect-in {2} --reflect-out {3} --xor-in {4:#x} --xor-out 0x0'.format(width, poly, refin, refout, init)
+ cmd_str = self.pycrc_bin + ' ' + args
+ ret = self.__run_command(cmd_str)
+ if ret is None:
+ return False
+ ret = int(ret, 16)
+ if not self.__check_bin(args, ret, width > 32):
+ return False
+ return True
+
+
+ def run(self, opt):
+ """
+ Run all tests
+ """
+ self.use_algo_bit_by_bit = 'bit-by-bit' in opt.algorithm or 'bbb' in opt.algorithm
+ self.use_algo_bit_by_bit_fast = 'bit-by-bit-fast' in opt.algorithm or 'bbf' in opt.algorithm
+ self.use_algo_table_driven = 'table-driven' in opt.algorithm or 'tbl' in opt.algorithm
+ self.verbose = opt.verbose
+
+ if self.python3:
+ self.pycrc_bin = 'python3 pycrc.py'
+ else:
+ self.pycrc_bin = 'python pycrc.py'
+
+ if not self.__setup_files(opt):
+ return False
+
+ if not self.__test_models():
+ return False
+
+ if opt.Compile and not self.__test_compiled_models():
+ return False
+
+ if opt.Compile and not self.__test_compiled_special_cases():
+ return False
+
+ if opt.VariableWidth and not self.__test_variable_width():
+ return False
+
+ if opt.CompileMixedArgs and not self.__test_compiled_mixed_args():
+ return False
+
+ if opt.RandomParameters and not self.__test_random_params():
+ return False
+
+ return True
+
+
+def main():
+ """
+ Main function.
+ """
+ opt = Options()
+ opt.parse(sys.argv[1:])
+
+ test = CrcTests()
+ if not test.run(opt):
+ return 1
+ print('Test OK')
+ return 0
+
+
+# program entry point
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/y2014/control_loops/claw/claw_lib_test.cc b/y2014/control_loops/claw/claw_lib_test.cc
index 753c087..f10e61e 100644
--- a/y2014/control_loops/claw/claw_lib_test.cc
+++ b/y2014/control_loops/claw/claw_lib_test.cc
@@ -37,7 +37,7 @@
ClawMotorSimulation(double initial_top_position,
double initial_bottom_position)
: claw_plant_(new StateFeedbackPlant<4, 2, 2>(MakeClawPlant())),
- claw_queue(".y2014.control_loops.claw_queue", 0x9f1a99dd,
+ claw_queue(".y2014.control_loops.claw_queue",
".y2014.control_loops.claw_queue.goal",
".y2014.control_loops.claw_queue.position",
".y2014.control_loops.claw_queue.output",
@@ -254,7 +254,7 @@
double min_separation_;
ClawTest()
- : claw_queue(".y2014.control_loops.claw_queue", 0x9f1a99dd,
+ : claw_queue(".y2014.control_loops.claw_queue",
".y2014.control_loops.claw_queue.goal",
".y2014.control_loops.claw_queue.position",
".y2014.control_loops.claw_queue.output",
diff --git a/y2014/control_loops/shooter/shooter.cc b/y2014/control_loops/shooter/shooter.cc
index e2f104c..f61cc02 100644
--- a/y2014/control_loops/shooter/shooter.cc
+++ b/y2014/control_loops/shooter/shooter.cc
@@ -699,12 +699,10 @@
status->shots = shot_count_;
}
-void ShooterMotor::ZeroOutputs() {
- queue_group()->output.MakeWithBuilder()
- .voltage(0)
- .latch_piston(latch_piston_)
- .brake_piston(brake_piston_)
- .Send();
+void ShooterMotor::Zero(::y2014::control_loops::ShooterQueue::Output *output) {
+ output->voltage = 0.0;
+ output->latch_piston = latch_piston_;
+ output->brake_piston = brake_piston_;
}
} // namespace control_loops
diff --git a/y2014/control_loops/shooter/shooter.h b/y2014/control_loops/shooter/shooter.h
index 0530012..75a05db 100644
--- a/y2014/control_loops/shooter/shooter.h
+++ b/y2014/control_loops/shooter/shooter.h
@@ -41,7 +41,7 @@
const static int kZeroingMaxVoltage = 5;
- virtual void CapU();
+ void CapU() override;
// Returns the accumulated voltage.
double voltage() const { return voltage_; }
@@ -158,15 +158,15 @@
State state() { return state_; }
protected:
- virtual void RunIteration(
+ void RunIteration(
const ::y2014::control_loops::ShooterQueue::Goal *goal,
const ::y2014::control_loops::ShooterQueue::Position *position,
::y2014::control_loops::ShooterQueue::Output *output,
- ::y2014::control_loops::ShooterQueue::Status *status);
+ ::y2014::control_loops::ShooterQueue::Status *status) override;
private:
// We have to override this to keep the pistons in the correct positions.
- virtual void ZeroOutputs();
+ void Zero(::y2014::control_loops::ShooterQueue::Output *output) override;
// Friend the test classes for acces to the internal state.
friend class testing::ShooterTest_UnloadWindupPositive_Test;
diff --git a/y2014/control_loops/shooter/shooter_lib_test.cc b/y2014/control_loops/shooter/shooter_lib_test.cc
index 972b8b0..108502b 100644
--- a/y2014/control_loops/shooter/shooter_lib_test.cc
+++ b/y2014/control_loops/shooter/shooter_lib_test.cc
@@ -35,12 +35,11 @@
plunger_latched_(false),
brake_piston_state_(true),
brake_delay_count_(0),
- shooter_queue_(
- ".y2014.control_loops.shooter_queue", 0xcbf22ba9,
- ".y2014.control_loops.shooter_queue.goal",
- ".y2014.control_loops.shooter_queue.position",
- ".y2014.control_loops.shooter_queue.output",
- ".y2014.control_loops.shooter_queue.status") {
+ shooter_queue_(".y2014.control_loops.shooter_queue",
+ ".y2014.control_loops.shooter_queue.goal",
+ ".y2014.control_loops.shooter_queue.position",
+ ".y2014.control_loops.shooter_queue.output",
+ ".y2014.control_loops.shooter_queue.status") {
Reinitialize(initial_position);
}
@@ -299,15 +298,13 @@
}
ShooterTestTemplated()
- : shooter_queue_(
- ".y2014.control_loops.shooter_queue", 0xcbf22ba9,
- ".y2014.control_loops.shooter_queue.goal",
- ".y2014.control_loops.shooter_queue.position",
- ".y2014.control_loops.shooter_queue.output",
- ".y2014.control_loops.shooter_queue.status"),
+ : shooter_queue_(".y2014.control_loops.shooter_queue",
+ ".y2014.control_loops.shooter_queue.goal",
+ ".y2014.control_loops.shooter_queue.position",
+ ".y2014.control_loops.shooter_queue.output",
+ ".y2014.control_loops.shooter_queue.status"),
shooter_motor_(&shooter_queue_),
- shooter_motor_plant_(0.2) {
- }
+ shooter_motor_plant_(0.2) {}
void VerifyNearGoal() {
shooter_queue_.goal.FetchLatest();
diff --git a/y2016/control_loops/python/BUILD b/y2016/control_loops/python/BUILD
index 08a34e8..93212d8 100644
--- a/y2016/control_loops/python/BUILD
+++ b/y2016/control_loops/python/BUILD
@@ -70,11 +70,10 @@
restricted_to = ["//tools:k8"],
deps = [
":python_init",
- "//aos/util:py_trapezoid_profile",
"//external:python-gflags",
"//external:python-glog",
+ "//frc971/control_loops/python:angular_system",
"//frc971/control_loops/python:controls",
- "@matplotlib",
],
)
diff --git a/y2016/control_loops/python/intake.py b/y2016/control_loops/python/intake.py
index abb755c..ceb4170 100755
--- a/y2016/control_loops/python/intake.py
+++ b/y2016/control_loops/python/intake.py
@@ -2,6 +2,7 @@
from aos.util.trapezoid_profile import TrapezoidProfile
from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import angular_system
from frc971.control_loops.python import controls
import numpy
import sys
@@ -12,302 +13,41 @@
FLAGS = gflags.FLAGS
try:
- gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
except gflags.DuplicateFlagError:
- pass
+ pass
-class Intake(control_loop.ControlLoop):
- def __init__(self, name="Intake"):
- super(Intake, self).__init__(name)
- # TODO(constants): Update all of these & retune poles.
- # Stall Torque in N m
- self.stall_torque = 0.71
- # Stall Current in Amps
- self.stall_current = 134
- # Free Speed in RPM
- self.free_speed = 18730
- # Free Current in Amps
- self.free_current = 0.7
-
- # Resistance of the motor
- self.R = 12.0 / self.stall_current
- # Motor velocity constant
- self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
- (12.0 - self.R * self.free_current))
- # Torque constant
- self.Kt = self.stall_torque / self.stall_current
- # Gear ratio
- self.G = (56.0 / 12.0) * (54.0 / 14.0) * (64.0 / 18.0) * (48.0 / 16.0)
-
- # Moment of inertia, measured in CAD.
- # Extra mass to compensate for friction is added on.
- self.J = 0.34 + 0.40
-
- # Control loop time step
- self.dt = 0.005
-
- # State is [position, velocity]
- # Input is [Voltage]
-
- C1 = self.G * self.G * self.Kt / (self.R * self.J * self.Kv)
- C2 = self.Kt * self.G / (self.J * self.R)
-
- self.A_continuous = numpy.matrix(
- [[0, 1],
- [0, -C1]])
-
- # Start with the unmodified input
- self.B_continuous = numpy.matrix(
- [[0],
- [C2]])
-
- self.C = numpy.matrix([[1, 0]])
- self.D = numpy.matrix([[0]])
-
- self.A, self.B = self.ContinuousToDiscrete(
- self.A_continuous, self.B_continuous, self.dt)
-
- controllability = controls.ctrb(self.A, self.B)
-
- glog.debug("Free speed is %f", self.free_speed * numpy.pi * 2.0 / 60.0 / self.G)
-
- q_pos = 0.20
- q_vel = 5.0
- self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0],
- [0.0, (1.0 / (q_vel ** 2.0))]])
-
- self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0))]])
- self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
-
- q_pos_ff = 0.005
- q_vel_ff = 1.0
- self.Qff = numpy.matrix([[(1.0 / (q_pos_ff ** 2.0)), 0.0],
- [0.0, (1.0 / (q_vel_ff ** 2.0))]])
-
- self.Kff = controls.TwoStateFeedForwards(self.B, self.Qff)
-
- glog.debug('K %s', repr(self.K))
- glog.debug('Poles are %s',
- repr(numpy.linalg.eig(self.A - self.B * self.K)[0]))
-
- self.rpl = 0.30
- self.ipl = 0.10
- self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
- self.rpl - 1j * self.ipl])
-
- glog.debug('L is %s', repr(self.L))
-
- q_pos = 0.10
- q_vel = 1.65
- self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0],
- [0.0, (q_vel ** 2.0)]])
-
- r_volts = 0.025
- self.R = numpy.matrix([[(r_volts ** 2.0)]])
-
- self.KalmanGain, self.Q_steady = controls.kalman(
- A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
-
- glog.debug('Kal %s', repr(self.KalmanGain))
- self.L = self.A * self.KalmanGain
- glog.debug('KalL is %s', repr(self.L))
-
- # The box formed by U_min and U_max must encompass all possible values,
- # or else Austin's code gets angry.
- self.U_max = numpy.matrix([[12.0]])
- self.U_min = numpy.matrix([[-12.0]])
-
- self.InitializeState()
-
-class IntegralIntake(Intake):
- def __init__(self, name="IntegralIntake"):
- super(IntegralIntake, self).__init__(name=name)
-
- self.A_continuous_unaugmented = self.A_continuous
- self.B_continuous_unaugmented = self.B_continuous
-
- self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
- self.A_continuous[0:2, 0:2] = self.A_continuous_unaugmented
- self.A_continuous[0:2, 2] = self.B_continuous_unaugmented
-
- self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
- self.B_continuous[0:2, 0] = self.B_continuous_unaugmented
-
- self.C_unaugmented = self.C
- self.C = numpy.matrix(numpy.zeros((1, 3)))
- self.C[0:1, 0:2] = self.C_unaugmented
-
- self.A, self.B = self.ContinuousToDiscrete(
- self.A_continuous, self.B_continuous, self.dt)
-
- q_pos = 0.12
- q_vel = 2.00
- q_voltage = 4.0
- self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0, 0.0],
- [0.0, (q_vel ** 2.0), 0.0],
- [0.0, 0.0, (q_voltage ** 2.0)]])
-
- r_pos = 0.05
- self.R = numpy.matrix([[(r_pos ** 2.0)]])
-
- self.KalmanGain, self.Q_steady = controls.kalman(
- A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
- self.L = self.A * self.KalmanGain
-
- self.K_unaugmented = self.K
- self.K = numpy.matrix(numpy.zeros((1, 3)))
- self.K[0, 0:2] = self.K_unaugmented
- self.K[0, 2] = 1
-
- self.Kff = numpy.concatenate((self.Kff, numpy.matrix(numpy.zeros((1, 1)))), axis=1)
-
- self.InitializeState()
-
-class ScenarioPlotter(object):
- def __init__(self):
- # Various lists for graphing things.
- self.t = []
- self.x = []
- self.v = []
- self.a = []
- self.x_hat = []
- self.u = []
- self.offset = []
-
- def run_test(self, intake, end_goal,
- controller_intake,
- observer_intake=None,
- iterations=200):
- """Runs the intake plant with an initial condition and goal.
-
- Args:
- intake: intake object to use.
- end_goal: end_goal state.
- controller_intake: Intake object to get K from, or None if we should
- use intake.
- observer_intake: Intake object to use for the observer, or None if we should
- use the actual state.
- iterations: Number of timesteps to run the model for.
- """
-
- if controller_intake is None:
- controller_intake = intake
-
- vbat = 12.0
-
- if self.t:
- initial_t = self.t[-1] + intake.dt
- else:
- initial_t = 0
-
- goal = numpy.concatenate((intake.X, numpy.matrix(numpy.zeros((1, 1)))), axis=0)
-
- profile = TrapezoidProfile(intake.dt)
- profile.set_maximum_acceleration(70.0)
- profile.set_maximum_velocity(10.0)
- profile.SetGoal(goal[0, 0])
-
- U_last = numpy.matrix(numpy.zeros((1, 1)))
- for i in xrange(iterations):
- observer_intake.Y = intake.Y
- observer_intake.CorrectObserver(U_last)
-
- self.offset.append(observer_intake.X_hat[2, 0])
- self.x_hat.append(observer_intake.X_hat[0, 0])
-
- next_goal = numpy.concatenate(
- (profile.Update(end_goal[0, 0], end_goal[1, 0]),
- numpy.matrix(numpy.zeros((1, 1)))),
- axis=0)
-
- ff_U = controller_intake.Kff * (next_goal - observer_intake.A * goal)
-
- U_uncapped = controller_intake.K * (goal - observer_intake.X_hat) + ff_U
- U = U_uncapped.copy()
- U[0, 0] = numpy.clip(U[0, 0], -vbat, vbat)
- self.x.append(intake.X[0, 0])
-
- if self.v:
- last_v = self.v[-1]
- else:
- last_v = 0
-
- self.v.append(intake.X[1, 0])
- self.a.append((self.v[-1] - last_v) / intake.dt)
-
- offset = 0.0
- if i > 100:
- offset = 2.0
- intake.Update(U + offset)
-
- observer_intake.PredictObserver(U)
-
- self.t.append(initial_t + i * intake.dt)
- self.u.append(U[0, 0])
-
- ff_U -= U_uncapped - U
- goal = controller_intake.A * goal + controller_intake.B * ff_U
-
- if U[0, 0] != U_uncapped[0, 0]:
- profile.MoveCurrentState(
- numpy.matrix([[goal[0, 0]], [goal[1, 0]]]))
-
- glog.debug('Time: %f', self.t[-1])
- glog.debug('goal_error %s', repr(end_goal - goal))
- glog.debug('error %s', repr(observer_intake.X_hat - end_goal))
-
- def Plot(self):
- pylab.subplot(3, 1, 1)
- pylab.plot(self.t, self.x, label='x')
- pylab.plot(self.t, self.x_hat, label='x_hat')
- pylab.legend()
-
- pylab.subplot(3, 1, 2)
- pylab.plot(self.t, self.u, label='u')
- pylab.plot(self.t, self.offset, label='voltage_offset')
- pylab.legend()
-
- pylab.subplot(3, 1, 3)
- pylab.plot(self.t, self.a, label='a')
- pylab.legend()
-
- pylab.show()
+kIntake = angular_system.AngularSystemParams(
+ name='Intake',
+ motor=control_loop.Vex775Pro(),
+ # (1 / 35.0) * (20.0 / 40.0) -> 16 tooth sprocket on #25 chain
+ G=(12.0 / 56.0) * (14.0 / 54.0) * (18.0 / 64.0) * (16.0 / 48.0),
+ J=0.34 - 0.03757568,
+ q_pos=0.20,
+ q_vel=5.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=4.0,
+ kalman_r_position=0.05)
def main(argv):
- argv = FLAGS(argv)
- glog.init()
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi / 2.0], [0.0]])
+ angular_system.PlotMotion(kIntake, R)
- scenario_plotter = ScenarioPlotter()
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the intake and integral intake.'
+ )
+ else:
+ namespaces = ['y2016', 'control_loops', 'superstructure']
+ angular_system.WriteAngularSystem(kIntake, argv[1:3], argv[3:5],
+ namespaces)
- intake = Intake()
- intake_controller = IntegralIntake()
- observer_intake = IntegralIntake()
-
- # Test moving the intake with constant separation.
- initial_X = numpy.matrix([[0.0], [0.0]])
- R = numpy.matrix([[numpy.pi/2.0], [0.0], [0.0]])
- scenario_plotter.run_test(intake, end_goal=R,
- controller_intake=intake_controller,
- observer_intake=observer_intake, iterations=200)
-
- if FLAGS.plot:
- scenario_plotter.Plot()
-
- # Write the generated constants out to a file.
- if len(argv) != 5:
- glog.fatal('Expected .h file name and .cc file name for the intake and integral intake.')
- else:
- namespaces = ['y2016', 'control_loops', 'superstructure']
- intake = Intake("Intake")
- loop_writer = control_loop.ControlLoopWriter('Intake', [intake],
- namespaces=namespaces)
- loop_writer.Write(argv[1], argv[2])
-
- integral_intake = IntegralIntake("IntegralIntake")
- integral_loop_writer = control_loop.ControlLoopWriter("IntegralIntake", [integral_intake],
- namespaces=namespaces)
- integral_loop_writer.Write(argv[3], argv[4])
if __name__ == '__main__':
- sys.exit(main(sys.argv))
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2016/control_loops/shooter/shooter_lib_test.cc b/y2016/control_loops/shooter/shooter_lib_test.cc
index c5d5aa7..bba603f 100644
--- a/y2016/control_loops/shooter/shooter_lib_test.cc
+++ b/y2016/control_loops/shooter/shooter_lib_test.cc
@@ -53,7 +53,7 @@
::y2016::control_loops::shooter::MakeShooterPlant())),
shooter_plant_right_(new ShooterPlant(
::y2016::control_loops::shooter::MakeShooterPlant())),
- shooter_queue_(".y2016.control_loops.shooter", 0x78d8e372,
+ shooter_queue_(".y2016.control_loops.shooter",
".y2016.control_loops.shooter.goal",
".y2016.control_loops.shooter.position",
".y2016.control_loops.shooter.output",
@@ -101,7 +101,7 @@
class ShooterTest : public ::aos::testing::ControlLoopTest {
protected:
ShooterTest()
- : shooter_queue_(".y2016.control_loops.shooter", 0x78d8e372,
+ : shooter_queue_(".y2016.control_loops.shooter",
".y2016.control_loops.shooter.goal",
".y2016.control_loops.shooter.position",
".y2016.control_loops.shooter.output",
diff --git a/y2016/control_loops/superstructure/superstructure_lib_test.cc b/y2016/control_loops/superstructure/superstructure_lib_test.cc
index 824f291..e4d1301 100644
--- a/y2016/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2016/control_loops/superstructure/superstructure_lib_test.cc
@@ -88,7 +88,7 @@
pot_encoder_shoulder_(
constants::Values::kShoulderEncoderIndexDifference),
pot_encoder_wrist_(constants::Values::kWristEncoderIndexDifference),
- superstructure_queue_(".y2016.control_loops.superstructure", 0x0,
+ superstructure_queue_(".y2016.control_loops.superstructure",
".y2016.control_loops.superstructure.goal",
".y2016.control_loops.superstructure.status",
".y2016.control_loops.superstructure.output",
@@ -246,7 +246,7 @@
class SuperstructureTest : public ::aos::testing::ControlLoopTest {
protected:
SuperstructureTest()
- : superstructure_queue_(".y2016.control_loops.superstructure", 0x0,
+ : superstructure_queue_(".y2016.control_loops.superstructure",
".y2016.control_loops.superstructure.goal",
".y2016.control_loops.superstructure.status",
".y2016.control_loops.superstructure.output",
diff --git a/y2017/control_loops/python/BUILD b/y2017/control_loops/python/BUILD
index 3f23bde..50f5584 100644
--- a/y2017/control_loops/python/BUILD
+++ b/y2017/control_loops/python/BUILD
@@ -88,6 +88,7 @@
":python_init",
"//external:python-gflags",
"//external:python-glog",
+ "//frc971/control_loops/python:controls",
"//frc971/control_loops/python:linear_system",
],
)
diff --git a/y2017/control_loops/superstructure/BUILD b/y2017/control_loops/superstructure/BUILD
index 3a3d17e..732c6dc 100644
--- a/y2017/control_loops/superstructure/BUILD
+++ b/y2017/control_loops/superstructure/BUILD
@@ -1,123 +1,123 @@
-package(default_visibility = ['//visibility:public'])
+package(default_visibility = ["//visibility:public"])
-load('//aos/build:queues.bzl', 'queue_library')
+load("//aos/build:queues.bzl", "queue_library")
queue_library(
- name = 'superstructure_queue',
- srcs = [
- 'superstructure.q',
- ],
- deps = [
- '//aos/controls:control_loop_queues',
- '//frc971/control_loops:profiled_subsystem_queue',
- '//frc971/control_loops:queues',
- ],
+ name = "superstructure_queue",
+ srcs = [
+ "superstructure.q",
+ ],
+ deps = [
+ "//aos/controls:control_loop_queues",
+ "//frc971/control_loops:profiled_subsystem_queue",
+ "//frc971/control_loops:queues",
+ ],
)
cc_library(
- name = 'superstructure_lib',
- srcs = [
- 'superstructure.cc',
- ],
- hdrs = [
- 'superstructure.h',
- ],
- deps = [
- ':vision_distance_average',
- ':superstructure_queue',
- '//aos/controls:control_loop',
- '//y2017/control_loops/superstructure/column',
- '//y2017/control_loops/superstructure/hood',
- '//y2017/control_loops/superstructure/intake',
- '//y2017/control_loops/superstructure/shooter',
- '//y2017:constants',
- ],
+ name = "superstructure_lib",
+ srcs = [
+ "superstructure.cc",
+ ],
+ hdrs = [
+ "superstructure.h",
+ ],
+ deps = [
+ ":superstructure_queue",
+ ":vision_distance_average",
+ "//aos/controls:control_loop",
+ "//y2017:constants",
+ "//y2017/control_loops/superstructure/column",
+ "//y2017/control_loops/superstructure/hood",
+ "//y2017/control_loops/superstructure/intake",
+ "//y2017/control_loops/superstructure/shooter",
+ ],
)
cc_test(
- name = 'superstructure_lib_test',
- srcs = [
- 'superstructure_lib_test.cc',
- ],
- deps = [
- ':superstructure_queue',
- ':superstructure_lib',
- '//aos/controls:control_loop_test',
- '//aos:math',
- '//aos:queues',
- '//aos/time:time',
- '//aos/testing:googletest',
- '//frc971/control_loops:position_sensor_sim',
- '//frc971/control_loops:team_number_test_environment',
- '//y2017/control_loops/superstructure/column:column_plants',
- '//y2017/control_loops/superstructure/hood:hood_plants',
- '//y2017/control_loops/superstructure/intake:intake_plants',
- '//y2017/control_loops/superstructure/shooter:shooter_plants',
- ],
+ name = "superstructure_lib_test",
+ srcs = [
+ "superstructure_lib_test.cc",
+ ],
+ deps = [
+ ":superstructure_lib",
+ ":superstructure_queue",
+ "//aos:math",
+ "//aos:queues",
+ "//aos/controls:control_loop_test",
+ "//aos/testing:googletest",
+ "//aos/time",
+ "//frc971/control_loops:position_sensor_sim",
+ "//frc971/control_loops:team_number_test_environment",
+ "//y2017/control_loops/superstructure/column:column_plants",
+ "//y2017/control_loops/superstructure/hood:hood_plants",
+ "//y2017/control_loops/superstructure/intake:intake_plants",
+ "//y2017/control_loops/superstructure/shooter:shooter_plants",
+ ],
)
cc_binary(
- name = 'superstructure',
- srcs = [
- 'superstructure_main.cc',
- ],
- deps = [
- '//aos:init',
- ':superstructure_lib',
- ':superstructure_queue',
- ],
+ name = "superstructure",
+ srcs = [
+ "superstructure_main.cc",
+ ],
+ deps = [
+ ":superstructure_lib",
+ ":superstructure_queue",
+ "//aos:init",
+ ],
)
cc_library(
- name = 'vision_time_adjuster',
- hdrs = [
- 'vision_time_adjuster.h',
- ],
- srcs = [
- 'vision_time_adjuster.cc',
- ],
- deps = [
- ':superstructure_queue',
- '//aos/ring_buffer:ring_buffer',
- '//frc971/control_loops/drivetrain:drivetrain_queue',
- '//y2017/control_loops/drivetrain:polydrivetrain_plants',
- '//y2017/vision:vision_queue',
- ],
+ name = "vision_time_adjuster",
+ srcs = [
+ "vision_time_adjuster.cc",
+ ],
+ hdrs = [
+ "vision_time_adjuster.h",
+ ],
+ deps = [
+ ":superstructure_queue",
+ "//aos/containers:ring_buffer",
+ "//frc971/control_loops/drivetrain:drivetrain_queue",
+ "//y2017/control_loops/drivetrain:polydrivetrain_plants",
+ "//y2017/vision:vision_queue",
+ ],
)
cc_test(
- name = 'vision_time_adjuster_test',
- srcs = [
- 'vision_time_adjuster_test.cc',
- ],
- deps = [
- ':vision_time_adjuster',
- '//aos/time:time',
- '//aos/testing:googletest',
- '//aos/testing:test_shm',
- ],
+ name = "vision_time_adjuster_test",
+ srcs = [
+ "vision_time_adjuster_test.cc",
+ ],
+ deps = [
+ ":vision_time_adjuster",
+ "//aos/testing:googletest",
+ "//aos/testing:test_shm",
+ "//aos/time",
+ ],
)
cc_library(
- name = 'vision_distance_average',
- hdrs = [
- 'vision_distance_average.h',
- ],
- deps = [
- '//aos/time:time',
- '//aos/ring_buffer:ring_buffer',
- '//y2017/vision:vision_queue',
- ],
+ name = "vision_distance_average",
+ hdrs = [
+ "vision_distance_average.h",
+ ],
+ deps = [
+ "//aos/containers:ring_buffer",
+ "//aos/time",
+ "//y2017/vision:vision_queue",
+ ],
)
cc_test(
- name = 'vision_distance_average_test',
- srcs = [
- 'vision_distance_average_test.cc',
- ],
- deps = [
- ':vision_distance_average',
- '//aos/time:time',
- '//aos/testing:googletest',
- ],
+ name = "vision_distance_average_test",
+ srcs = [
+ "vision_distance_average_test.cc",
+ ],
+ deps = [
+ ":vision_distance_average",
+ "//aos/testing:googletest",
+ "//aos/time",
+ ],
)
diff --git a/y2017/control_loops/superstructure/intake/intake.h b/y2017/control_loops/superstructure/intake/intake.h
index 941b4ce..40f49fc 100644
--- a/y2017/control_loops/superstructure/intake/intake.h
+++ b/y2017/control_loops/superstructure/intake/intake.h
@@ -56,7 +56,8 @@
int disable_count_ = 0;
::frc971::control_loops::SingleDOFProfiledSubsystem<
- ::frc971::zeroing::PotAndAbsEncoderZeroingEstimator> profiled_subsystem_;
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator>
+ profiled_subsystem_;
};
} // namespace intake
diff --git a/y2017/control_loops/superstructure/superstructure_lib_test.cc b/y2017/control_loops/superstructure/superstructure_lib_test.cc
index 894f99d..de47690 100644
--- a/y2017/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2017/control_loops/superstructure/superstructure_lib_test.cc
@@ -134,7 +134,7 @@
column_plant_(new ColumnPlant(
::y2017::control_loops::superstructure::column::MakeColumnPlant())),
- superstructure_queue_(".y2017.control_loops.superstructure", 0xdeadbeef,
+ superstructure_queue_(".y2017.control_loops.superstructure",
".y2017.control_loops.superstructure.goal",
".y2017.control_loops.superstructure.position",
".y2017.control_loops.superstructure.output",
@@ -432,7 +432,7 @@
class SuperstructureTest : public ::aos::testing::ControlLoopTest {
protected:
SuperstructureTest()
- : superstructure_queue_(".y2017.control_loops.superstructure", 0xdeadbeef,
+ : superstructure_queue_(".y2017.control_loops.superstructure",
".y2017.control_loops.superstructure.goal",
".y2017.control_loops.superstructure.position",
".y2017.control_loops.superstructure.output",
diff --git a/y2017/control_loops/superstructure/vision_distance_average.h b/y2017/control_loops/superstructure/vision_distance_average.h
index 22464b7..74e775d 100644
--- a/y2017/control_loops/superstructure/vision_distance_average.h
+++ b/y2017/control_loops/superstructure/vision_distance_average.h
@@ -4,7 +4,7 @@
#include <stdint.h>
#include <chrono>
-#include "aos/ring_buffer/ring_buffer.h"
+#include "aos/containers/ring_buffer.h"
#include "aos/time/time.h"
#include "y2017/vision/vision.q.h"
diff --git a/y2017/control_loops/superstructure/vision_time_adjuster.h b/y2017/control_loops/superstructure/vision_time_adjuster.h
index 52e7d70..b475a99 100644
--- a/y2017/control_loops/superstructure/vision_time_adjuster.h
+++ b/y2017/control_loops/superstructure/vision_time_adjuster.h
@@ -3,7 +3,7 @@
#include <stdint.h>
-#include "aos/ring_buffer/ring_buffer.h"
+#include "aos/containers/ring_buffer.h"
#include "aos/time/time.h"
#include "y2017/vision/vision.q.h"
diff --git a/y2018/control_loops/python/BUILD b/y2018/control_loops/python/BUILD
index 43fa3a8..14f8290 100644
--- a/y2018/control_loops/python/BUILD
+++ b/y2018/control_loops/python/BUILD
@@ -163,18 +163,6 @@
],
)
-py_library(
- name = "basic_window",
- srcs = [
- "basic_window.py",
- "color.py",
- ],
- restricted_to = ["//tools:k8"],
- deps = [
- ":python_init",
- "@python_gtk",
- ],
-)
py_binary(
name = "graph_edit",
@@ -187,7 +175,7 @@
restricted_to = ["//tools:k8"],
srcs_version = "PY3",
deps = [
- ":basic_window",
+ "//frc971/control_loops/python:basic_window",
":python_init",
"@python_gtk",
],
@@ -207,22 +195,6 @@
],
)
-py_binary(
- name = "path_edit",
- srcs = [
- "path_edit.py",
- ],
- default_python_version = "PY3",
- legacy_create_init = False,
- restricted_to = ["//tools:k8"],
- srcs_version = "PY3",
- deps = [
- ":basic_window",
- ":python_init",
- "@python_gtk",
- ],
-)
-
py_library(
name = "python_init",
srcs = ["__init__.py"],
diff --git a/y2018/control_loops/superstructure/arm/arm.h b/y2018/control_loops/superstructure/arm/arm.h
index a9b3590..0a4666a 100644
--- a/y2018/control_loops/superstructure/arm/arm.h
+++ b/y2018/control_loops/superstructure/arm/arm.h
@@ -91,8 +91,10 @@
::aos::monotonic_clock::time_point claw_close_start_time_ =
::aos::monotonic_clock::min_time;
- ::frc971::zeroing::PotAndAbsEncoderZeroingEstimator proximal_zeroing_estimator_;
- ::frc971::zeroing::PotAndAbsEncoderZeroingEstimator distal_zeroing_estimator_;
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator
+ proximal_zeroing_estimator_;
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator
+ distal_zeroing_estimator_;
double proximal_offset_ = 0.0;
double distal_offset_ = 0.0;
diff --git a/y2018/control_loops/superstructure/intake/intake.h b/y2018/control_loops/superstructure/intake/intake.h
index dff0adc..552086a 100644
--- a/y2018/control_loops/superstructure/intake/intake.h
+++ b/y2018/control_loops/superstructure/intake/intake.h
@@ -104,7 +104,7 @@
State state_ = State::UNINITIALIZED;
- ::frc971::zeroing::PotAndAbsEncoderZeroingEstimator zeroing_estimator_;
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator zeroing_estimator_;
double intake_last_position_ = 0.0;
};
diff --git a/y2018/control_loops/superstructure/superstructure.q b/y2018/control_loops/superstructure/superstructure.q
index ee38539..19fbc05 100644
--- a/y2018/control_loops/superstructure/superstructure.q
+++ b/y2018/control_loops/superstructure/superstructure.q
@@ -35,7 +35,7 @@
float delayed_voltage;
// State of the estimator.
- .frc971.AbsoluteEstimatorState estimator_state;
+ .frc971.PotAndAbsoluteEncoderEstimatorState estimator_state;
};
struct IntakeGoal {
@@ -62,8 +62,8 @@
struct ArmStatus {
// State of the estimators.
- .frc971.AbsoluteEstimatorState proximal_estimator_state;
- .frc971.AbsoluteEstimatorState distal_estimator_state;
+ .frc971.PotAndAbsoluteEncoderEstimatorState proximal_estimator_state;
+ .frc971.PotAndAbsoluteEncoderEstimatorState distal_estimator_state;
// The node we are currently going to.
uint32_t current_node;
diff --git a/y2018/control_loops/superstructure/superstructure_lib_test.cc b/y2018/control_loops/superstructure/superstructure_lib_test.cc
index 62b20f8..0856a7d 100644
--- a/y2018/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2018/control_loops/superstructure/superstructure_lib_test.cc
@@ -196,7 +196,7 @@
constants::GetValues().right_intake.zeroing),
arm_(constants::GetValues().arm_proximal.zeroing,
constants::GetValues().arm_distal.zeroing),
- superstructure_queue_(".y2018.control_loops.superstructure", 0xdeadbeef,
+ superstructure_queue_(".y2018.control_loops.superstructure",
".y2018.control_loops.superstructure.goal",
".y2018.control_loops.superstructure.position",
".y2018.control_loops.superstructure.output",
@@ -268,7 +268,7 @@
class SuperstructureTest : public ::aos::testing::ControlLoopTest {
protected:
SuperstructureTest()
- : superstructure_queue_(".y2018.control_loops.superstructure", 0xdeadbeef,
+ : superstructure_queue_(".y2018.control_loops.superstructure",
".y2018.control_loops.superstructure.goal",
".y2018.control_loops.superstructure.position",
".y2018.control_loops.superstructure.output",
diff --git a/y2018/vision/exposure_loop.sh b/y2018/vision/exposure_loop.sh
new file mode 100755
index 0000000..5fd2da5
--- /dev/null
+++ b/y2018/vision/exposure_loop.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+while true
+do
+ v4l2-ctl --set-ctrl="exposure_absolute=300" -d /dev/video0
+ v4l2-ctl --set-ctrl="exposure_absolute=300" -d /dev/video1
+ sleep 10
+done
diff --git a/y2019/BUILD b/y2019/BUILD
index 49ae5a5..52c0264 100644
--- a/y2019/BUILD
+++ b/y2019/BUILD
@@ -1,5 +1,23 @@
+cc_library(
+ name = "constants",
+ srcs = [
+ "constants.cc",
+ ],
+ hdrs = [
+ "constants.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos:once",
+ "//aos/logging",
+ "//aos/mutex",
+ "//aos/network:team_number",
+ "//frc971:constants",
+ ],
+)
+
py_library(
name = "python_init",
srcs = ["__init__.py"],
visibility = ["//visibility:public"],
-)
+)
\ No newline at end of file
diff --git a/y2019/constants.cc b/y2019/constants.cc
new file mode 100644
index 0000000..0851577
--- /dev/null
+++ b/y2019/constants.cc
@@ -0,0 +1,83 @@
+#include "y2019/constants.h"
+
+#include <inttypes.h>
+
+#include <map>
+
+#if __has_feature(address_sanitizer)
+#include "sanitizer/lsan_interface.h"
+#endif
+
+#include "aos/logging/logging.h"
+#include "aos/mutex/mutex.h"
+#include "aos/network/team_number.h"
+#include "aos/once.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace y2019 {
+namespace constants {
+
+const int Values::kZeroingSampleSize;
+
+namespace {
+
+const uint16_t kCompTeamNumber = 971;
+const uint16_t kPracticeTeamNumber = 9971;
+
+const Values *DoGetValuesForTeam(uint16_t team) {
+ Values *const r = new Values();
+
+ switch (team) {
+ // A set of constants for tests.
+ case 1:
+ break;
+
+ case kCompTeamNumber:
+ break;
+
+ case kPracticeTeamNumber:
+ break;
+
+ default:
+ LOG(FATAL, "unknown team #%" PRIu16 "\n", team);
+ }
+
+ return r;
+}
+
+const Values *DoGetValues() {
+ uint16_t team = ::aos::network::GetTeamNumber();
+ LOG(INFO, "creating a Constants for team %" PRIu16 "\n", team);
+ return DoGetValuesForTeam(team);
+}
+
+} // namespace
+
+const Values &GetValues() {
+ static ::aos::Once<const Values> once(DoGetValues);
+ return *once.Get();
+}
+
+const Values &GetValuesForTeam(uint16_t team_number) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker locker(&mutex);
+
+ // IMPORTANT: This declaration has to stay after the mutex is locked to avoid
+ // race conditions.
+ static ::std::map<uint16_t, const Values *> values;
+
+ if (values.count(team_number) == 0) {
+ values[team_number] = DoGetValuesForTeam(team_number);
+#if __has_feature(address_sanitizer)
+ __lsan_ignore_object(values[team_number]);
+#endif
+ }
+ return *values[team_number];
+}
+
+
+} // namespace constants
+} // namespace y2019
diff --git a/y2019/constants.h b/y2019/constants.h
new file mode 100644
index 0000000..e4dda86
--- /dev/null
+++ b/y2019/constants.h
@@ -0,0 +1,37 @@
+#ifndef Y2019_CONSTANTS_H_
+#define Y2019_CONSTANTS_H_
+
+#include <stdint.h>
+#include <math.h>
+
+#include "frc971/constants.h"
+
+namespace y2019 {
+namespace constants {
+
+// Has all of our "constants", except the ones that come from other places. The
+// ones which change between robots are put together with a workable way to
+// retrieve the values for the current robot.
+
+// Everything is in SI units (volts, radians, meters, seconds, etc).
+// Some of these values are related to the conversion between raw values
+// (encoder counts, voltage, etc) to scaled units (radians, meters, etc).
+//
+// All ratios are from the encoder shaft to the output units.
+
+struct Values {
+ static const int kZeroingSampleSize = 200;
+};
+
+// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
+// returns a reference to it.
+const Values &GetValues();
+
+// Creates Values instances for each team number it is called with and returns
+// them.
+const Values &GetValuesForTeam(uint16_t team_number);
+
+} // namespace constants
+} // namespace y2019
+
+#endif // Y2019_CONSTANTS_H_
diff --git a/y2019/control_loops/python/BUILD b/y2019/control_loops/python/BUILD
index 9dddf12..f3e4ca1 100644
--- a/y2019/control_loops/python/BUILD
+++ b/y2019/control_loops/python/BUILD
@@ -67,6 +67,55 @@
":python_init",
"//external:python-gflags",
"//external:python-glog",
+ "//frc971/control_loops/python:controls",
"//frc971/control_loops/python:linear_system",
],
)
+
+py_binary(
+ name = "wrist",
+ srcs = [
+ "wrist.py",
+ ],
+ legacy_create_init = False,
+ restricted_to = ["//tools:k8"],
+ deps = [
+ ":python_init",
+ "//external:python-gflags",
+ "//external:python-glog",
+ "//frc971/control_loops/python:angular_system",
+ "//frc971/control_loops/python:controls",
+ ],
+)
+
+py_binary(
+ name = "intake",
+ srcs = [
+ "intake.py",
+ ],
+ legacy_create_init = False,
+ restricted_to = ["//tools:k8"],
+ deps = [
+ ":python_init",
+ "//external:python-gflags",
+ "//external:python-glog",
+ "//frc971/control_loops/python:angular_system",
+ "//frc971/control_loops/python:controls",
+ ],
+)
+
+py_binary(
+ name = "lift",
+ srcs = [
+ "lift.py",
+ ],
+ legacy_create_init = False,
+ restricted_to = ["//tools:k8"],
+ deps = [
+ ":python_init",
+ "//external:python-gflags",
+ "//external:python-glog",
+ "//frc971/control_loops/python:linear_system",
+ "//frc971/control_loops/python:controls",
+ ],
+)
diff --git a/y2019/control_loops/python/intake.py b/y2019/control_loops/python/intake.py
new file mode 100755
index 0000000..97f8880
--- /dev/null
+++ b/y2019/control_loops/python/intake.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import angular_system
+from frc971.control_loops.python import controls
+import numpy
+import sys
+from matplotlib import pylab
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+kIntake = angular_system.AngularSystemParams(
+ name='Intake',
+ motor=control_loop.BAG(),
+ G=(1.0 / 9.0) * (1.0 / 9.0) * (16.0 / 38.0),
+ # TODO(austin): Pull moments of inertia from CAD when it's done.
+ J=0.8,
+ q_pos=0.20,
+ q_vel=5.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=4.0,
+ kalman_r_position=0.05)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi / 2.0], [0.0]])
+ angular_system.PlotMotion(kIntake, R)
+ angular_system.PlotKick(kIntake, R)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the intake and integral intake.'
+ )
+ else:
+ namespaces = ['y2019', 'control_loops', 'superstructure', 'intake']
+ angular_system.WriteAngularSystem(kIntake, argv[1:3], argv[3:5],
+ namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2019/control_loops/python/lift.py b/y2019/control_loops/python/lift.py
new file mode 100755
index 0000000..dab2754
--- /dev/null
+++ b/y2019/control_loops/python/lift.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import linear_system
+import numpy
+import sys
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+kLift = linear_system.LinearSystemParams(
+ name='Lift',
+ motor=control_loop.Vex775Pro(),
+ G=(12.0 / 100.0) * (14.0 / 52.0),
+ # 5mm pitch, 18 tooth
+ radius=0.005 * 18.0 / (2.0 * numpy.pi),
+ # Or, 2.34 lb * 2 (2.1 kg) when lifting back up
+ mass=40.0,
+ q_pos=0.070,
+ q_vel=1.2,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.00,
+ kalman_q_voltage=35.0,
+ kalman_r_position=0.05,
+ dt=0.00505)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[1.5], [0.0]])
+ linear_system.PlotKick(kLift, R)
+ linear_system.PlotMotion(kLift, R, max_velocity=5.0)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the elevator and integral elevator.'
+ )
+ else:
+ namespaces = ['y2019', 'control_loops', 'superstructure', 'lift']
+ linear_system.WriteLinearSystem(kLift, argv[1:3], argv[3:5],
+ namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2019/control_loops/python/wrist.py b/y2019/control_loops/python/wrist.py
new file mode 100755
index 0000000..0229f6e
--- /dev/null
+++ b/y2019/control_loops/python/wrist.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import angular_system
+from frc971.control_loops.python import controls
+import numpy
+import sys
+from matplotlib import pylab
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+kWrist = angular_system.AngularSystemParams(
+ name='Wrist',
+ motor=control_loop.BAG(),
+ G=(6.0 / 60.0) * (20.0 / 100.0) * (24.0 / 84.0),
+ # TODO(austin): Pull moments of inertia from CAD when it's done.
+ J=0.34,
+ q_pos=0.20,
+ q_vel=5.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=4.0,
+ kalman_r_position=0.05)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi / 2.0], [0.0]])
+ angular_system.PlotMotion(kWrist, R)
+ angular_system.PlotKick(kWrist, R)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 5:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the wrist and integral wrist.'
+ )
+ else:
+ namespaces = ['y2019', 'control_loops', 'superstructure', 'wrist']
+ angular_system.WriteAngularSystem(kWrist, argv[1:3], argv[3:5],
+ namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2019/control_loops/superstructure/BUILD b/y2019/control_loops/superstructure/BUILD
new file mode 100644
index 0000000..79d851a
--- /dev/null
+++ b/y2019/control_loops/superstructure/BUILD
@@ -0,0 +1,40 @@
+package(default_visibility = ["//visibility:public"])
+
+load("//aos/build:queues.bzl", "queue_library")
+
+queue_library(
+ name = "superstructure_queue",
+ srcs = [
+ "superstructure.q",
+ ],
+ deps = [
+ "//aos/controls:control_loop_queues",
+ "//frc971/control_loops:profiled_subsystem_queue",
+ "//frc971/control_loops:queues",
+ ],
+)
+
+cc_library(
+ name = 'superstructure_lib',
+ srcs = [
+ "superstructure.cc",
+ ],
+ hdrs = [
+ "superstructure.h",
+ ],
+ deps = [
+ ":superstructure_queue",
+ "//aos/controls:control_loop",
+ ]
+)
+
+cc_binary(
+ name = "superstructure",
+ srcs = [
+ "superstructure_main.cc",
+ ],
+ deps = [
+ ":superstructure_lib",
+ "//aos:init",
+ ]
+)
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure.cc b/y2019/control_loops/superstructure/superstructure.cc
new file mode 100644
index 0000000..2f0832c
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure.cc
@@ -0,0 +1,32 @@
+#include "y2019/control_loops/superstructure/superstructure.h"
+
+#include "aos/controls/control_loops.q.h"
+#include "frc971/control_loops/control_loops.q.h"
+
+namespace y2019 {
+namespace control_loops {
+namespace superstructure {
+
+Superstructure::Superstructure(
+ SuperstructureQueue *superstructure_queue)
+ : aos::controls::ControlLoop<SuperstructureQueue>(
+ superstructure_queue) {}
+
+void Superstructure::RunIteration(
+ const SuperstructureQueue::Goal *unsafe_goal,
+ const SuperstructureQueue::Position *position,
+ SuperstructureQueue::Output *output,
+ SuperstructureQueue::Status *status) {
+ (void)unsafe_goal;
+ (void)position;
+ (void)output;
+ (void)status;
+
+ if (WasReset()) {
+ LOG(ERROR, "WPILib reset, restarting\n");
+ }
+}
+
+} // namespace superstructure
+} // namespace control_loops
+} // namespace y2019
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure.h b/y2019/control_loops/superstructure/superstructure.h
new file mode 100644
index 0000000..1faa6b4
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure.h
@@ -0,0 +1,34 @@
+#ifndef Y2019_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
+#define Y2019_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
+
+#include "aos/controls/control_loop.h"
+#include "y2019/control_loops/superstructure/superstructure.q.h"
+
+namespace y2019 {
+namespace control_loops {
+namespace superstructure {
+
+class Superstructure
+ : public ::aos::controls::ControlLoop<SuperstructureQueue> {
+ public:
+ explicit Superstructure(
+ SuperstructureQueue *my_superstructure =
+ &superstructure_queue);
+
+ protected:
+ virtual void RunIteration(
+ const SuperstructureQueue::Goal *unsafe_goal,
+ const SuperstructureQueue::Position *position,
+ SuperstructureQueue::Output *output,
+ SuperstructureQueue::Status *status) override;
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(Superstructure);
+};
+
+} // namespace superstructure
+} // namespace control_loops
+} // namespace y2019
+
+#endif // Y2019_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure.q b/y2019/control_loops/superstructure/superstructure.q
new file mode 100644
index 0000000..2cd7238
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure.q
@@ -0,0 +1,127 @@
+package y2019.control_loops.superstructure;
+
+import "aos/controls/control_loops.q";
+import "frc971/control_loops/profiled_subsystem.q";
+
+struct ElevatorGoal {
+ // Meters, 0 = lowest position - mechanical hard stop,
+ // positive = upward
+ double height;
+
+ .frc971.ProfileParameters profile_params;
+};
+
+struct IntakeGoal {
+ // Positive is rollers intaking inward.
+ double roller_voltage;
+
+ // 0 = linkage on the sprocket is pointing straight up,
+ // positive = forward
+ double joint_angle;
+
+ .frc971.ProfileParameters profile_params;
+};
+
+struct SuctionGoal {
+ // True = open solenoid (apply suction)
+ // Top/bottom are when wrist is forward
+ bool top;
+ bool bottom;
+};
+
+struct StiltsGoal {
+ // Distance stilts extended out of the bottom of the robot. Positive = down.
+ // 0 is the height such that the bottom of the stilts is tangent to the bottom
+ // of the middle wheels.
+ double height;
+
+ .frc971.ProfileParameters profile_params;
+};
+
+struct WristGoal {
+ // 0 = Straight up parallel to elevator
+ // Positive rotates toward intake from 0
+ double angle;
+ .frc971.ProfileParameters profile_params;
+};
+
+queue_group SuperstructureQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ ElevatorGoal elevator;
+ IntakeGoal intake;
+ SuctionGoal suction;
+ StiltsGoal stilts;
+ WristGoal wrist;
+ };
+
+ message Status {
+ // All subsystems know their location.
+ bool zeroed;
+
+ // If true, we have aborted. This is the or of all subsystem estops.
+ bool estopped;
+
+ // Whether suction_pressure indicates cargo is held
+ bool has_piece;
+
+ // Status of each subsystem.
+ .frc971.control_loops.AbsoluteProfiledJointStatus elevator;
+ .frc971.control_loops.AbsoluteProfiledJointStatus wrist;
+ .frc971.control_loops.AbsoluteProfiledJointStatus intake;
+ .frc971.control_loops.AbsoluteProfiledJointStatus stilts;
+ };
+
+ message Position {
+ // Input from pressure sensor in psi
+ // 0 = current atmospheric pressure, negative = suction.
+ double suction_pressure;
+
+ // Position of the elevator, 0 at lowest position, positive when up.
+ .frc971.PotAndAbsolutePosition elevator;
+
+ // Position of wrist, 0 when up, positive is rotating toward the front,
+ // over the top.
+ .frc971.PotAndAbsolutePosition wrist;
+
+ // Position of the intake. 0 when rollers are retracted, positive extended.
+ .frc971.PotAndAbsolutePosition intake_joint;
+
+ // Position of the stilts, 0 when retracted (defualt), positive lifts robot.
+ .frc971.PotAndAbsolutePosition stilts;
+ };
+
+ message Output {
+ // Voltage sent to motors moving elevator up/down. Positive is up.
+ double elevator_voltage;
+
+ // Voltage sent to wrist motors on elevator to rotate.
+ // Positive rotates over the top towards the front of the robot.
+ double wrist_voltage;
+
+ // Voltage sent to motors on intake joint. Positive extends rollers.
+ double intake_joint_voltage;
+
+ // Voltage sent to rollers on intake. Positive rolls inward.
+ double intake_roller_voltage;
+
+ // Voltage sent to motors to move stilts height. Positive moves robot upward.
+ double stilts_voltage;
+
+ // True opens solenoid (applies suction)
+ // Top/bottom are when wrist is toward the front of the robot
+ bool intake_suction_top;
+ bool intake_suction_bottom;
+
+ // Voltage sent to the vacuum pump motors.
+ double pump_voltage;
+ };
+
+ queue Goal goal;
+ queue Output output;
+ queue Status status;
+ queue Position position;
+};
+
+queue_group SuperstructureQueue superstructure_queue;
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure_main.cc b/y2019/control_loops/superstructure/superstructure_main.cc
new file mode 100644
index 0000000..f3dd1ad
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure_main.cc
@@ -0,0 +1,11 @@
+#include "y2019/control_loops/superstructure/superstructure.h"
+
+#include "aos/init.h"
+
+int main() {
+ ::aos::Init();
+ ::y2019::control_loops::superstructure::Superstructure superstructure;
+ superstructure.Run();
+ ::aos::Cleanup();
+ return 0;
+}
diff --git a/y2019/jevois/BUILD b/y2019/jevois/BUILD
new file mode 100644
index 0000000..8a8bca3
--- /dev/null
+++ b/y2019/jevois/BUILD
@@ -0,0 +1,11 @@
+cc_library(
+ name = "structures",
+ hdrs = [
+ "structures.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/containers:sized_array",
+ "//third_party/eigen",
+ ],
+)
diff --git a/y2019/jevois/structures.h b/y2019/jevois/structures.h
new file mode 100644
index 0000000..d2ab3e5
--- /dev/null
+++ b/y2019/jevois/structures.h
@@ -0,0 +1,103 @@
+#ifndef Y2019_JEVOIS_STRUCTURES_H_
+#define Y2019_JEVOIS_STRUCTURES_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <bitset>
+#include <chrono>
+
+#include "Eigen/Dense"
+
+#include "aos/containers/sized_array.h"
+
+namespace frc971 {
+namespace jevois {
+
+// The overall flow to get data to the roboRIO consists of:
+// 1. Camera captures a frame and grabs an absolute timestamp.
+// 2. Camera processes the frame.
+// 3. Camera grabs another absolute timestamp and subtracts to get a
+// camera_duration.
+// 4. Camera sends the frame via UART to the Teensy.
+// 5. Teensy grabs an absolute timestamp for the first character received.
+// 6. Teensy buffers at most one frame from each camera.
+// 7. roboRIO toggles the CS line.
+// 8. Teensy grabs an absolute timestamp for CS being asserted.
+// 9. Teensy pulls together up to three frames and adds the time each one
+// spent in its queue to the timestamps, and queues them in its SPI
+// peripheral. This all happens before the roboRIO has enough time to start
+// actually moving data.
+// 10. roboRIO transfers the frames, and sends back light/status commands.
+
+using camera_duration = std::chrono::duration<uint8_t, std::milli>;
+
+// This file declares the shared datastructures for the JeVois-based image
+// system.
+//
+// Note that floating-point numbers are represented with floats. This is because
+// a float has more bits than we need for anything, and the Teensy can't handle
+// doubles very quickly. It would probably be quick enough, but it's easier to
+// just use floats and not worry about it.
+
+struct Target {
+ // Distance to the target in meters. Specifically, the distance from the
+ // center of the camera's image plane to the center of the target.
+ float distance;
+
+ // Height of the target in meters. Specifically, the distance from the floor
+ // to the center of the target.
+ float height;
+
+ // Heading of the center of the target in radians. Zero is straight out
+ // perpendicular to the camera's image plane. Images to the left (looking at a
+ // camera image) are at a positive angle.
+ float heading;
+
+ // The angle between the target and the camera's image plane. This is
+ // projected so both are assumed to be perpendicular to the floor. Parallel
+ // targets have a skew of zero. Targets rotated such that their left edge
+ // (looking at a camera image) is closer are at a positive angle.
+ float skew;
+};
+
+// The information extracted from a single camera frame.
+//
+// This is all the information sent from each camera to the Teensy.
+struct Frame {
+ // The top most interesting targets found in this frame.
+ aos::SizedArray<Target, 3> targets;
+
+ // How long ago from the current time this frame was captured.
+ camera_duration age;
+};
+
+// This is all the information sent from the Teensy to each camera.
+struct CameraCalibration {
+ // The calibration matrix. This defines where the camera is pointing.
+ //
+ // TODO(Parker): What are the details on how this is defined.
+ Eigen::Matrix<float, 3, 4> calibration;
+};
+
+// This is all the information the Teensy sends to the RoboRIO.
+struct TeensyToRoborio {
+ // The newest frames received from up to three cameras. These will be the
+ // three earliest-received of all buffered frames.
+ aos::SizedArray<Frame, 3> frames;
+};
+
+// This is all the information the RoboRIO sends to the Teensy.
+struct RoborioToTeensy {
+ // Brightnesses for each of the beacon light channels. 0 is off, 255 is fully
+ // on.
+ std::array<uint8_t, 3> beacon_brightness;
+
+ // Whether the light ring for each camera should be on.
+ std::bitset<5> light_rings;
+};
+
+} // namespace jevois
+} // namespace frc971
+
+#endif // Y2019_JEVOIS_STRUCTURES_H_